【问题标题】:Spring Data JPA - Java 8 Stream Support & Transactional Best PracticesSpring Data JPA - Java 8 流支持和事务最佳实践
【发布时间】:2016-08-12 14:06:11
【问题描述】:

我有一个非常标准的 MVC 设置,其中包含用于 DAO 层的 Spring Data JPA 存储库、一个处理事务问题并实现业务逻辑的服务层,以及一个具有一些可爱的基于 REST 的 JSON 端点的视图层。

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

那么,处理这个问题的最佳方法是什么?将服务层内部的结果完全物化为列表并将其交还给他们?视图层是否将完成块交给服务层,以便可以在事务内部完成进一步的处理?

感谢您的帮助!

【问题讨论】:

    标签: spring-mvc java-8 spring-data spring-data-jpa java-stream


    【解决方案1】:

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

    这是我的服务基类:

    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 模式。

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

    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 来决定服务的返回类型是对泛型的一种非常巧妙的使用。虽然看到 Controller 直接返回 Service 调用的结果对我来说很奇怪,但我很欣赏这段代码的紧凑性和表现力。

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

    【讨论】:

    • 简单地将转换器函数传递给服务是一个了不起的想法。我在同一个泡菜中并试图延迟交易,但您的想法非常适合。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-03
    • 2011-06-25
    相关资源
    最近更新 更多