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

Mic*_*ler 7 spring-mvc java-8 spring-data spring-data-jpa java-stream

我有一个非常标准的MVC设置,包括用于DAO层的Spring Data JPA存储库,一个处理Transactional关注和实现业务逻辑的Service层,以及一个具有一些可爱的基于REST的JSON端点的视图层.

我的问题是将Java 8批量采用Stream到这个可爱的架构中:如果我的所有DAO都返回Streams,我的服务返回那些相同的Streams(但是做Transactional工作),我的视图处理并处理这些Stream,然后通过当我的视图开始处理我的Streams中的Model对象时,Service层创建的事务将被关闭.如果底层数据存储尚未实现我的所有模型对象(它Stream毕竟是尽可能的懒惰),那么我的视图将在尝试访问事务之外的新结果时出错.以前这不是问题,因为我会将结果完全实现到List中 - 但现在我们处于Streams 的勇敢新世界.

那么,处理这个问题的最佳方法是什么?完全将服务层内的结果实现为List并将其交还?让View层将Service层交给一个完成块,以便在事务内部进行进一步的处理吗?

谢谢您的帮助!

Mic*_*ler 4

经过思考,我决定尝试我在问题中提到的完成块解决方案。我的所有服务方法现在都有一个结果转换器作为其最终参数,该转换器获取模型对象流并将其转换为视图层需要/请求的任何结果类型。我很高兴地报告它的作用就像一个魅力,并且有一些很好的副作用。

这是我的服务基类:

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);
    }

}
Run Code Online (Sandbox Code Playgroud)

这里的方法resultsTransformer是一个温和的提醒子类不要忘记 try-with-resources 模式。

这是调用服务基类的示例控制器:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我认为让控制器通过 Java 8 lambda 来指定服务的返回类型是对泛型的一种非常巧妙的使用。虽然看到控制器直接返回服务调用的结果对我来说很奇怪,但我确实很欣赏这段代码的紧凑性和表现力。

我想说,这对于尝试大规模切换到 Java 8 Streams 来说是一个积极的结果。希望这可以帮助以后遇到类似问题的人。