Java @事务性注释Spring boot 2.0和hibernate LazyInitializationException

Java @事务性注释Spring boot 2.0和hibernate LazyInitializationException,java,spring,hibernate,Java,Spring,Hibernate,我有以下问题。据我所知,@Transactional注释应该使会话保持活动状态,从而允许延迟获取子实体,而无需执行特定的连接查询 在下面的场景中,我不明白为什么我仍然得到一个LazyInitializationException 我的应用程序运行一个解析器,以便为各种控制器服务提供一个已解析的对象,以便可以直接使用它 所述解析器截取来自请求的报头,并使用其值尝试查询数据库以获取对象。现在所讨论的对象非常简单,尽管它有两个子实体的列表,但它正在进行操作 为了执行解析操作,我使用了一个额外的服务,其

我有以下问题。据我所知,
@Transactional
注释应该使会话保持活动状态,从而允许延迟获取子实体,而无需执行特定的连接查询

在下面的场景中,我不明白为什么我仍然得到一个
LazyInitializationException

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

所述解析器截取来自请求的报头,并使用其值尝试查询数据库以获取对象。现在所讨论的对象非常简单,尽管它有两个子实体的列表,但它正在进行操作

为了执行解析操作,我使用了一个额外的服务,其中我基本上包装了一些
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
,这意味着应该在所述方法的上下文中保持会话活动

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

1) 使用我看到的调试器,即使在该级别
getByAppClientId
包含延迟加载的子实体的列表也得到了很好的解决

2) 在冲突解决程序本身上,从委派方法接收到对象时,由于出现
lazyinstinitializationexception
,无法计算列表

3) 最后,在最终控制器服务方法上,也被标记为
@Transactional
,与上面发生的相同,这意味着这最终将失败于它的作业(因为它正在执行初始化失败的列表的get)

基于以上所述,我想知道处理此问题的最佳方法是什么。这一次,我不想使用急切获取类型,也希望避免使用获取查询。同时将我的解析程序标记为
@Transactional
,从而保持会话在那里打开也是不可能的

我认为,因为
@Transactional
将保持会话打开,从而使最终服务方法能够获得子实体列表。情况似乎并非如此

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

处理这个问题的最佳方法是什么?我在这里读了很多文章,但我不知道从SpringBoot2.0和Hibernate5开始,哪种方法是最被接受的

更新:

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

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


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

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

我正是为这个用例而创建的。基本上,您将JPA实体的DTO定义为接口,并将它们应用于查询。它支持映射嵌套DTO、集合等,基本上是您所期望的一切,而且最重要的是,它将提高您的查询性能,因为它将生成只获取您实际需要的数据的查询对于DTO

您的示例的实体视图可能如下所示

@EntityView(AppClient.class)
interface AppClientDto {
  String getName();
}
List<AppClientDto> dtos = entityViewManager.applySetting(
  EntityViewSetting.create(AppClientDto.class),
  criteriaBuilderFactory.create(em, AppClient.class)
).getResultList();
查询可能是这样的

@EntityView(AppClient.class)
interface AppClientDto {
  String getName();
}
List<AppClientDto> dtos = entityViewManager.applySetting(
  EntityViewSetting.create(AppClientDto.class),
  criteriaBuilderFactory.create(em, AppClient.class)
).getResultList();
List dtos=entityViewManager.applySetting(
EntityViewSetting.create(AppClientDto.class),
create(em,AppClient.class)
).getResultList();

请注意,对于阅读本文的任何人,我也考虑过使用DTO投影,但我希望避免这种情况。这里的最佳实践是在查询中加入获取,您希望避免这种情况……您可以启用
休眠。启用\u lazy\u load\u no\u trans
并放弃
@Transactional
。我认为这是在反模式中启用的。如果如果您设法让
@Transactional
加载惰性实体,则最终会出现Select N+1问题,这也是一个反模式问题。查询的数量将与
启用惰性加载否转换时相同。只是数据库连接的数量将减少。为什么要避免使用fetch查询?至于查询我担心我似乎不太明白如何做一个合适的