org.hibernate.LazyInitializationException - 无法初始化代理 - 没有会话

Ble*_*tri 159 java orm session hibernate lazy-loading

我得到以下异常:

Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:167)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:215)
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190)
    at sei.persistence.wf.entities.Element_$$_jvstc68_47.getNote(Element_$$_jvstc68_47.java)
    at JSON_to_XML.createBpmnRepresantation(JSON_to_XML.java:139)
    at JSON_to_XML.main(JSON_to_XML.java:84)
Run Code Online (Sandbox Code Playgroud)

当我尝试从main调用以下行时:

Model subProcessModel = getModelByModelGroup(1112);
System.out.println(subProcessModel.getElement().getNote());
Run Code Online (Sandbox Code Playgroud)

getModelByModelGroup(int modelgroupid)首先实现了这个方法,如下所示:

public static Model getModelByModelGroup(int modelGroupId, boolean openTransaction) {

    Session session = SessionFactoryHelper.getSessionFactory().getCurrentSession();     
    Transaction tx = null;

    if (openTransaction) {
        tx = session.getTransaction();
    }

    String responseMessage = "";

    try {
        if (openTransaction) {
            tx.begin();
        }
        Query query = session.createQuery("from Model where modelGroup.id = :modelGroupId");
        query.setParameter("modelGroupId", modelGroupId);

        List<Model> modelList = (List<Model>)query.list(); 
        Model model = null;

        for (Model m : modelList) {
            if (m.getModelType().getId() == 3) {
                model = m;
                break;
            }
        }

        if (model == null) {
            Object[] arrModels = modelList.toArray();
            if (arrModels.length == 0) {
                throw new Exception("Non esiste ");
            }

            model = (Model)arrModels[0];
        }

        if (openTransaction) {
            tx.commit();
        }

        return model;

   } catch(Exception ex) {
       if (openTransaction) {
           tx.rollback();
       }
       ex.printStackTrace();
       if (responseMessage.compareTo("") == 0) {
           responseMessage = "Error" + ex.getMessage();
       }
       return null;
    }
}
Run Code Online (Sandbox Code Playgroud)

得到了例外.然后一位朋友建议我总是测试会话并获取当前会话以避免此错误.所以我这样做了:

