【问题标题】:Is it thread safe to use Spring Data JPA repository inside Java 8 forEach loop?在 Java 8 forEach 循环中使用 Spring Data JPA 存储库是否线程安全?
【发布时间】:2017-04-22 21:43:10
【问题描述】:

我可以在 Java 8 forEach 循环中使用 Spring Data JPA 存储库吗?它是线程安全的吗?

public interface MajorModOptRepository extends CrudRepository<MajorModOpt, Long> {
    Set<MajorModOpt> findManyByManCodeAndModCode(String manCode, String modCode);
}

这里是 forEach 循环:

@Component
public class MajorModOptsHelper {

    @Autowired
    private MajorModOptRepository majorModOptRepository;

    public void setMajorOpts(@NonNull List<Vehicle> vehicles) {
        vehicles.forEach(this::setMajorOpts);

    }

    // This method is called by above forEach
    public void setMajorOpts(Vehicle vehicle) {
        ...
        // Repository method is called here
        Set<MajorModOptVO> knownOpts = majorModOptRepository.findManyByManCodeAndModCode(vehicle.getManCode(), vehicle.getModCode());
        ...
    }
}

提前谢谢你。

【问题讨论】:

  • 在集合上调用forEach 时不涉及多线程。唯一要记住的是,某些集合,如 ArrayListHashSet 在迭代时不得修改,但在使用基于 Iteratorfor 循环时也是如此。
  • 它是线程安全的。但是,尽管如此,我更被你即将做的一件坏事所吸引。这只是在forEach 中调用findManyByManCodeAndModCode 方法。 :)

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


【解决方案1】:

这是踩保存。不涉及多线程。

您唯一应该注意的是,不要为每个findManyByManCodeAndModCode 开启新事务(除非您想要它)。解决这个问题最简单的方法是在你的setMajorOpts(@NonNull List&lt;Vehicle&gt; vehicles) 方法中添加一个@Transactional

【讨论】:

  • 更糟。他在forEach 中触发了该存储库方法,这基本上等同于N+1 问题。
  • @Branislav Lazic:你是对的,但这个问题的范围是线程问题,也许是线程问题。性能不在该问题的范围内。 我认为您的评论很有价值,。无论如何,只有在了解上下文的情况下才能对性能问题进行有意义的论证。例如:如果车辆列表通常只有 2 或 3 项,那么任何对帽子的优化都可能过早地浪费时间。
  • 如果我在 MajorModOptsHelper 类上添加 @Transactional,那么每次调用 setMajorOpts 方法都会打开一个事务,对吗?这正是你所警告的。谢谢。
【解决方案2】:

无论是您当前的forEach 实现还是传统的for-loop,您的代码都是线程安全的。

您应该担心事务边界(以编程方式或通过@Transactional 分界实现),因为从概念上讲,您将List&lt;Vehicle&gt; save 操作拆分为两个方法。如果Vehicle save 操作之一失败应该怎么办?使用默认的Propagation.REQUIRED,整个操作将被回滚,而如果您的第二个每车辆save 操作是Propagation.REQUIRES_NEW,那么之前的所有故障前Vehicles 都将通过(=已提交)。

【讨论】:

  • 其实这里你唯一应该担心的是使用字段注入(不好)而不是构造函数注入(正确)。
  • 一个简单的@Transactional 注释setMajorOpts(@NonNull List&lt;Vehicle&gt; vehicles) 或更外部的方法围绕工作单元构建一个块将解决这个问题。
  • @Engineer Dollery - 同意。但这并不是唯一需要担心的事情。根据 OP 提供的代码,事务边界在这里很重要。
  • 对于任何实际用途,是的,但您永远不知道,事务边界在这里可能并不重要,或者可能在控制器中首先调用此代码。
  • 我找到了关于@EngineerDollery 提到的主题的好文章:vojtechruzicka.com/…
猜你喜欢
  • 1970-01-01
  • 2017-03-16
  • 2015-06-19
  • 2019-10-19
  • 1970-01-01
  • 2013-04-04
  • 1970-01-01
  • 2015-07-13
  • 1970-01-01
相关资源
最近更新 更多