如何修复org.hibernate.LazyInitializationException - 无法初始化代理 - 没有Session

时间:2014-02-05 10:12:21

标签: java hibernate session orm 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)

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

Model subProcessModel = getModelByModelGroup(1112);
System.out.println(subProcessModel.getElement().getNote());

我首先实现了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);
            @SuppressWarnings("unchecked")
            List<Model> modelList = (List<Model>)query.list(); 
            Model model = null;
            // Cerco il primo Model che è in esercizio: idwf_model_type = 3
            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;        
        }

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

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);
            @SuppressWarnings("unchecked")
            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;        
        }

    }

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

19 个答案:

答案 0 :(得分:100)

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

@Transactional
public class MyClass {
    ...
}

通过使用@Transactional,可以自动处理许多重要方面,例如事务传播。在这种情况下,如果调用另一个事务方法,该方法将具有加入正在进行的事务的选项,避免&#34; no session&#34;异常。

答案 1 :(得分:98)

您可以尝试设置

<property name="hibernate.enable_lazy_load_no_trans">true</property>

在hibernate.cfg.xml或persistence.xml

要清楚地解释这个属性的问题here

答案 2 :(得分:74)

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

<property name="current_session_context_class">thread</property> 

在您的配置中。

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

Hibernate.initialize(subProcessModel.getElement());

当你还在参加活动时。

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

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

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

更多阅读:

session factory configuration

problem with closed session

答案 3 :(得分:39)

The best way to handle the LazyInitializationException是使用JOIN FETCH指令:

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

无论如何,请勿使用以下某些答案所建议的反模式:

有时,DTO projection是获取实体的更好选择,这样,您就不会获得任何LazyInitializationException

答案 4 :(得分:12)

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

@OneToMany(mappedBy="department", cascade = CascadeType.ALL)

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

@OneToMany(mappedBy="department", cascade = CascadeType.ALL, fetch=FetchType.EAGER)

答案 5 :(得分:7)

由于您致电session.getEntityById()时会出现此异常,会话将被关闭。因此,您需要将实体重新附加到会话中。或者Easy解决方案只是为default-lazy="false"配置entity.hbm.xml,或者如果您使用注释,只需将@Proxy(lazy=false)添加到您的实体类。

答案 6 :(得分:6)

如果你使用spring data jpa,spring boot你可以在application.properties

中添加这一行
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true

答案 7 :(得分:4)

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

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

答案 8 :(得分:2)

这里有几个很好的答案可以在广泛的范围内处理这个错误。我遇到了Spring Security的特定情况,它有一个快速但可能不是最佳的修复方法。

在用户授权期间(登录并通过身份验证后立即),我正在为扩展SimpleUrlAuthenticationSuccessHandler的自定义类中的特定权限测试用户实体。

我的用户实体实现UserDetails并且有一组延迟加载的角色,它抛出了“org.hibernate.LazyInitializationException - 无法初始化代理 - 无会话”异常。将该设置从“fetch = FetchType.LAZY”更改为“fetch = FetchType.EAGER”为我修复此问题。

答案 9 :(得分:1)

如果您使用的是JPQL,请使用JOIN FETCH是最简单的方法: http://www.objectdb.com/java/jpa/query/jpql/from#LEFT_OUTER_INNER_JOIN_FETCH_

答案 10 :(得分:1)

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

例如:

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

查询:

SELECT objA FROM ObjectA obj JOIN FETCH obj.objectB objB

答案 11 :(得分:0)

如果您使用的是Grail's Framework,则可以通过在域类中的特定字段上使用Lazy关键字来解决延迟初始化异常

例如:

class Book {
    static belongsTo = [author: Author]
    static mapping = {
        author lazy: false
    }
}

查找更多信息here

答案 12 :(得分:0)

在 Spring 应用程序中只需添加

@Transactional(readOnly = true)

在您的函数上。

<块引用>

提醒导入spring Transactional注解

导入 org.springframework.transaction.annotation.Transactional;

答案 13 :(得分:0)

当我已经使用@Transactional(value=...)并使用多个事务管理器时,这发生在我身上。

我的表单正在发送回已带有@JsonIgnore的数据,因此从表单发送回的数据不完整。

最初,我使用了反图案解决方案,但发现它的运行速度非常慢。我通过将其设置为false来禁用它。

spring.jpa.properties.hibernate.enable_lazy_load_no_trans=false

解决方法是确保首先从数据库中检索所有具有未延迟加载的数据的对象。

Optional<Object> objectDBOpt = objectRepository.findById(object.getId());

if (objectDBOpt.isEmpty()) {
    // Throw error
} else {
    Object objectFromDB = objectDBOpt.get();
}

简而言之,如果您尝试了所有其他答案,请确保回头检查一下是否首先从数据库中加载,如果您没有提供所有@JsonIgnore属性并且正在使用它们在您的数据库查询中。

答案 14 :(得分:0)

在不同的用例中遇到了相同的异常。

enter image description here

用例:尝试使用 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;
}

}

持久性类

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

}

CustomerDAO界面

public interface CustomerDAO 
     {
   public CustomerTO getCustomerById(int cid);
     }

实体传输对象类

public class CustomerTO {

private int customerId;

private String customerName;

private String city;

//constructors , setters and getters

}

工厂类别

public class DAOFactory {

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

public static CustomerDAO getCustomerDAO() {
    return customerDAO;
}

}

特定于实体的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;
}

}

检索数据:测试类

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

当前数据

enter image description here

Hibernate系统生成的查询和输出

休眠:从CustomerLab31 customer0_中选择customer0_.Id作为ID1_0_0_,customer0_.City作为City2_0_0_,customer0_.Name作为名称3_0_0_,其中customer0_.Id =?

CustomerName-> Cody,CustomerCity-> LA

答案 15 :(得分:0)

在我的情况下,放错位置的session.clear()导致了此问题。

答案 16 :(得分:0)

这意味着您正在代码中使用JPA或休眠状态,并在不进行业务逻辑事务的情况下对DB执行修改操作。 如此简单的解决方案是将您的代码标记为@Transactional

谢谢。

答案 17 :(得分:-3)

servlet-context.xml

中执行以下更改
    <beans:property name="hibernateProperties">
        <beans:props>

            <beans:prop key="hibernate.enable_lazy_load_no_trans">true</beans:prop>

        </beans:props>
    </beans:property>

答案 18 :(得分:-3)

你也可以通过在你的* .hbm.xml文件中添加lazy = false来解决它,或者当你从db获取对象时可以在Hibernate.init(Object)中初始化你的对象