跨多个存储库的Spring Data事务

13

我需要将2个不同的实体插入到2个不同的表中,使用同一个事务。如果第二个插入失败,第一个插入应该被回滚。

有没有一种比较好的方式来实现这个需求?

伪代码:

start tx
repo1.save(myEntity);
repo2.save(anotherEntity);
try commit

我知道你可以利用@Transactional,但仅限于方法级别?


2
将所有调用包装在一个单一的服务方法中,该方法本身带有 @Transactional 注释。 - M. Deinum
当然,你需要XA JDBC驱动程序和一个事务管理器。使用Spring的注解很容易实现这一点。 - duffymo
3个回答

11
  1. 您需要检查是否已经设置了 autocommit = false。

  2. 将保存操作包装到一个服务方法中,并将其标记为 @Transactional。但如果您使用的是 save() 自定义方法,请检查是否将其保存为标记为 @Transactional,并具有传播级别 required_new 或 nested。如果需要,您可以在保存服务方法中使用 REQUIRES_NEW,使该服务方法与其他事务无关。

同时,您可以将其包装在 TransactionTemplate 中。

@Autowired
private TransactionTemplate transactionTemplate;

transactionTemplate.execute(new TransactionCallbackWithoutResult() {
    @Override
    public void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
            repo1.save(myEntity);
            repo2.save(anotherEntity);
    });

很好的答案!解决了我一次提交多个存储库的问题。 - hazy

10

通常在仓库方法周围声明 @Transactional 是错误的想法。

仓库只是用于访问域实体。业务逻辑通常涉及多个域实体之间的协作。

你的架构中应该有一个层来组合业务逻辑。这通常对应于向外部公开的服务。

这通常是您应该设置事务边界的地方。通常是一个控制器或服务方法。


我同意你的答案,认为事务应该应用于服务方法而不是控制器方法或存储库... - Hamedz
@Hamedz 我个人认为,就我在答案中所说的而言,在控制器周围设置事务边界可能是有意义的,这取决于您的设计/约定。尽管存储库很少是事务边界的正确位置。 - Adrian Shum

-3

操作步骤:

  1. 确保在repo1和repo2方法中使用接口作为Spring事务代理,如果只使用类,则可能需要添加其他依赖项。
  2. 使用 @Transactional(propagation=Propagation.REQUIRED) 注释 repo1.save(..) 和 repo2.save(..) 方法。
  3. 从类外的任何方法中调用 repo1.save()。
  4. 使用 special junit runner 充分测试代码。

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