我得到以下异常:
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,但我不允许这样做,这就是为什么我被建议控制会话
如果您使用 Spring 将类标记为 @Transactional,则 Spring 将处理会话管理。
@Transactional
public class MyClass {
...
}
通过使用 @Transactional
,可以自动处理事务传播等许多重要方面。在这种情况下,如果调用另一个事务方法,该方法将可以选择加入正在进行的事务,从而避免“无会话”异常。
警告如果您使用 @Transactional
,请注意由此产生的行为。有关常见陷阱,请参阅 this article。例如,即使您没有明确调用 save
,实体更新也会保持
你可以尝试设置
<property name="hibernate.enable_lazy_load_no_trans">true</property>
在 hibernate.cfg.xml 或 persistence.xml
这个属性要记住的问题得到了很好的解释here
<property name="hibernate.enable_lazy_load_no_trans" value="true"/>
这里的问题是您的会话管理配置设置为在您提交事务时关闭会话。检查你是否有类似的东西:
<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 的模型。
更多阅读:
处理 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
,但这不是正确的解决方案,对吧?
@Transactional
服务之前使用 DTO 或初始化所有关联。
如果你使用 spring data jpa , spring boot 你可以在 application.properties 添加这一行
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
对于以下注释的一对多关系,我遇到了相同的错误。
@OneToMany(mappedBy="department", cascade = CascadeType.ALL)
添加 fetch=FetchType.EAGER 后更改如下,它对我有用。
@OneToMany(mappedBy="department", cascade = CascadeType.ALL, fetch=FetchType.EAGER)
此异常是因为当您调用 session.getEntityById()
时,会话将被关闭。因此,您需要将实体重新附加到会话。或者简单的解决方案只是将 default-lazy="false"
配置到您的 entity.hbm.xml
,或者如果您使用注释,只需将 @Proxy(lazy=false)
添加到您的实体类。
我遇到了同样的问题。我认为解决此问题的另一种方法是您可以更改查询以加入从模型中获取您的元素,如下所示:
Query query = session.createQuery("from Model m join fetch m.element where modelGroup.id = :modelGroupId")
这意味着您尝试访问的对象未加载,因此请编写一个查询,对您尝试访问的对象进行连接获取。
例如:
如果您尝试从 ObjectA 获取 ObjectB,其中 ObjectB 是 ObjectA 中的外键。
询问 :
SELECT objA FROM ObjectA obj JOIN FETCH obj.objectB objB
在不同的用例中面临相同的异常。
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
这里有几个很好的答案可以在广泛的范围内处理此错误。我遇到了 Spring Security 的一个特定情况,它有一个快速但可能不是最佳的修复。
在用户授权期间(在登录并通过身份验证后立即),我正在为扩展 SimpleUrlAuthenticationSuccessHandler 的自定义类中的特定权限测试用户实体。
我的用户实体实现了 UserDetails 并具有一组延迟加载的角色,这些角色引发了“org.hibernate.LazyInitializationException - 无法初始化代理 - 无会话”异常。将该 Set 从“fetch=FetchType.LAZY”更改为“fetch=FetchType.EAGER”为我解决了这个问题。
这意味着您在代码中使用 JPA 或休眠并在数据库上执行修改操作而不进行业务逻辑事务。如此简单的解决方案就是标记你的代码@Transactional
如果您使用 JPQL,使用 JOIN FETCH 是最简单的方法:http://www.objectdb.com/java/jpa/query/jpql/from#LEFT_OUTER_INNER_JOIN_FETCH_
如果您使用的是 Grail's
框架,则可以通过在域类中的特定字段上使用 Lazy
关键字来轻松解决 延迟初始化异常。
例如:
class Book {
static belongsTo = [author: Author]
static mapping = {
author lazy: false
}
}
查找更多信息here
在我的情况下,放错位置的 session.clear()
导致了这个问题。
在 Spring 应用程序中只需添加
@Transactional(readOnly = true)
在你的功能上。
提醒一下 import spring Transactional annotation import org.springframework.transaction.annotation.Transactional;
使用 @NamedEntityGraph。 Eagar fetch 会降低性能。请参阅 https://thorben-janssen.com/lazyinitializationexception/ 了解详细说明。
当我已经在使用 @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
属性并且在数据库查询中使用它们.
关于添加 JOIN FETCH(或左连接提取)的所有答案都是正确的,我只想添加这个:如果您有转换器,请确保 getAsObject 子使用“查找”而不是在 sql 中包含 Join Fetch。我浪费了很多时间来解决类似的问题,问题出在转换器上。
springBootVersion = '2.6.7'
休眠 = 5.6.8.Final'
对我来说,我得到错误:
MyEntity myEntity = myEntityRepository.getById(id);
我改成这样:
MyEntity myEntity = myEntityRepository.findById(id).orElse(null);
我在实体中添加 @ManyToOne(fetch = FetchType.EAGER)
您也可以通过将lazy=false 添加到您的 *.hbm.xml 文件中来解决它,或者当您从 db 获取对象时,您可以在 Hibernate.init(Object) 中初始化您的对象
使用 session.get(*.class, id);但不加载功能
在 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>
@EnableTransactionManagement
添加到您的配置中才能启用事务。 “如果另一个事务方法被调用,该方法将可以选择加入正在进行的事务”这种行为对于实现事务的不同方式是不同的,即接口代理与类代理或 AspectJ 编织。请参阅documentation。Transactional
注释,不仅用于修改事务,还用于仅访问事务?