Spring Data JPA - Java 8流支持和事务最佳实践

8
我有一个标准的MVC架构,使用Spring Data JPA Repositories作为DAO层,Service层处理事务关注点并实现业务逻辑,视图层具有一些可爱的基于REST的JSON终点。
我的问题是关于Java 8 Stream在这个美妙的架构中的全面采用:如果所有的DAO都返回Stream,则我的服务也返回相同的Stream(但执行Transactional工作),我的视图对这些Stream进行操作和处理,那么当我的视图开始在Stream中处理模型对象时,由Service层创建的事务将已经被关闭。如果底层数据存储尚未将所有模型对象实现(它毕竟是一个可能懒惰的Stream),则我的视图在事务外尝试访问新结果时会出现错误。以前这不是问题,因为我会完全将结果物化为List-但现在我们处于Stream的美好新世界中。
那么,最好的处理方式是什么?在Service层内完全将结果物化为List并交还吗?让View层将完成块交还给Service层,以便进一步处理可以在事务内完成?
谢谢您的帮助!
1个回答

4
在思考后,我决定尝试在我的问题中提到的completion block解决方案。现在,所有的服务方法都以结果转换器作为它们的最后一个参数,该转换器将Model对象的Stream转换为View层所需/请求的任何结果类型。我很高兴地报告它像魔法一样运行,并且有一些很好的副作用。
这是我的服务基类:
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方法是提醒子类不要忘记使用try-with-resources模式的温柔提示。
下面是一个示例Controller调用服务基类:
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指定服务的返回类型是一个非常棒的方法。虽然对于我来说,看到控制器直接返回服务调用的结果有些奇怪,但我确实欣赏这段代码的紧凑和表达力。

我想说,这对于尝试全面转换为Java 8 Streams是一个积极的影响。希望这能帮助到以后有类似问题的人。


将转换器函数简单地传递给服务是一个绝妙的想法。我曾经也遇到同样的问题,试图延迟交易,但你的想法非常优雅地解决了这个问题。 - Pieter De Bie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接