简体   繁体   English

@Transactional 方法结束后数据库不会立即更改

[英]Database is not changed immediately after the @Transactional method ends

I have a Java, Spring Data application with a PostgreSQL DB and I have a code similar to this in my service layer:我有一个 Java、Spring 数据应用程序和一个 PostgreSQL 数据库,我的服务层中有一个与此类似的代码:

public void method1() {
    PersonDto dto = method2();
    method3(dto);
}

@Transactional
public PersonDto method2() {
    Person p1 = personRepository.saveAndFlush(new Person());
    return createPersonDto(p1);
}

@Transactional
public void method3(PersonDto p1) {
    Person p2 = personRepository.findById(p1.getId());
    if (p2 == null) {
        System.out.println("Not in the DB");
    } else {
        System.out.println("In the DB");
    }
}

Sometimes this code prints "Not in the DB", when I was expecting that it would always print "In the DB".有时这段代码打印“不在数据库中”,而我期望它总是打印“在数据库中”。 My questions are:我的问题是:

  1. This saveAndFlush is even necessary?这个 saveAndFlush 甚至是必要的? Since I'm not reusing that Entity in the same transaction, I guess it would have the same effect as a save, right?因为我没有在同一个事务中重复使用那个实体,我想它会和保存有同样的效果,对吧?
  2. How to guarantee that the changes in method2 will be committed before method 3 calls it?如何保证method2 的修改在method 3 调用之前提交? So it will always print "In the DB"?所以它总是打印“在数据库中”?
  3. The fact that I'm converting my Entity to a DTO is what is messing this up?我正在将我的实体转换为 DTO 的事实是什么搞砸了? Maybe if I was passing the Entity itself it would work properly?也许如果我传递实体本身它会正常工作?

I'm thinking about @Transactional only in method1, or replacing @Transactional in method3 with @Transactional(isolation = Isolation.READ_UNCOMMITTED).我正在考虑仅在 method1 中使用 @Transactional,或者将 method3 中的 @Transactional 替换为 @Transactional(isolation = Isolation.READ_UNCOMMITTED)。 Any advice?有什么建议吗?

I can see a couple of reason why you're getting the results you describe.我可以看到您获得所描述结果的几个原因。

One possibility is that @Transactional doesn't necessarily mean "execute this method in a totally separate transaction."一种可能性是@Transactional不一定意味着“在完全独立的事务中执行此方法”。 The default propagation is REQUIRED , which will use an existing transaction if one is already active.默认传播是REQUIRED ,如果一个已经处于活动状态,它将使用现有事务。 So if method2() is called in a stack where a transaction was opened earlier, it won't create a new one (and thus the new entity won't be committed) until that original transaction is committed.因此,如果在先前打开事务的堆栈中调用method2() ,则在提交原始事务之前,它不会创建新事务(因此不会提交新实体)。 If this is what's happening, one solution would be to change the propagation to REQUIRES_NEW :如果发生这种情况,一种解决方案是将传播更改为REQUIRES_NEW

@Transactional(propagation = REQUIRES_NEW)

Another possibility, which seems more likely, is that these methods are all in the same class. To understand why that fails, you have to understand how Spring implements handling of @Transactional (and most other behavioral annotations): using proxies.另一种可能性(似乎更有可能)是这些方法都在同一个 class 中。要理解失败的原因,您必须了解 Spring 如何实现@Transactional (以及大多数其他行为注释)的处理:使用代理。 That means that only method calls that come through the proxy get the transactional behavior.这意味着只有通过代理的方法调用才能获得事务行为。 That's the typical scenario when one object calls a reference it has to another;这是典型的场景,当一个 object 调用它对另一个的引用时; the reference is actually to the proxy, which intercepts the call and wraps the behavior (in this case, transactionality) around it.该引用实际上是对代理的引用,它拦截调用并将行为(在本例中为事务性)包装在调用周围。 However, when calling methods within the same instance, there is no proxy in play (Spring doesn't/can't replace this with a reference to the proxy), and thus no transactional behavior.然而,当在同一个实例中调用方法时,没有代理在起作用(Spring 不会/不能用对代理的引用替换this ),因此没有事务行为。

There are a few ways to work around that, but first I would consider how to restructure my classes and methods to avoid it.有几种方法可以解决这个问题,但首先我会考虑如何重构我的类和方法来避免它。 If that's not reasonable, things like AspectJ, TransactionTemplate , and maybe some other ideas are discussed in this SO Q&A .如果这不合理,则在此 SO Q&A中讨论诸如 AspectJ、 TransactionTemplate以及其他一些想法之类的内容。


Addendum: This question and its answers also talks about options to work around the proxying problem.附录:这个问题及其答案还讨论了解决代理问题的选项。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM