我有以下问题。根据我的理解,@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)在解析器本身,从委托方法收到对象的地方,由于LazyInitializationException
,列表无法评估。
3)最后在最终控制器服务方法上也标记为@Transactional
,与上面相同的意思是这最终失败了它的工作(因为它正在执行未能初始化的列表的获取
基于以上所有,我想知道处理这个问题的最佳方法是什么。一次我不想使用Eager抓取类型,我也想避免使用抓取查询。同时将我的解析器标记为@Transactional
,从而保持会话在那里打开也是不可能的。
我认为,因为@Transactional
会使会话保持打开状态,从而使最终服务方法能够获得子实体列表。情况似乎并非如此。
基于以上所有内容,似乎我需要一种方法来获取调用的最终服务方法(需要手头的列表)以某种方式获取它。
处理此问题的最佳方法是什么?我在这里阅读了不少帖子,但我不知道Spring Boot 2.0和hibernate 5中哪些方法最受欢迎。
更新:
似乎用以下内容注释子权利:
@Fetch(FetchMode.SELECT) @LazyCollection(LazyCollectionOption.TRUE)
解决了问题,但我仍然不知道这是否是最佳方法。
答案 0 :(得分:1)
您可以通过调试来初始化集合。调试器通常使用触发初始化的收集方法以特殊的方式表示收集,因此这可能是它在调试期间似乎可以正常工作的原因。我想解析器运行在getByAppClientId
范围之外?届时会话已关闭,这就是您看到异常的原因。
我正是为此用例创建了Blaze-Persistence Entity Views。您实际上将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();