Spring’s declarative transactions are easy to implement. You can bind Transactional controls to methods requiring Transactional control by adding the @Transactional annotation to them. But the configuration of the parameters is outlined today, and there is an AOP trap that needs your attention.
Propagation properties | The characteristics of |
---|---|
REQUIRED | The default propagation property, indicating that if a transaction exists in the current environment, the transaction is kept executing, otherwise a new transaction is opened and executed in a new transaction |
REQUIRES_NEW | Create a new transaction and execute it in the new transaction regardless of whether there are transactions in the current environment, suspending the original transaction |
SUPPORTS | Indicates that if a transaction exists in the current environment, it is executed in that transaction; otherwise, it is not executed in transactional mode |
NOT_SUPPORTED | Indicates that this method does not control transactions and suspends if there are transactions in the current environment |
MANDATORY | Indicates that this method must be executed in a transaction, throwing an exception if there is no transaction in the current environment |
NEVER | Indicates that this method can run without transaction control and throws an exception as soon as a transaction is propagated |
NESTED | Indicates that the transaction is run in a nested transaction if it exists, or executed on the REQUIRED attribute if no transaction exists |
Common attributes are REQUIRED and REQUIRES_NEW. Let’s use code to verify these two common attributes:
1. Open transaction support @ EnableTransactionManagement
/ * * *@author wangzhi
*/
@SpringBootApplication
@EnableTransactionManagement
public class DemoApplication {
public static void main(String[] args) {
newSpringApplication(DemoApplication.class).run(args); }}Copy the code
2. Entity classes and databases
/ * * *@author wangzhi
*/
@Data
@TableName("course")
public class CourseEntity {
@TableId(type = IdType.AUTO)
private Integer id;
private String courseName;
private BigDecimal price;
}
Copy the code
3. Service layer business code
Let’s first check REQUIRED
/ * * *@author wangzhi
*/
@Service
public class TransactionService {
@Autowired
private CourseMapper courseMapper;
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void save(a) {
CourseEntity course = new CourseEntity();
course.setCourseName("Chinese");
course.setPrice(new BigDecimal(100));
courseMapper.insert(course);
// create an exception
System.out.println(1/0);
}
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void saveInit(a) {
CourseEntity course = new CourseEntity();
course.setCourseName("Mathematics");
course.setPrice(new BigDecimal(100));
courseMapper.insert(course);
// Call the save method
try{
save();
}catch(Exception e){ e.printStackTrace(); }}}Copy the code
4. How many records does the database have?
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = DemoApplication.class)
class DemoApplicationTests {
@Autowired
TransactionService transactionService;
@Test
public void transactionTest(a) throws Exception { transactionService.saveInit(); }}Copy the code
5. Look at the results
4. Calm down. REQUIRES_NEW
/ * * *@author wangzhi
*/
@Service
public class TransactionService {
@Autowired
private CourseMapper courseMapper;
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
public void save(a) {
CourseEntity course = new CourseEntity();
course.setCourseName("Chinese");
course.setPrice(new BigDecimal(100));
courseMapper.insert(course);
// create an exception
System.out.println(1/0);
}
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void saveInit(a) {
CourseEntity course = new CourseEntity();
course.setCourseName("Mathematics");
course.setPrice(new BigDecimal(100));
courseMapper.insert(course);
// Call the save method
try {
save();
}catch(Exception e){ e.printStackTrace(); }}}Copy the code
5. This is not a problem with transaction propagation, but with dynamic proxies. Call each other transaction methods in the same class, cutting into the method that initiated the transaction, and whatever transaction method is called internally will default to this object to invoke normal methods. These transaction annotations simply don’t work. Transaction is based on the dynamic objects generated by the dynamic proxy to perform the control of the transaction, so calling other transaction methods within the same method must obtain its corresponding proxy object to call, otherwise it must be placed in a different class. Ending method:
6. The introduction of AOP
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Copy the code
7.@EnableAspectJAutoProxy(exposeProxy = true)
/ * * *@author wangzhi
*/
@SpringBootApplication
@EnableTransactionManagement
@MapperScan("com.example.mapper")
@EnableAspectJAutoProxy(exposeProxy = true)
public class DemoApplication {
public static void main(String[] args) {
newSpringApplication(DemoApplication.class).run(args); }}Copy the code
8. Revamp service
/ * * *@author wangzhi
*/
@Service
public class TransactionService {
@Autowired
private CourseMapper courseMapper;
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
public void save(a) {
CourseEntity course = new CourseEntity();
course.setCourseName("Chinese");
course.setPrice(new BigDecimal(100));
courseMapper.insert(course);
// create an exception
System.out.println(1/0);
}
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void saveInit(a) {
CourseEntity course = new CourseEntity();
course.setCourseName("Mathematics");
course.setPrice(new BigDecimal(100));
courseMapper.insert(course);
// Call the save method
try {
TransactionService proxy = (TransactionService)AopContext.currentProxy();
proxy.save();
}catch(Exception e){ e.printStackTrace(); }}}Copy the code
Check out the results: