ChatGPT解决这个技术问题 Extra ChatGPT

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

我得到以下异常:

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);

        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;
    }
}

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

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;        
        }
    }
}

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


m
mowwwalker

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

@Transactional
public class MyClass {
    ...
}

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

警告如果您使用 @Transactional,请注意由此产生的行为。有关常见陷阱,请参阅 this article。例如,即使您没有明确调用 save,实体更新也会保持


我不能夸大这个答案的重要性。我强烈建议您先尝试此选项。
另请注意,您必须将 @EnableTransactionManagement 添加到您的配置中才能启用事务。 “如果另一个事务方法被调用,该方法将可以选择加入正在进行的事务”这种行为对于实现事务的不同方式是不同的,即接口代理与类代理或 AspectJ 编织。请参阅documentation
因此,我们是否应该理解 Spring Transactional 注释,不仅用于修改事务,还用于仅访问事务?
我强烈建议仅在课堂上使用此注释进行测试。真正的代码应该将每个方法分别标记为类中的事务。除非类中的所有方法都需要打开与数据库的事务连接。
放置 @Transactional(readOnly = true) 而不仅仅是 @Transactional 不是安全的吗?
C
Community

你可以尝试设置

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

在 hibernate.cfg.xml 或 persistence.xml

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


你能解释一下它的含义吗?
我也很好奇这是做什么的。它确实解决了我遇到的问题,但我想了解原因。
经过数千次“只需在配置文件中放入lazy = false”和“只需使用Hibernate.initialize()初始化每个对象”最终对我来说是一个具体可行的解决方案。这个选项应该在hibernate中成为标准!
对于persistence.xml:<property name="hibernate.enable_lazy_load_no_trans" value="true"/>
请勿使用此属性,如果 Spring 管理您的交易此属性将导致交易爆炸,则 Simple Spring 将关闭应用程序
M
Max

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

<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


谢谢!我使用 openSession() 而不是 getCurrentSession() 作为您给我建议的链接之一解决了我的问题,但是现在我很害怕这样做是错误的
不,应该没问题。但请阅读更多内容,以便能够完全控制您的会话和事务。了解基础知识非常重要,因为所有更高级别的技术(如 Spring、Hibernate 等)都在相同的概念上运行。
V
Vlad Mihalcea

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

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

无论如何,请不要按照某些答案的建议使用以下反模式:

在视图中打开会话

hibernate.enable_lazy_load_no_trans

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


如何确定哪个呼叫有问题?我发现很难识别电话。有什么办法吗?出于测试目的,我使用了 FetchType=EAGER,但这不是正确的解决方案,对吧?
只需使用日志记录。 EAGER 很糟糕,是的。
然后,您应该在离开 @Transactional 服务之前使用 DTO 或初始化所有关联。
我们应该提倡最好的企业实践,而不是速战速决。
这是最好的修复
S
Shaaban Ebrahim

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

spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true

这被认为是一种反模式。 vladmihalcea.com/…
如果它是一个反模式,你能建议一个替代方案吗?我也在使用 Spring Data,而我看过的其他答案对我没有帮助。
谢谢这对我有用。
A
Ahmed Ashour

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

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

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

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

是的,它可能会修复它,但现在您正在加载整个数据树。在大多数情况下,这会对性能产生负面影响
解决了我的问题非常感谢
G
Garf365

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


您如何将实体重新附加到会话?
T
Tony Vu

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

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

r
rex roy

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

例如:

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

询问 :

SELECT objA FROM ObjectA obj JOIN FETCH obj.objectB objB

K
Kms

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

https://i.stack.imgur.com/NuOKs.png

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

呈现数据

https://i.stack.imgur.com/1afXX.png

Hibernate System 生成的查询和输出

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

CustomerName -> Cody ,CustomerCity -> LA


N
Night Owl

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

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

我的用户实体实现了 UserDetails 并具有一组延迟加载的角色,这些角色引发了“org.hibernate.LazyInitializationException - 无法初始化代理 - 无会话”异常。将该 Set 从“fetch=FetchType.LAZY”更改为“fetch=FetchType.EAGER”为我解决了这个问题。


g
giannis christofakis

这意味着您在代码中使用 JPA 或休眠并在数据库上执行修改操作而不进行业务逻辑事务。如此简单的解决方案就是标记你的代码@Transactional


x
xihui

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


Z
Zeb

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

例如:

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

查找更多信息here


a
absin

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


M
Mehdi Varse

在 Spring 应用程序中只需添加

@Transactional(readOnly = true)

在你的功能上。

提醒一下 import spring Transactional annotation import org.springframework.transaction.annotation.Transactional;


L
Leena

使用 @NamedEntityGraph。 Eagar fetch 会降低性能。请参阅 https://thorben-janssen.com/lazyinitializationexception/ 了解详细说明。


N
Nightfa11

当我已经在使用 @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 属性并且在数据库查询中使用它们.


F
Fabio Sartor

关于添加 JOIN FETCH(或左连接提取)的所有答案都是正确的,我只想添加这个:如果您有转换器,请确保 getAsObject 子使用“查找”而不是在 sql 中包含 Join Fetch。我浪费了很多时间来解决类似的问题,问题出在转换器上。


A
AissaDevLab

springBootVersion = '2.6.7'

休眠 = 5.6.8.Final'

对我来说,我得到错误:

MyEntity myEntity = myEntityRepository.getById(id);

我改成这样:

MyEntity myEntity = myEntityRepository.findById(id).orElse(null);

我在实体中添加 @ManyToOne(fetch = FetchType.EAGER)


S
Sandeep Roniyaar

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


通常添加 lazy=false 不是一个好主意。这就是为什么 lazy 默认为 true
OP事先明确表示不允许他这样做。
A
Artavazd Manukyan

使用 session.get(*.class, id);但不加载功能


你能解释一下吗?
A
ArunDhwaj IIITH

在 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>