java - @Transactional 注解 Spring boot 2.0 和 hibernate LazyInitializationException

标签 java spring hibernate

我有以下问题。据我了解@Transactional注释应该使 session 保持 Activity 状态,从而能够延迟获取子实体,而无需执行特定的联接查询。

我遇到以下情况,我不明白为什么我仍然得到 LazyInitializationException .

我的应用程序运行解析器,以便为各种 Controller 服务提供已解析的对象,以便可以直接使用它。

所述解析器拦截请求中的 header ,并使用它的值尝试查询数据库以获取对象。现在所讨论的对象非常简单,尽管它有两个子实体的列表,但它的作用却很简单。

为了执行解析操作,我使用了一项额外的服务,其中我基本上包装了一些 JpaRepository方法。完整内容如下:

@Service
public class AppClientServiceImpl implements AppClientService {

    private static final Logger LOGGER = LoggerFactory.getLogger(AppClientServiceImpl.class.getCanonicalName());

    private final AppClientRepository repository;

    @Autowired
    public AppClientServiceImpl(AppClientRepository repository) {
    this.repository = repository;
    }

    @Override
    @Transactional(readOnly = true)
    public AppClient getByAppClientId(final String appClientId) {
        LOGGER.debug("Attempting to retrieve appClient with id:: {}", appClientId);
    return repository.findByAppClientId(appClientId);
    }

    @Override
    @Transactional
    public void saveAndFlush(final AppClient appClient) {
        LOGGER.debug("Attempting to save/update appClient:: {}", appClient);
    repository.saveAndFlush(appClient);
    }

}

如您所见,这两种方法都注释为 @Transactional这意味着应该在该方法的上下文中保持 session 处于 Activity 状态。

现在,我的主要问题如下:

1) 使用我在该级别上看到的调试器 getByAppClientId包含延迟加载的子实体的列表已得到很好的解析。

2) 在解析器本身上,从委托(delegate)方法接收到对象时,由于LazyInitializationException而无法评估列表。 .

3) 最后是最终 Controller 服务方法,也标记为 @Transactional ,与上面相同的情况发生意味着这最终无法完成它的工作(因为它正在执行未能初始化的列表的获取。

基于上述所有内容,我想知道处理此问题的最佳方法是什么。这一次我不想使用急切获取类型,并且我也想避免使用获取查询。还将我的解析器标记为 @Transactional因此,在那里保持 session 开放也是不可能的。

我认为自从 @Transactional将使 session 保持打开状态,从而使最终服务方法能够获取子实体列表。看来事实并非如此。

基于上述所有内容,我似乎需要一种方法来调用最终的服务方法(需要手头的列表)以某种方式获取它。

处理这个问题的最佳方法是什么?我在这里阅读了很多帖子,但我无法弄清楚 Spring boot 2.0 和 hibernate 5 中哪种方法是最受接受的方法。

更新:

似乎用以下内容注释子实体:

@Fetch(FetchMode.SELECT) @LazyCollection(LazyCollectionOption.TRUE)

解决了问题,但我仍然不知道这是否是最好的方法。

最佳答案

您可以通过调试来初始化集合。调试器通常通过使用触发初始化的集合方法以特殊方式表示集合,因此这可能是它在调试期间似乎工作正常的原因。我想解析器在 getByAppClientId 的范围之外运行?此时 session 已关闭,这就是您看到异常的原因。

我创建了Blaze-Persistence Entity Views正是针对该用例。您本质上将 JPA 实体的 DTO 定义为接口(interface)并将它们应用于查询。它支持映射嵌套 DTO、集合等,基本上是您期望的所有内容,最重要的是,它将提高您的查询性能,因为它将生成仅获取 DTO 实际需要的数据的查询。

您的示例的实体 View 可能如下所示

@EntityView(AppClient.class)
interface AppClientDto {
  String getName();
}

查询可能如下所示

List<AppClientDto> dtos = entityViewManager.applySetting(
  EntityViewSetting.create(AppClientDto.class),
  criteriaBuilderFactory.create(em, AppClient.class)
).getResultList();

关于java - @Transactional 注解 Spring boot 2.0 和 hibernate LazyInitializationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50785611/

相关文章:

java - Spring-MVC、 hibernate : Creating DTO objects from Domain objects

java - Jboss hibernate 工具 : Reading error schema - Probably a JDBC driver Issue

java - native 库 sqljdbc_auth.dll 已在另一个类加载器中加载

java - FileWriter 不写入文本文件(java)

java - 使用 applicationcontext.getbean 与 @configurable 的优势

spring - 如何防止 MappingJackson2XmlHttpMessageConverter 接管序列化?

java - HQL 子查询中的 NPE

java - 在 Adempiere 的模型类中添加确认(是/否)对话框

java - 从字符串中删除多个子字符串 - Java

java - spring bean配置文件放在什么位置