XML implementation of declarative transactions
Development steps
Step 1: Introduce AOP-related aspectj
dependencies
<!--aspectj dependency--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>6.0.0-M2</version> </dependency>
Step 2: Write the dao layer interface and its implementation class
public interface AccountDao {<!-- --> // Check the balance based on the account number Account selectByActno(String actno); //Update account information int update(Account act); }
@Repository("accountDao") public class AccountDaoImpl implements AccountDao {<!-- --> @Resource(name = "jdbcTemplate") private JdbcTemplate jdbcTemplate; @Override public Account selectByActno(String actno) {<!-- --> String sql = "select actno, balance from t_act where actno = ?"; Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Account.class), actno); return account; } @Override public int update(Account act) {<!-- --> String sql = "update t_act set balance = ? where actno = ?"; int count = jdbcTemplate.update(sql, act.getBalance(), act.getActno()); return count; } }
Step 3: Write the Service interface and its implementation class. Here we no longer use the @Transactional annotation
but implement declarative transactions based on XML configuration
@Service("accountService") public class AccountServiceImpl implements AccountService {<!-- --> @Resource(name = "accountDao") private AccountDao accountDao; @Override public void transfer(String fromActno, String toActno, double money) {<!-- --> // Check whether the balance of the transfer-out account is sufficient Account fromAct = accountDao.selectByActno(fromActno); if (fromAct.getBalance() < money) {<!-- --> throw new RuntimeException("Insufficient balance!!!"); } // The balance is sufficient Account toAct = accountDao.selectByActno(toActno); // Modify the balance of the two objects in the memory first fromAct.setBalance(fromAct.getBalance() - money); toAct.setBalance(toAct.getBalance() + money); //Database update int count = accountDao.update(fromAct); // Simulate exception String s = null; s.toString(); count + = accountDao.update(toAct); if(count != 2) {<!-- --> throw new RuntimeException("Transfer failed, contact the bank!"); } } }
Step 4: Add the context, aop and tx
namespaces and their constraint files in the spring configuration file
, and then configure the data source and JdbcTemplate ,transaction manager,notification,aspect
Tag | Description |
---|---|
tx:advice | Customize a transaction notification |
tx:method | Configure the method of transaction notification and the related attributes of the transaction |
advice-ref | Introducing custom notifications |
<?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--Component scanning--> <context:component-scan base-package="com.powernode.bank"/> <!--Configuring data source--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/spring6"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <!--Configuring JdbcTemplate--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <!--Configuration transaction manager--> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--Associate the configured transaction manager, and then configure transaction-related notifications. This enhanced transaction-related code has been encapsulated by Spring--> <tx:advice id="txAdvice" transaction-manager="txManager"> <!--Configure which business methods the transaction notification controls, and set the relevant transaction attributes--> <tx:attributes> <!--Exact match--> <tx:method name="transfer" propagation="REQUIRED" rollback-for="java.lang.Throwable"/> <!--Fuzzy matching--> <tx:method name="save*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/> <tx:method name="delete*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/> <tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/> <tx:method name="modify*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/> <tx:method name="query*" read-only="true"/> <tx:method name="select*" read-only="true"/> <tx:method name="find*" read-only="true"/> <tx:method name="get*" read-only="true"/> </tx:attributes> </tx:advice> <!--Configuration aspects--> <aop:config> <!--General pointcut expression, here represents all methods in AccountServiceImpl--> <aop:pointcut id="txPointcut" expression="execution(* com.powernode.bank.service..*(..))"/> <!--Aspect = notification + pointcut. Although the pointcut here is all the methods of the business class, the txAdvice notification we configured ourselves has specified the specific business method--> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/> </aop:config> </beans>
Step 5: Test whether the configured transaction notification
can play a transaction control role in the target method of the target object
@Test public void testTransferXml(){<!-- --> ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); AccountService accountService = applicationContext.getBean("accountService", AccountService.class); try {<!-- --> accountService.transfer("act-001", "act-002", 10000); System.out.println("Transfer successful"); } catch (Exception e) {<!-- --> e.printStackTrace(); } }