When it comes to Spring AOP, we are going to enhance transaction control by ourselves, and the Spring framework is ready for us to use these functions. Of course, don't think that SrpingAOP can only enhance the transaction function. AOP can be used to enhance any method you want to enhance. So how do we use it?
XML configuration declarative transaction
Configuration steps of declarative transaction control based on XML in spring 1. Configure transaction manager 2. Notification of configuration transaction At this point, we need to import constraints of transaction tx namespace and constraints, and also need aop's Configure transaction notifications with < TX: advice > tags Properties: id: a unique identifier for transaction notification Transaction manager: provides a transaction manager reference for transaction notifications 3. Configure common pointcut expressions in aop 4. Establish the relationship between transaction notification and pointcut expression Use< aop:advisor >Label Property: pointcut Ref: pointcut expression for reference configuration Advice Ref: used to refer to transaction notifications 5. Configure transaction properties Is inside the notification < TX: advice > tag of the transaction Isolation: used to specify the isolation level of transactions. The DEFAULT level is DEFAULT, which means to use the DEFAULT isolation level of the database Propagation: used to specify the propagation behavior of a transaction. The default value is REQUIRED, which means there must be transaction, adding, deleting and changing options. Support can be selected for query method Read only: used to specify whether the transaction is read-only. Only the query method can be set to true. The default value is false, indicating read-write Timeout: used to specify the timeout of the transaction. The default value is - 1, which means never timeout. If a value is specified, in seconds Rollback for: used to specify an exception. When the exception is generated, the transaction is rolled back. When other exceptions are generated, the transaction is not rolled back. There is no default value. Indicates that any exception is rolled back No rollback for: used to specify an exception. When the exception is generated, the transaction will not be rolled back. When other exceptions are generated, the transaction will be rolled back. There is no default value. Indicates that any exception is rolled back
XML configuration transaction case (case using JdbcTemplate)
IAccountDao interface class
package com.dao; import com.domain.Account; //Account persistence interface public interface IAccountDao { Account findAccountById(int id); Account findAccountByName(String name); void updateAccount(Account account); }
IAccountDao implementation class
package com.dao.impl; import com.dao.IAccountDao; import com.domain.Account; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import java.util.List; //Account persistence layer implementation class public class AccountDaoImpl implements IAccountDao { private JdbcTemplate jdbcTemplate; public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } public Account findAccountById(int id) { List<Account> accountList = jdbcTemplate.query("select * from account where id = ?" , new BeanPropertyRowMapper<Account>(Account.class) , id); return accountList.isEmpty()? null : accountList.get(0); } public Account findAccountByName(String name) { List<Account> accountList = jdbcTemplate.query("select * from account where name = ? " , new BeanPropertyRowMapper<Account>(Account.class) , name); if(accountList.isEmpty()){ return null; } if(accountList.size() > 1){ throw new RuntimeException("Result set is not unique"); } return accountList.get(0); } public void updateAccount(Account account) { jdbcTemplate.update("update account set name = ? , money = ? where id = ?" , account.getName() , account.getMoney() , account.getId()); } }
IAccountService interface class
package comservice; import com.domain.Account; public interface IAccountService { Account findAccountById(Integer id); void transfer(String sourceName , String targetName , Float money); }
IAccountService implementation class
package com.service.impl; import com.dao.IAccountDao; import com.domain.Account; import com.service.IAccountService; import java.util.List; //Business layer implementation class of account //Transaction control is at the business level public class AccountServiceImpl implements IAccountService { private IAccountDao accountDao; public void setAccountDao(IAccountDao accountDao) { this.accountDao = accountDao; } public Account findAccountById(Integer id) { Account accounts = accountDao.findAccountById(id); return accounts; } public void updateAccount(Account account) { accountDao.updateAccount(account); } public void transfer(String sourceName, String targetName, Float money) { System.out.println("Transfer in progress"); //1. Query transfer out account by name Account source = accountDao.findAccountByName(sourceName); //2. Query transfer in account by name Account target = accountDao.findAccountByName(targetName); //3. Decrease in transfer out account source.setMoney(source.getMoney() - money); //4. Add money to transfer in account target.setMoney(target.getMoney() + money); //5. Update transfer out account accountDao.updateAccount(source); //int i =1/0; //6. Update transfer in account accountDao.updateAccount(target); } }
bean.xml Constraints for adding transaction constraints
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
Configure transaction manager
<!--Configure transaction manager--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean>
Notification of configuration transaction (detailed explanation in the introduction)
<tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!--Configure notifications for transactions--> <tx:method name="*" propagation="REQUIRED" read-only="false"/> <tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method> </tx:attributes> </tx:advice>
Configure AOP to establish the corresponding relationship between pointcut expression and transaction notification at the same time (detailed explanation in the beginning)
<aop:config> <! -- configure pointcut expressions -- > <aop:pointcut id="pointCut" expression="execution(* com.itheima.service.impl.*.*(..))"></aop:pointcut> <! -- establish correspondence between pointcut expression and transaction notification -- > <aop:advisor advice-ref="txAdvice" pointcut-ref="pointCut"></aop:advisor> </aop:config>
Test class
package com.test; import com.service.IAccountService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; //Use Junit unit to test our configuration @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:bean.xml") public class AccountServiceTest { @Autowired private IAccountService as; @Test public void testTransfer(){ as.transfer("aaa" , "bbb" , 200f); } }
When there is no abnormality:
After operation:
When an exception occurs:
Turn off the int i =1/0 comment in the transfer method.
After operation:
It can be found that transaction control has been enhanced into the pointcut method. At this point, declarative transactions are configured based on XML.
Annotation configuration declarative transaction
Configuration steps of declarative transaction control with annotations in spring 1. Configure transaction manager 2. Enable spring annotation transaction support 3. Use @ Transactional annotation where transaction support is needed
Note: in this process, pure annotation is used
newly build jdbcConfig.properties Property profile
The information stored in it is needed to create the data source.
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/test jdbc.username=root jdbc.password=
New IAccountDao
package com.dao; import com.domain.Account; //Account persistence interface public interface IAccountDao { Account findAccountById(int id); Account findAccountByName(String name); void updateAccount(Account account); }
New IAccountDao implementation class
package com.dao.impl; import com.dao.IAccountDao; import com.domain.Account; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import java.util.List; //Account persistence layer implementation class @Repository("accountDao") public class AccountDaoImpl implements IAccountDao { @Autowired private JdbcTemplate jdbcTemplate; public Account findAccountById(int id) { List<Account> accountList = jdbcTemplate.query("select * from account where id = ?" , new BeanPropertyRowMapper<Account>(Account.class) , id); return accountList.isEmpty()? null : accountList.get(0); } public Account findAccountByName(String name) { List<Account> accountList = jdbcTemplate.query("select * from account where name = ? " , new BeanPropertyRowMapper<Account>(Account.class) , name); if(accountList.isEmpty()){ return null; } if(accountList.size() > 1){ throw new RuntimeException("Result set is not unique"); } return accountList.get(0); } public void updateAccount(Account account) { jdbcTemplate.update("update account set name = ? , money = ? where id = ?" , account.getName() , account.getMoney() , account.getId()); } }
New IAccountService interface class
package com.service; import com.domain.Account; public interface IAccountService { Account findAccountById(Integer id); void transfer(String sourceName , String targetName , Float money); }
Create a new IAccountService implementation class
package com.service.impl; import com.dao.IAccountDao; import com.domain.Account; import com.service.IAccountService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; //Business layer implementation class of account //Transaction control is at the business level @Service //This label indicates that transaction support is required. Only classes with this annotation can start transactions @Transactional public class AccountServiceImpl implements IAccountService { @Autowired private IAccountDao accountDao; public Account findAccountById(Integer id) { Account accounts = accountDao.findAccountById(id); return accounts; } public void updateAccount(Account account) { accountDao.updateAccount(account); } public void transfer(String sourceName, String targetName, Float money) { System.out.println("Transfer in progress"); //1. Query transfer out account by name Account source = accountDao.findAccountByName(sourceName); //2. Query transfer in account by name Account target = accountDao.findAccountByName(targetName); //3. Decrease in transfer out account source.setMoney(source.getMoney() - money); //4. Add money to transfer in account target.setMoney(target.getMoney() + money); //5. Update transfer out account accountDao.updateAccount(source); //int i =1/0; //6. Update transfer in account accountDao.updateAccount(target); } }
New JdbcConfig configuration class
package config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DriverManagerDataSource; import javax.sql.DataSource; //Configuration classes related to connecting database @Configuration//Configure class labels @ComponentScan//Packages to be scanned when adding containers public class JdbcConfig { //The Value tag assigns a Value to the variable, and the EL expression in it uses ${} to reference the Value of the external file, which represents the Value in the reference property file. @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; //The Bean tag stores the returned value object in a container @Bean(name="jdbcTemplate") public JdbcTemplate createJdbcTemplate(DataSource dataSource){ return new JdbcTemplate(dataSource); } //The Bean tag stores the returned value object in a container @Bean(name="dataSource") public DataSource createDataSource(){ DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource(); driverManagerDataSource.setDriverClassName(driver); driverManagerDataSource.setUrl(url); driverManagerDataSource.setUsername(username); driverManagerDataSource.setPassword(password); return driverManagerDataSource; } }
New transaction configuration class TransactionManagerConfig
package config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; //Configure class labels @Configuration public class TransactionManagerConfig { //The Bean tag stores the returned value object in a container @Bean(name="transactionManager") public PlatformTransactionManager createTransactionManager(DataSource dataSource){ return new DataSourceTransactionManager(dataSource); } }
Create a new SpringConfiguration configuration class (equivalent to bean.xml )
package config; import org.springframework.context.annotation.*; import org.springframework.transaction.annotation.EnableTransactionManagement; //Configuration class of spring @Configuration//Represents a configuration class @ComponentScan("com")//Packages to scan when creating a new container @Import({JdbcConfig.class , TransactionManagerConfig.class})//This configuration class is the main configuration class. If you need to reference other configuration classes, you need to Import them with the Import tag. Property is the bytecode of the configuration class. @PropertySource("jdbcConfig.properties")//This label is the import property profile @EnableTransactionManagement//Turn on transaction control @EnableAspectJAutoProxy//Enable AOP as annotation mode public class SpringConfiguration { }
New test class
package com.test; import com.service.IAccountService; import config.SpringConfiguration; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; //Use Junit unit to test our configuration @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfiguration.class) public class AccountServiceTest { @Autowired private IAccountService as; @Test public void testTransfer(){ as.transfer("aaa" , "bbb" , 500f); } }
test result
Database raw data:
When no exception occurs:
Transfer function is normal
When an exception occurs (open the annotation int i = 1/0 in the transfer method):