声明式事务之XML实现方式
开发步骤
第一步: 引入AOP相关的aspectj
依赖
<!--aspectj依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.0-M2</version>
</dependency>
第二步: 编写dao层接口及其实现类
public interface AccountDao {
// 根据账号查询余额
Account selectByActno(String actno);
// 更新账户信息
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;
}
}
第三步: 编写Service接口及其实现类,这里就不再使用@Transactional注解
而是基于XML配置的方式实现声明式事务
@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Resource(name = "accountDao")
private AccountDao accountDao;
@Override
public void transfer(String fromActno, String toActno, double money) {
// 查询转出账户的余额是否充足
Account fromAct = accountDao.selectByActno(fromActno);
if (fromAct.getBalance() < money) {
throw new RuntimeException("余额不足!!!");
}
// 余额充足
Account toAct = accountDao.selectByActno(toActno);
// 将内存中两个对象的余额先修改
fromAct.setBalance(fromAct.getBalance() - money);
toAct.setBalance(toAct.getBalance() + money);
// 数据库更新
int count = accountDao.update(fromAct);
// 模拟异常
String s = null;
s.toString();
count += accountDao.update(toAct);
if(count != 2) {
throw new RuntimeException("转账失败,联系银行!");
}
}
}
第四步: 在spring的配置文件
中添加context,aop和tx
的命名空间及其约束文件,然后配置数据源,JdbcTemplate,事务管理器,通知,切面
标签 | 描述 |
---|---|
tx:advice | 自定义一个事务通知 |
tx:method | 配置事务通知作用的方法和事务的相关属性 |
advice-ref | 引入自定义的通知 |
<?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">
<!--组件扫描-->
<context:component-scan base-package="com.powernode.bank"/>
<!--配置数据源-->
<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>
<!--配置JdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务管理器-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--关联配置的事务管理器,然后配置事务相关的通知,这段控制事务相关的增强代码Spring已经封装好了-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!--配置事务通知对哪些业务方法进行事务控制,同时设置相关的事务属性-->
<tx:attributes>
<!--精确匹配-->
<tx:method name="transfer" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<!--模糊匹配-->
<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>
<!--配置切面-->
<aop:config>
<!--通用的切点表达式,这里表示AccountServiceImpl中的所有方法-->
<aop:pointcut id="txPointcut" expression="execution(* com.powernode.bank.service..*(..))"/>
<!--切面=通知+切点,这里切点虽然是业务类所有的方法,但我们自己配置的txAdvice通知已经指定了具体作用的业务方法-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
</beans>
第五步: 测试配置的事务通知
能否对目标对象的目标方法起到事务控制的作用
@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("转账成功");
} catch (Exception e) {
e.printStackTrace();
}
}