@Transactional
is an annotation that we can hardly escape when using Spring. This annotation is mainly used to declare transactions. Its implementation principle is to weave the implementation statements of transaction management before and after the annotation modification method through Spring AOP, so developers only need to use one annotation to replace a series of tedious transaction start, transaction closure and other repetitive coding tasks.
The coding method is indeed simple, but because the intuitive realization logic is hidden, some wrong coding methods may @Transactional
annotation and fail to achieve the function of the transaction. The most direct manifestation is that an exception was thrown during the execution of the method, but the transaction was not rolled back, which eventually led to the generation of dirty data.
I also wrote an interesting discussion on my blog before me ask a question: Will this transaction be rolled back? , at that time many people gave the wrong answer , if you haven’t seen it, why not go in and challenge?
Although some special situations have been discussed before, there are still small partners who will email and ask some questions about transaction failure in WeChat groups. The main @Transactional
that 061935000b2224 declares that the transaction is invalid. There are really many situations! So, write an article today to summarize, if you encounter it again next time, then open this article and look down one by one to see if something is wrong. Of course, there may be omissions here, so if you have other error cases, you can also tell me, and I will continue to organize them into this article.
1. Call in the same class
Error case:
public class A {
public void methodA() {
methodB();
// 其他操作
}
@Transactional
public void methodB() {
// 写数据库操作
}
}
This type of error for all annotation-based Spring AOP implementation, such as: "Use @Async asynchronous call" mentioned @Async
notes, "use @Scheduled achieve timing task" mentioned @Scheduled
notes, also there the Spring annotation using caching solution mentioned @Cacheable
notes and so on.
The way to solve this problem is relatively simple, it is better to plan the hierarchical relationship reasonably, such as this:
@Service
@AllArgsConstructor
public class A {
private B b;
public void methodA() {
b.methodB();
// 其他操作
}
}
@Service
public class B {
@Transactional
public void methodB() {
// 写数据库操作
}
}
Note : Here Class A uses the constructor to inject the implementation of B (why @Autowrire
not used, you can see this shared a few days ago when not to use @Autowired to inject ), the constructor is generated @AllArgsConstructor
If you are not familiar with this, you can take a look at the previous article Lombok: Make JAVA code more elegant ( ).
2. @Transactional modified method is not public
Error case:
public class TransactionalMistake {
@Transactional
private void method() {
// 写数据库操作
}
}
This is also the requirement to be met based on the annotations implemented in Spring AOP. This is the simplest, easy to understand, and intuitive, so I won’t expand it in detail. public
change the method access type to 061935000b23d9.
3. Different data sources
Error case:
public class TransactionalMistake {
@Transactional
public void createOrder(Order order) {
orderRepo1.save(order);
orderRepo2.save(order);
}
}
Sometimes, one operation may write multiple data sources at the same time. For example, orderRepo1
and orderRepo2
in the above example are two different data sources connected. By default, this kind of cross-data source transaction will not succeed.
If you want to implement transactions between multiple data sources, you can introduce JTA. For details on how to do it, you can see the previous sharing "Use JTA to achieve transaction management of multiple data sources"
4. The rollback exception configuration is incorrect
By default, only RuntimeException
and Error
are rolled back. If they and their descendants are abnormal, they will not roll back.
Therefore, when customizing exceptions, you must make appropriate plans. If you want to affect the transaction rollback, you can define it as RuntimeException
; if it is not RuntimeException
, but you also want to trigger a rollback, you can use the rollbackFor
attribute to specify the Rollback exception.
public class TransactionalMistake {
@Transactional(rollbackFor = XXXException.class)
public void method() throws XXXException {
}
}
5. The database engine does not support transactions
This example comes from a reader's feedback. The code is exactly the same as my case. My side is good, but it just doesn't roll back.
Later, it was found out because the configuration of a key attribute was missing:
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.database-platform
configuration here is mainly used to set the dialect used by hibernate. MySQL5InnoDBDialect is specially used here, mainly to ensure that when using Spring Data JPA, Hibernate uses the InnoDB storage engine when automatically creating tables, otherwise the default storage engine MyISAM will be used to build the tables, and the MyISAM storage engine is transaction-free.
If your transaction does not take effect, then you can see whether the created table is using the MyISAM storage engine, if so, that's the reason!
summary
If you see at the end and find that there are other things that have not been included, welcome to let us know, and we will continue to update this article! To help readers who encounter such problems.
Well, that's all for today's study! If you encounter difficulties in the learning process? You can join our super high-quality Spring technical exchange group , participate in exchanges and discussions, and better learn and progress!
Welcome to pay attention to my public account: Program Ape DD, share knowledge and thoughts that can’t be seen elsewhere
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。