2

当我运行以下代码时,我不断收到此错误“线程“主”org.hibernate.LazyInitializationException 中的异常:无法初始化代理 - 拥有的会话已关闭”:

public ArrayList<ProfileDTO> getInitialProfiles(Contracts ct){ 
   SessionFactory sessionFactory=new Configuration().configure().buildSessionFactory();
   Session session=sessionFactory.openSession();
   Transaction tx=session.beginTransaction(); 
   ArrayList<ProfileDTO> profileDTOs=new ArrayList<ProfileDTO>();
   try{
       Hibernate.initialize(ct);
        SQLQuery query=session.createSQLQuery("select {b.*},{p.*},{t.*} from bidtool.bt_boiler_plates b,bidtool.bt_profile p,bidtool.bt_trade_lane t where b.contract_id=:val AND p.contract_id=:val AND t.contract_id=:val")
                  .addEntity("b",Boiler_Plates.class)
                  .addEntity("p",BidToolProfiles.class)
                  .addEntity("t",BidToolTradeLanes.class);
        query.setParameter("val", ct.getContract_id());
        List list=query.list();  

        Iterator iteContract = list.iterator();
        while ( iteContract.hasNext() ) {
            Object[] pair =(Object[]) iteContract.next();
            Boiler_Plates bp=(Boiler_Plates)pair[0];
            BidToolProfiles p=(BidToolProfiles)pair[1];
            ProfileDTO profileDTO=new ProfileDTO();
            profileDTO.setProfileId(p.getProfileId());
            profileDTO.setBt_contracts(p.getBt_contracts());
            profileDTO.setCreated(p.getCreated());
            profileDTO.setProfileContent(p.getProfileContent());
            profileDTO.setEditable(p.getEditable());
            profileDTO.setProfileName(p.getProfileName());
            profileDTOs.add(profileDTO);
        } 
        return profileDTOs;
   }
   catch(Exception ex){
       System.out.println(ex.getMessage());
       return profileDTOs;
   }
   finally{
    session.flush();   
    session.close();
   }
 }

只要我不关闭会话,它就可以正常工作,但我不能这样做。我需要关闭会话。您的帮助将不胜感激。谢谢你。

4

4 回答 4

6

在会话关闭后访问休眠实体中的关联或集合时会引发错误。查看您的代码,我猜问题可能出在以下行:

profileDTO.setBt_contracts(p.getBt_contracts());

当您尝试从不同的会话中访问代码中其他地方的此集合时。

尝试将您的查询更改为:

select {b.*},{p.*},{t.*} from bidtool.bt_boiler_plates b,bidtool.bt_profile p,bidtool.bt_trade_lane t left join fetch p.bt_contracts btcontracts where b.contract_id=:val AND p.contract_id=:val AND t.contract_id=:val

请注意,我在集合上添加了一个连接提取。这应该确保在获取BidToolProfiles实体的同时获取合同集合。还可以尝试在将集合设置到 DTO 之前对其进行初始化。

Hibernate.initialize(p.getBt_contracts());
profileDTO.setBt_contracts(p.getBt_contracts());
于 2012-05-30T16:48:02.707 回答
3

好吧,您需要在会话关闭后初始化您计划使用的每个对象。

假设您有一个 User 实体,它的地址具有 OneToOne 惰性关联。并假设您在会话打开时加载用户。由于地址是延迟加载的,当您第一次调用地址上的方法时(user.getAddress().getStreet()例如),Hibernate 将执行查询以加载用户的地址,从而能够让您访问其街道。但是如果会话关闭,则不再有与数据库的连接,用户实体被分离,Hibernate 就会抛出这个异常。

用于Hibernate.initialize()初始化您需要的对象,或执行具有必要提取的查询以一次加载所需的所有内容。

旁注:您的异常处理很糟糕。当被要求返回配置文件时,而不是说:“对不起,由于以下异常,我不能这样做”,您基本上忽略了异常并说“数据库中没有配置文件”。请想象一下癌症检测系统以这种方式工作。您是否希望您的癌症因系统异常而未被发现?

于 2012-05-30T16:49:25.070 回答
0

你是如何使用这种方法的?你的映射是什么样的?您启用了哪些属性的延迟加载?这是一个网络应用程序吗?您正在创建 ProfileDTO,但 getBt_contracts 呢?这将从“启用会话的”BidToolProfiles 返回内容,因此如果您在该方法返回后访问延迟加载的属性,则会收到此错误。

鉴于我看不到您的映射,这只是我认为错误的一个示例。这里重要的是,在您延迟加载执行所需任务所需的所有属性后,会话应该关闭。

再一次 - 取决于这是否是一个网络应用程序,您可以采用不同的会话管理策略(网络应用程序上的每个请求的会话,如果它是桌面应用程序,则每个线程的会话等)

于 2012-05-30T16:45:20.533 回答
0

用于session.merge()将从旧会话加载的实例与新会话合并。顺便说一下,这意味着 Hibernate 从数据库中再次加载实体。

另一种可能性是避免延迟加载——无论是在映射中还是通过访问必要的值,只要会话仍然打开(即使这些值在那一刻不是必需的)。

于 2012-05-30T16:48:12.217 回答