public static Model getModelByModelGroup(int modelGroupId) {
    Session session = null;
    boolean openSession = session == null;
    Transaction tx = null;
    if (openSession) {
        session = SessionFactoryHelper.getSessionFactory().getCurrentSession(); 
        tx = session.getTransaction();
    }
    String responseMessage = "";

    try {
        if (openSession) {
            tx.begin();
        }
        Query query = session.createQuery("from Model where modelGroup.id = :modelGroupId");
        query.setParameter("modelGroupId", modelGroupId);

        List<Model> modelList = (List<Model>)query.list(); 
        Model model = null;

        for (Model m : modelList) {
            if (m.getModelType().getId() == 3) {
                model = m;
                break;
            }
        }

        if (model == null) {
            Object[] arrModels = modelList.toArray();
            if (arrModels.length == 0) {
                throw new RuntimeException("Non esiste");
            }

            model = (Model)arrModels[0];

            if (openSession) {
                tx.commit();
            }
            return model;
        } catch(RuntimeException ex) {
            if (openSession) {
                tx.rollback();
            }
            ex.printStackTrace();
            if (responseMessage.compareTo("") == 0) {
                responseMessage = "Error" + ex.getMessage();
            }
            return null;        
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但仍然得到相同的错误.我一直在阅读这个错误,并找到了一些可能的解决方案.其中一个是将lazyLoad设置为false但我不允许这样做,这就是为什么我被建议控制会话

use*_*995 134

如果你使用Spring将类标记为@Transactional,那么Spring将处理会话管理.

@Transactional
public class MyClass {
    ...
}
Run Code Online (Sandbox Code Playgroud)

通过使用@Transactional,可以自动处理许多重要方面,例如事务传播.在这种情况下,如果调用另一个事务方法,则该方法可以选择加入正在进行的事务,避免"无会话"异常.

  • 我不能夸大这个答案的重要性.我真的建议先尝试这个选项. (17认同)
  • 另请注意,您必须在配置中添加"@ EnableTransactionManagement"才能启用事务."如果调用另一个事务方法,则该方法将具有加入正在进行的事务的选项",这种行为对于实现事务的不同方式是不同的,即接口代理与类代理或AspectJ编织.请参阅[文档](http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/transaction.html#transaction-declarative-annotations). (6认同)
  • 我强烈建议仅在类顶部使用此批注进行测试。实际代码应将每个方法分别标记为类中的事务。除非类中的所有方法都需要打开与事务到数据库的连接。 (5认同)
  • 放置 @Transactional(readOnly = true) 而不仅仅是 @Transactional 不是安全吗? (4认同)
  • 我们是否应该理解 Spring 的“Transactional”注释不仅用于修改事务,还用于仅访问事务? (2认同)

Wil*_*wan 105

你可以尝试设置

<property name="hibernate.enable_lazy_load_no_trans">true</property>
Run Code Online (Sandbox Code Playgroud)

在hibernate.cfg.xml或persistence.xml中

这个属性要记住的问题在这里得到了很好的解释

  • 你知道[`hibernate.enable_lazy_load_no_trans`是反模式](https://vladmihalcea.com/2016/09/05/the-hibernate-enable_lazy_load_no_trans-anti-pattern/),对吗? (26认同)
  • 你能解释一下它的含义吗? (8认同)
  • 不要使用这个属性,如果春天管理你的交易,这个属性将导致交易爆炸,简单的春天将关闭应用程序 (5认同)
  • for persistence.xml:`<property name ="hibernate.enable_lazy_load_no_trans"value ="true"/>` (3认同)
  • 我也很好奇这是做什么的.它确实解决了我遇到的问题,但我想了解原因. (2认同)

gor*_*ncy 80

这里的错误是您的会话管理配置设置为在提交事务时关闭会话.检查您是否有类似的东西:

<property name="current_session_context_class">thread</property>
Run Code Online (Sandbox Code Playgroud)

在您的配置中.

为了克服这个问题,您可以更改会话工厂的配置或打开另一个会话,而不仅仅是要求那些延迟加载的对象.但我在这里建议的是在getModelByModelGroup本身初始化这个惰性集合并调用:

Hibernate.initialize(subProcessModel.getElement());
Run Code Online (Sandbox Code Playgroud)

当你还在活跃的会话中.

最后一件事.友好的建议.你的方法中有这样的东西:

for (Model m : modelList) {
    if (m.getModelType().getId() == 3) {
        model = m;
        break;
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意这段代码只是在查询语句中过滤那些类型id等于3的模型,只需要几行.

更多阅读:

会话工厂配置

关闭会话的问题

  • 不,它可能没问题.但是阅读更多内容以便能够完全控制会话和事务.了解基础知识非常重要,因为所有更高级别的技术(如Spring,Hibernate等)都在同一个概念上运行. (2认同)

Vla*_*cea 43

处理的最好方法LazyInitializationException是使用JOIN FETCH指令:

Query query = session.createQuery(
    "from Model m " +
    "join fetch m.modelType " +
    "where modelGroup.id = :modelGroupId"
);
Run Code Online (Sandbox Code Playgroud)

无论如何,不​​要使用以下一些答案所建议的反模式:

有时,DTO投影是比获取实体更好的选择,这样,你就不会得到任何实体LazyInitializationException.

  • 我们应该倡导最好的企业实践而不是快速解决方案. (2认同)
  • 这是最好的修复 (2认同)

Smr*_*thy 12

对于下面的注释,我得到了一对多关系的相同错误.

@OneToMany(mappedBy="department", cascade = CascadeType.ALL)
Run Code Online (Sandbox Code Playgroud)

添加fetch = FetchType.EAGER后更改如下,它对我有用.

@OneToMany(mappedBy="department", cascade = CascadeType.ALL, fetch=FetchType.EAGER)
Run Code Online (Sandbox Code Playgroud)

  • 是的,它可以修复它,但现在你正在加载整个数据树.在大多数情况下,这会对性能产生负面影响 (18认同)

Sha*_*him 9

如果你使用spring data jpa,spring boot你可以在application.properties中添加这一行

spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
Run Code Online (Sandbox Code Playgroud)

  • 这被认为是一种反模式.https://vladmihalcea.com/the-hibernate-enable_lazy_load_no_trans-anti-pattern/ (5认同)

小智 8

这个例外因为你打电话session.getEntityById(),会话将被关闭.因此,您需要将实体重新附加到会话中.或者Easy解决方案只是配置default-lazy="false" 为您entity.hbm.xml或者如果您使用注释只是添加@Proxy(lazy=false)到您的实体类.


Ton*_* Vu 5

我遇到了同样的问题。我认为解决此问题的另一种方法是,您可以按以下方式更改查询以加入从模型中获取元素的联接:

Query query = session.createQuery("from Model m join fetch m.element where modelGroup.id = :modelGroupId")
Run Code Online (Sandbox Code Playgroud)


小智 5

这意味着您尝试访问的对象未加载,因此编写一个查询,对您尝试访问的对象进行连接提取

例如:

如果您尝试从 ObjectA 获取 ObjectB,其中 ObjectB 是 ObjectA 中的外键。

询问 :

SELECT objA FROM ObjectA obj JOIN FETCH obj.objectB objB
Run Code Online (Sandbox Code Playgroud)


Kms*_*Kms 5

在不同的用例中面临相同的异常。

在此输入图像描述

用例:尝试使用 DTO 投影从数据库读取数据

解决方案:使用get方法而不是load

通用操作

public class HibernateTemplate {
public static Object loadObject(Class<?> cls, Serializable s) {
    Object o = null;
    Transaction tx = null;
    try {
        Session session = HibernateUtil.getSessionFactory().openSession();
        tx = session.beginTransaction();
        o = session.load(cls, s); /*change load to get*/
        tx.commit();
        session.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return o;
}
Run Code Online (Sandbox Code Playgroud)

}

持久化类

public class Customer {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "Id")
private int customerId;

@Column(name = "Name")
private String customerName;

@Column(name = "City")
private String city;

//constructors , setters and getters

}
Run Code Online (Sandbox Code Playgroud)

客户DAO接口

public interface CustomerDAO 
     {
   public CustomerTO getCustomerById(int cid);
     }
Run Code Online (Sandbox Code Playgroud)

实体传输对象类

public class CustomerTO {

private int customerId;

private String customerName;

private String city;

//constructors , setters and getters
Run Code Online (Sandbox Code Playgroud)

}

工厂级

public class DAOFactory {

static CustomerDAO customerDAO;
static {
    customerDAO = new HibernateCustomerDAO();
}

public static CustomerDAO getCustomerDAO() {
    return customerDAO;
}
Run Code Online (Sandbox Code Playgroud)

}

实体特定的 DAO

public class HibernateCustomerDAO implements CustomerDAO {

@Override
public CustomerTO getCustomerById(int cid) {
    Customer cust = (Customer) HibernateTemplate.loadObject(Customer.class, cid);
    CustomerTO cto = new CustomerTO(cust.getCustomerId(), cust.getCustomerName(), cust.getCity());
    return cto;
}
Run Code Online (Sandbox Code Playgroud)

}

检索数据:测试类

CustomerDAO cdao = DAOFactory.getCustomerDAO();
CustomerTO c1 = cdao.getCustomerById(2);
System.out.println("CustomerName -> " + c1.getCustomerName() + " ,CustomerCity -> " + c1.getCity());
Run Code Online (Sandbox Code Playgroud)

当前数据

在此输入图像描述

Hibernate系统生成的查询和输出

休眠:从 CustomerLab31 customer0_ 中选择 customer0_.Id 作为 Id1_0_0_、customer0_.City 作为 City2_0_0_、customer0_.Name 作为 Name3_0_0_,其中 customer0_.Id=?

客户名称 -> 科迪,客户城市 -> 洛杉矶