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查询?至于查询我担心我似乎不太明白如何做一个合适的