通过忽略来解决LazyInitializationException

11 浏览
0 Comments

通过忽略来解决LazyInitializationException

这里有无数个问题,如何通过急切获取、保持事务开启、打开另一个事务、OpenEntityManagerInViewFilter等方式解决“无法初始化代理”问题。但是,是否可能简单地告诉Hibernate忽略这个问题,假装集合是空的?在我的情况下,不提前获取它只意味着我不关心它。

实际上,这是一个XY问题,具体问题如下:

我有以下类:

class Detail {
    @ManyToOne(optional=false) Master master;
    ...
}
class Master {
    @OneToMany(mappedBy="master") List details;
    ...
}

并且想要处理两种请求:一种返回带有所有`details`的单个`master`,另一种返回不带`details`的`master`列表。结果通过Gson转换为JSON。

我尝试了`session.clear`和`session.evict(master)`,但它们不会影响代替`details`的代理对象。有效的方法是:

master.setDetails(nullOrSomeCollection)

这种方法感觉有点不正规。我更希望有一种“忽略”问题的方式,因为它可以普遍适用,而不需要知道哪些部分是代理的。

编写一个忽略`initialized=false`的`AbstractPersistentCollection`实例的Gson `TypeAdapter`可能是一种方法,但这将依赖于`org.hibernate.collection.internal`,这肯定不是一个好事。在`TypeAdapter`中捕获异常听起来也不好。

一些回答后的更新

我的目标不是“获取数据而不是异常”,而是“如何获取`null`而不是异常”。

Dragan提出了一个有效的观点,即忘记获取并返回错误数据比抛出异常更糟糕。但是有一个简单的解决方法:

- 只对集合执行此操作

- 从不对它们使用`null`

- 返回`null`而不是空集合来指示未获取的数据

这样,结果就不会被错误解释。如果我忘记获取某些东西,响应将包含无效的`null`。

0
0 Comments

解决LazyInitializationException via ignorance

LazyInitializationException是一个常见的错误,它在尝试访问延迟加载的数据时抛出。这个问题的出现是因为在访问延迟加载数据时,会触发数据库查询操作,但此时数据库连接已经关闭,导致无法获取数据。然而,我们可以通过一种名为“ignorance”的解决方法来解决这个问题。

首先,我们创建一个名为LazyLoader的接口,用于加载延迟加载的数据。接口定义如下:

public interface LazyLoader {
    void load(T t);
}

然后,在我们的Service类中实现这个接口,用于获取包含延迟加载数据的主实体列表。代码如下:

public class Service {
    List getWithDetails(LazyLoader loader) {
        // 从session获取masterList的代码
        for(Master master:masterList) {
            loader.load(master);
        }        
    }
}

接下来,我们可以通过以下方法调用这个Service类来获取带有延迟加载数据的主实体列表:

Service.getWithDetails(new LazyLoader() {
    public void load(Master master) {
        for(Detail detail:master.getDetails()) {
            detail.getId(); // 这将加载延迟加载的数据
        }
    }
});

在Java 8中,我们还可以使用Lambda表达式来简化代码:

Service.getWithDetails((master) -> {
    for(Detail detail:master.getDetails()) {
        detail.getId(); // 这将加载延迟加载的数据
    }
});

此外,我们还可以使用session.clear()和session.evict(master)来解决这个问题。但这些方法似乎并不能解决“如何获取不带延迟加载数据的Master实体,且不抛出代理异常”的问题。

总结起来,通过使用LazyLoader接口和Service类,我们可以解决LazyInitializationException异常的问题。这种方法允许我们在获取延迟加载数据时指定加载操作,以避免在数据库连接关闭后仍然访问延迟加载数据而导致的异常。同时,我们还可以使用session.clear()和session.evict(master)方法来清除缓存和解除代理对象,以避免异常的发生。

0
0 Comments

在使用Hibernate时,有时会遇到LazyInitializationException异常。这是因为在使用懒加载时,当访问一个未初始化的属性时,Hibernate会抛出这个异常。为了解决这个问题,可以使用Hibernate公共API中的Hibernate.isInitialized方法来判断属性是否已初始化。可以在TypeAdapter中添加以下代码来处理这个问题:

if ((value instanceof Collection) && !Hibernate.isInitialized(value)) {
   result = new ArrayList();
}

这样,当属性未初始化时,就可以创建一个空的ArrayList来代替。然而,笔者认为这种方法并不是最好的解决方案。因为属性未初始化可能意味着忘记了获取数据,这样返回的数据可能是错误的,即使不抛出异常,服务的消费者也会认为该集合是空的。为了解决这个问题,可以使用DTO(数据传输对象)的方式。只需要定义一个DTO来表示服务的响应,并在事务上下文中填充数据(没有LazyInitializationException的问题),然后将DTO传递给框架进行转换(如JSON、XML等)。

// 定义一个DTO
public class ResponseDTO {
   private List collection;
   // getter and setter
}
// 在事务上下文中填充数据
@Transactional
public ResponseDTO getServiceResponse() {
   ResponseDTO responseDTO = new ResponseDTO();
   List collection = // 从数据库中获取集合数据
   responseDTO.setCollection(collection);
   return responseDTO;
}
// 将DTO转换为服务响应
ResponseDTO responseDTO = getServiceResponse();
// 转换为JSON格式
String jsonResponse = // 框架将responseDTO转换为JSON字符串

这样,通过使用DTO,可以避免LazyInitializationException异常,并确保返回正确的数据给服务的消费者。当然,也可以注册一个自定义的序列化器来解决这个问题。使用这种方法,可以更灵活地处理每个字段的序列化,而不是为每个字段单独处理。使用DTO或自定义序列化器,可以有效地解决LazyInitializationException异常的问题。

以上是解决LazyInitializationException异常的原因和解决方法的整理。

0
0 Comments

问题的原因是在使用Hibernate进行延迟加载时,父实体被从会话中清除(通过session.evict(master)),但与父实体相关联的子实体集合(details)没有被清除。这导致在尝试访问子实体集合时抛出了LazyInitializationException异常。

解决方法是在清除父实体之前,先手动清除与之关联的子实体集合。通过调用session.evict(details)来清除子实体集合的代理对象,即可解决LazyInitializationException异常。

以下是解决方法的代码示例:

session.evict(details);
session.evict(master);

这样,在清除父实体之前,先清除了子实体集合的代理对象,避免了LazyInitializationException异常的发生。

0