Spring方法标注了@Transactional,为什么没有开启事务 | Java提升营

Spring方法标注了@Transactional,为什么没有开启事务

Question 1

@Transactional method calling another method without @Transactional anotation?

I’ve seen a method in a Service class that was marked as @Transactional but it was also calling some other methods in that same class which were not marked as @Transactional.

Does that mean the call to separate methods are causing the application to open separate connections to DB or suspend the parent transaction, etc?

What’s the default behavior for a method without any annotations that is called by another method with @Transactional annotation?

Answer:

When you call a method without @Transactional within a transaction block, the parent transaction will continue to the new method. It will use the same connection from the parent method(with @Transactional) and any exception caused in the called method(without @Transactional will cause the transaction to rollback as configured in the transaction definition.

If you call a method with a @Transactional annotation from a method with @Transactional within the same instance, then the called methods transactional behavior will not have any impact on the transaction. But if you call a method with a transaction definition from another method with a transaction definition, and they are in different instances, then the code in the called method will follow the transaction definitions given in the called method.

You can find more details in the section Declarative transaction management of spring transaction documentation.

Spring declarative transaction model uses AOP proxy. so the AOP proxy is responsible for creation of the transactions. The AOP proxy will be active only if the methods with in the instance are called from out side the instance.

Question 2

In Spring, a method that is annotated with @Transactional will obtain a new transaction if there isn’t one already, but I noticed that a transactional method does not obtain any transaction if it is called from a non-transactional one. Here’s the code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Component
public class FooDao {
private EntityManager entityManager;

@PersistenceContext
protected void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}

@Transactional
public Object save(Object bean) {
return this.entityManager.merge(bean);
}

public Object saveWrap(Object bean) {
return save(bean);
}
}

@Component
public class FooService {
private FooDao fooDao;

public void save(Object bean) {
this.fooDao.saveWrap(bean); // doesn't work.
this.fooDao.save(bean); // works
}
}

saveWrap() is a regular method that calls save() which is transactional, but saveWrap() won’t persist any changes.

I’m using Spring 3 and Hibernate 3. What am I doing wrong here? Thanks.

Answer 1:

It is one of the limitations of Springs AOP. Because the dao bean is in fact a proxy when it is created by spring, it means that calling a method from within the same class will not call the advice (which is the transaction). The same goes for any other pointcut

Answer 2:

Yes, this is expected behaviour. @Transactional tells spring to create a proxy around the object. The proxy intercepts calls to the object from other objects. The proxy does not intercept calls within the object.

If you want to make this work, add @Transactional on the method that is invoked from “outside”.

给老奴加个鸡腿吧 🍨.