preface
We all know that Spring provides us with a lot of abstractions, such as transactional abstractions when we are working with databases, making it very easy to manipulate databases transactional. Whether you use JDBC, Mybatis, Hibernate, etc., or whether you use DataSource or JTA, Spring Transaction Abstraction management is a good way to integrate it. Let’s look at the abstract core interface of things
Spring transaction Abstraction
PlatformTransactionManager is things manager interface
// The transaction manager interface has the following interfaces: get transaction information, commit, and rollback
public interface PlatformTransactionManager {
TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
void commit(TransactionStatus var1) throws TransactionException;
void rollback(TransactionStatus var1) throws TransactionException;
}
Copy the code
Common transaction managers are the following:
- DataSourceTransactionManager
- HibernateTransactionManager
- JtaTransactionManager
These managers realize the PlatformTransactionManager’s three interfaces, implementation logic is slightly different, but the difference is not big for the user
Define things some of the parameters, the parameters of some things in TransactionDefinition. In Java, details are as follows:
public interface TransactionDefinition {
int PROPAGATION_REQUIRED = 0;
int PROPAGATION_SUPPORTS = 1;
int PROPAGATION_MANDATORY = 2;
int PROPAGATION_REQUIRES_NEW = 3;
int PROPAGATION_NOT_SUPPORTED = 4;
int PROPAGATION_NEVER = 5;
int PROPAGATION_NESTED = 6;
// The default isolation level, which is the same as the database isolation level
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = 1;
int ISOLATION_READ_COMMITTED = 2;
int ISOLATION_REPEATABLE_READ = 4;
int ISOLATION_SERIALIZABLE = 8;
// No timeout by default
int TIMEOUT_DEFAULT = -1;
}
Copy the code
The following two graphs illustrate these parameters:
Seven transaction propagation features:
Four transaction isolation levels:
What are dirty reads, unrepeatable reads, and phantom reads before looking at transaction isolation levels
Dirty read:Dirty reads are uncommitted data from one object that is read by another object, which is obviously unacceptable
Unrepeatable read:Non-repeatable read means that the same data is read for many times in a transaction and the read results are inconsistent.
Phantom reads:Transaction A makes A change to one data in the table that involves all rows in the table. Transaction B also modifies the data in the table by inserting a new row into the table. Then the user of transaction A will find that there are still unmodified rows in the table, as if in an illusion
With these concepts in mind, let’s look at the isolation level:
Here we can see, the Spring is not provide all things management implementation, but provides a standard PlatformTransactionManager manager operation interface, and regulating the conduct of its implementation by each platform to realize the specific things. This is Spring’s transaction abstraction.
Spring programming things
Spring provides the TransactionTemplate utility class to make it easy to use programmatic transactions. By default TransactionTemplate use DataSourceTransactionManager. In the Spring context, we can get the TransactionTemplate without configuring the TransactionTemplate bean. Take the following example.
@Service
public class UserInfoService {
@Resource
private UserInfoDAO userInfoDAO;
@Autowired
private TransactionTemplate transactionTemplate;
public void updateUser1(a){
transactionTemplate.execute(transactionStatus -> {
userInfoDAO.updateUserName(1."zhangsanfeng");
transactionTemplate.execute(transactionStatus2 -> {
userInfoDAO.updateUserName(2."lisi");
return null;
});
return null; }); }}Copy the code
Since Spring’s default thing propagation feature is PROPAGATION_REQUIRED, let’s verify that this is true
As you can see from the above two figures, the newTransaction property in TransactionStatus, the first true and the second false, matches what is described as PROPAGATION_REQUIRED. Other propagation features can be verified on their own.
Declarative thing
In addition to programmatic things, Spring also provides us with declarative things. Use the @Transactional annotation. Transactional can work on interfaces, interface methods, classes, and class methods. When used on a class, all public methods of that class will have transaction attributes of that type, and we can also use this annotation at the method level to override class-level definitions.
Although the @Transactional annotation can be applied to interfaces, interface methods, classes, and class methods, Spring recommends not using the annotation on interfaces or interface methods because it only works when using an interface-based proxy. In addition, the @Transactional annotation should only be applied to public methods, due to the nature of Spring AOP. If you use the @Transactional annotation ona protected, private, or default visibility method, this is ignored and no exceptions are raised.
The rollbackFor attribute of @Transactional sets up an array of Throwable to indicate that the transaction rolls back if the method throws these exceptions. By default, if the rollbackFor attribute is not configured, the transaction will be rolled back only when a RuntimeException is encountered. The following code thing will not take effect:
@Transactional
public void updateUser2(a) throws Exception {
int r1 = userInfoDAO.updateUserName(1."wanger");
int r2 = userInfoDAO.updateUserName(2."mawu");
if (r2==1) {throw newException(); }}Copy the code
If we change the exception thrown to RuntimeException, then things will take effect. Or specify exceptions to make things work, such as @transactional (rollbackFor = exception.class).
Why does something work with the @Transactional annotation?
This is because the Spring container generates a proxy object for the annotated object, which, when actually called, is the proxy object being called. The implementation of proxy objects is an AOP enhancement that implements the implementation of things.
How are the specified propagation characteristics and isolation levels implemented through annotations?
public @interface Transactional {
@AliasFor("transactionManager")
String value(a) default "";
@AliasFor("value")
String transactionManager(a) default "";
String[] label() default {};
Propagation propagation(a) default Propagation.REQUIRED;
Isolation isolation(a) default Isolation.DEFAULT;
int timeout(a) default- 1;
String timeoutString(a) default "";
boolean readOnly(a) default false;
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}
Copy the code
As you can see from the code, we can specify isolation levels and propagation properties, which Spring will read in the enhanced logic when it generates the proxy class for us.
8 cases of things failing and solutions
The database engine does not support transactions
MySQL MyISAM engine does not support transactions. InnoDB is the engine that supports transactions. InnoDB is used to support transactions.
Not managed by Spring
There is no point in talking about Spring transaction management without Spring management.
Methods are not public
Transactional can only be used with public methods, otherwise transactions will not fail. To use a non-public method, enable the AspectJ proxy mode.
The data source is not configured with a transaction manager
That is, transaction management is disabled. If Springboot is not enabled, perform the following operations.
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
Copy the code
If is SpringBoot, can be annotated @ EnableTransactionManagement directly on to start the class.
The propagation characteristics are mismatched
Propagation.NOT_SUPPORTED or Propagation.NOT_SUPPORTED.
Exception type error
Because the default exception type is runtime exception, it does not take effect if other exceptions are thrown. Transactional(@transactional (rollbackFor = exception.class))
The exception is eaten
Things don’t work if you code like this:
@Transactional(rollbackFor = Exception.class)
public void updateUser2(a) {
try {
int r1 = userInfoDAO.updateUserName(1."3");
int r2 = userInfoDAO.updateUserName(2."4");
if (r2==1) {throw newRuntimeException(); }}catch (Exception e){
}
}
Copy the code
Solution: You must throw an exception, or Spring transaction management will not go to the rollback logic
Class invocation
@Service
public class UserInfoService {
public void justUpdate(a){
updateUser2();
}
@Transactional(rollbackFor = Exception.class)
public void updateUser2(a) {}}Copy the code
The above code does not take effect because the internal invocation does not involve invocation of the proxy class, and there is no AOP enhancement, so it will not take effect. Solution: 1. Self-injection
@Service
public class UserInfoService {
@Autowired
private UserInfoService userInfoService;
public void justUpdate(a){
userInfoService.updateUser2();
}
@Transactional(rollbackFor = Exception.class)
public void updateUser2(a) {}}Copy the code
2. Spring context
@Service
public class UserInfoService {
ApplicationContext applicationContext;
public void justUpdate(a){
UserInfoService userInfoService = (UserInfoService) applicationContext.getBean("userInfoService");
userInfoService.updateUser2();
}
@Transactional(rollbackFor = Exception.class)
public void updateUser2(a) {}}Copy the code
3. Get his proxy class and call it directly
@Service
public class UserInfoService {
public void justUpdate(a){
((UserInfoService) AopContext.currentProxy()).updateUser2();
}
@Transactional(rollbackFor = Exception.class)
public void updateUser2(a) {}}Copy the code
— — — — — — — — — — — — — — — — — — — — — — — — — — — — END — — — — — — — — — — — — — — — — — — — — — — — — — — — more Spring related knowledge, pay attention to me, each platform is the same ID