Spring Data JPA - Java 8 流支持和交易最佳实践
Spring Data JPA - Java 8 Stream Support & Transactional Best Practices
我有一个非常标准的 MVC 设置,其中包含用于我的 DAO 层的 Spring Data JPA 存储库,一个处理事务问题并实现业务逻辑的服务层,以及一个具有一些可爱的基于 REST 的视图层 JSON 端点。
我的问题是关于将 Java 8 Stream
大规模采用到这个可爱的架构中:如果我所有的 DAO return Stream
,我的服务 return 那些相同的 Stream
s(但做交易工作),我的视图作用于并处理那些 Stream
s,然后当我的视图开始处理我的 [= 中的模型对象时12=]s,Service层创建的事务将被关闭。如果底层数据存储尚未具体化我的所有模型对象(毕竟它是一个 Stream
,尽可能懒惰),那么我的视图将在尝试访问事务之外的新结果时出错。以前这不是问题,因为我会将结果完全具体化到列表中 - 但现在我们处于 Stream
的美丽新世界。
那么,处理这个问题的最佳方法是什么?将Service层内部的结果完全具体化为List并交还?视图层是否将完成块交给服务层,以便可以在事务内部完成进一步处理?
感谢您的帮助!
考虑到这一点,我决定尝试我在问题中提到的完成块解决方案。我所有的服务方法现在都有一个结果转换器作为它们的最终参数,它采用模型对象流并将其转换为视图层 needed/requested 的任何结果类型。我很高兴地报告它非常有效并且有一些不错的副作用。
这是我的服务基地 class:
public class ReadOnlyServiceImpl<MODEL extends AbstractSyncableEntity, DAO extends AbstractSyncableDAO<MODEL>> implements ReadOnlyService<MODEL> {
@Autowired
protected DAO entityDAO;
protected <S> S resultsTransformer(Supplier<Stream<MODEL>> resultsSupplier, Function<Stream<MODEL>, S> resultsTransform) {
try (Stream<MODEL> results = resultsSupplier.get()) {
return resultsTransform.apply(results);
}
}
@Override
@Transactional(readOnly = true)
public <S> S getAll(Function<Stream<MODEL>, S> resultsTransform) {
return resultsTransformer(entityDAO::findAll, resultsTransform);
}
}
这里的 resultsTransformer
方法是对 subclass 的温柔提醒,不要忘记 try-with-resources 模式。
下面是调用服务基础的控制器示例 class:
public abstract class AbstractReadOnlyController<MODEL extends AbstractSyncableEntity,
DTO extends AbstractSyncableDTOV2,
SERVICE extends ReadOnlyService<MODEL>>
{
@Autowired
protected SERVICE entityService;
protected Function<MODEL, DTO> modelToDTO;
protected AbstractReadOnlyController(Function<MODEL, DTO> modelToDTO) {
this.modelToDTO = modelToDTO;
}
protected List<DTO> modelStreamToDTOList(Stream<MODEL> s) {
return s.map(modelToDTO).collect(Collectors.toList());
}
// Read All
protected List<DTO> getAll(Optional<String> lastUpdate)
{
if (!lastUpdate.isPresent()) {
return entityService.getAll(this::modelStreamToDTOList);
} else {
Date since = new TimeUtility(lastUpdate.get()).getTime();
return entityService.getAllUpdatedSince(since, this::modelStreamToDTOList);
}
}
}
我认为让控制器通过 Java 8 lambda 指定 return 类型的服务是一种非常巧妙的泛型用法。虽然直接看到控制器 return 服务调用的结果对我来说很奇怪,但我很欣赏这段代码的紧凑性和表现力。
我想说这是尝试大规模切换到 Java 8 Streams 的积极因素。希望这对以后遇到类似问题的人有所帮助。
我有一个非常标准的 MVC 设置,其中包含用于我的 DAO 层的 Spring Data JPA 存储库,一个处理事务问题并实现业务逻辑的服务层,以及一个具有一些可爱的基于 REST 的视图层 JSON 端点。
我的问题是关于将 Java 8 Stream
大规模采用到这个可爱的架构中:如果我所有的 DAO return Stream
,我的服务 return 那些相同的 Stream
s(但做交易工作),我的视图作用于并处理那些 Stream
s,然后当我的视图开始处理我的 [= 中的模型对象时12=]s,Service层创建的事务将被关闭。如果底层数据存储尚未具体化我的所有模型对象(毕竟它是一个 Stream
,尽可能懒惰),那么我的视图将在尝试访问事务之外的新结果时出错。以前这不是问题,因为我会将结果完全具体化到列表中 - 但现在我们处于 Stream
的美丽新世界。
那么,处理这个问题的最佳方法是什么?将Service层内部的结果完全具体化为List并交还?视图层是否将完成块交给服务层,以便可以在事务内部完成进一步处理?
感谢您的帮助!
考虑到这一点,我决定尝试我在问题中提到的完成块解决方案。我所有的服务方法现在都有一个结果转换器作为它们的最终参数,它采用模型对象流并将其转换为视图层 needed/requested 的任何结果类型。我很高兴地报告它非常有效并且有一些不错的副作用。
这是我的服务基地 class:
public class ReadOnlyServiceImpl<MODEL extends AbstractSyncableEntity, DAO extends AbstractSyncableDAO<MODEL>> implements ReadOnlyService<MODEL> {
@Autowired
protected DAO entityDAO;
protected <S> S resultsTransformer(Supplier<Stream<MODEL>> resultsSupplier, Function<Stream<MODEL>, S> resultsTransform) {
try (Stream<MODEL> results = resultsSupplier.get()) {
return resultsTransform.apply(results);
}
}
@Override
@Transactional(readOnly = true)
public <S> S getAll(Function<Stream<MODEL>, S> resultsTransform) {
return resultsTransformer(entityDAO::findAll, resultsTransform);
}
}
这里的 resultsTransformer
方法是对 subclass 的温柔提醒,不要忘记 try-with-resources 模式。
下面是调用服务基础的控制器示例 class:
public abstract class AbstractReadOnlyController<MODEL extends AbstractSyncableEntity,
DTO extends AbstractSyncableDTOV2,
SERVICE extends ReadOnlyService<MODEL>>
{
@Autowired
protected SERVICE entityService;
protected Function<MODEL, DTO> modelToDTO;
protected AbstractReadOnlyController(Function<MODEL, DTO> modelToDTO) {
this.modelToDTO = modelToDTO;
}
protected List<DTO> modelStreamToDTOList(Stream<MODEL> s) {
return s.map(modelToDTO).collect(Collectors.toList());
}
// Read All
protected List<DTO> getAll(Optional<String> lastUpdate)
{
if (!lastUpdate.isPresent()) {
return entityService.getAll(this::modelStreamToDTOList);
} else {
Date since = new TimeUtility(lastUpdate.get()).getTime();
return entityService.getAllUpdatedSince(since, this::modelStreamToDTOList);
}
}
}
我认为让控制器通过 Java 8 lambda 指定 return 类型的服务是一种非常巧妙的泛型用法。虽然直接看到控制器 return 服务调用的结果对我来说很奇怪,但我很欣赏这段代码的紧凑性和表现力。
我想说这是尝试大规模切换到 Java 8 Streams 的积极因素。希望这对以后遇到类似问题的人有所帮助。