如何修复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)

当我试图从主要呼叫以下线路:

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,但我不允许这样做,这就是为什么我被建议控制会话

618230 次浏览

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

<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的模型。

更多阅读:

会话工厂配置

问题与关闭会话

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

你可以试着设置

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

在hibernate.cfg.xml或persistence.xml中

这个属性的问题已经很好地解释了在这里

我也遇到过同样的问题。我认为另一种解决这个问题的方法是,你可以改变查询,从模型中获取你的元素,如下所示:

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

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

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

@Transactional
public class MyClass {
...
}

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

如果你确实使用了@Transactional,请注意产生的行为。常见陷阱见这篇文章。例如,对实体的更新是持久化的即使,而不是显式调用save

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

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

我的用户实体实现了UserDetails,并有一组惰性加载的角色,这些角色抛出了“org.hibernate. properties”。LazyInitializationException -不能初始化代理-没有会话”异常。将Set从“fetch=FetchType.”LAZY" to "fetch=FetchType。“渴望”帮我解决了这个问题。

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>

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

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

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

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

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

如果你正在使用Grail's框架,通过在域类中的特定字段上使用Lazy关键字来解析延迟初始化异常是很简单的。

例如:

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

查找更多信息在这里

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

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

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

增加fetch=FetchType后更改如下。渴望,它对我有效。

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

这意味着你试图访问的对象没有加载,所以写一个查询,使你试图访问的对象的加入取回

例如:

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

查询:

SELECT objA FROM ObjectA obj JOIN FETCH obj.objectB objB

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

spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
这意味着您在代码中使用JPA或hibernate,并在DB上执行修改操作,而不进行业务逻辑事务。 因此,简单的解决方案是将代码段标记为@Transactional

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

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

enter image description here

用例:尝试用DTO投影。从DB读取数据

使用得到方法代替负载方法。

通用操作

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系统生成的查询和输出

Hibernate:选择customer0_。Id为Id1_0_0_, customer0_。City as city2_0_, customer0_。Name为Name3_0_0_ from CustomerLab31 customer0_ where customer0_. id =?

CustomerName -> Cody,CustomerCity -> LA

这种情况发生在我已经使用@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属性,并在你的数据库查询中使用它们。

在Spring Application中只需添加

@Transactional(readOnly = true)

在你的功能。

提醒导入spring Transactional注释

进口org.springframework.transaction.annotation.Transactional;

使用@NamedEntityGraph。急取会降低性能。详细解释请参考https://thorben-janssen.com/lazyinitializationexception/

所有关于添加JOIN FETCH(或左JOIN FETCH)的答案都是正确的,我只想添加这个: 如果你有转换器,请确保getAsObject子对象使用“查找”;比包括在sql Join Fetch太。 我浪费了很多时间来修复一个类似的问题,问题是在转换器

  • springBootVersion = '2.6.7'
  • hibernate = 5.6.8.Final'

对我来说,我得到的错误是:

MyEntity myEntity = myEntityRepository.getById(id);

我改成这样:

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

并在entity中添加@ManyToOne(fetch = FetchType.EAGER)

当我试图获取所有department时,JAX-RS应用程序中出现了这个错误。我必须将@JsonbTransient Annotation添加到两个类的属性中。我的实体是Department和Employee,数据库关系是多对多。

Employee.java

...
@ManyToMany
@JoinTable(
name = "emp_dept",
joinColumns = {@JoinColumn(name = "emp_id", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "dept_id", referencedColumnName = "id")}
)
@JsonbTransient
private Set<Department> departments = new HashSet<Department>();
...

Department.java

...
@ManyToMany(mappedBy = "departments")
@JsonbTransient
private Set<Employee> employees = new HashSet<Employee>();
...