通过AOP控制事务的案例

1.xml配置版

执行以下sql语句

create table account(
    id int primary key auto_increment,
    name varchar(40),
    money float
)character set utf8 collate utf8_general_ci;
​
insert into account(name,money) values('aaa',1000);
insert into account(name,money) values('bbb',1000);

创建对应的实体类

Account.java

public class Account {
    private int id;
    private String name;
    private double money;
  }

AccountDao.java

public interface AccountDao {
  
      void saveAccount(Account account);
​
      Account queryByName(String sourceName);
  
      void update(Account account);
}

AccountDaoImpl.java

@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
​
    @Autowired
    private QueryRunner queryRunner;
​
    @Autowired
    private ConnectionUtils connectionUtils;
​
    @Override
    public void saveAccount(Account account) {
        String sql = "insert into account(name,money) values(?,?) ";
        try {
            queryRunner.update(connectionUtils.getConnection(),sql, account.getName(), account.getMoney());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
  
      @Override
    public Account queryByName(String sourceName) {
        String sql = "select * from account where name=? ";
        List<Account> accounts = null;
        try {
            accounts = queryRunner.query(connectionUtils.getConnection(),sql, new BeanListHandler<Account>(Account.class),sourceName);
            if (accounts.isEmpty()) {
                return null;
            }
            if (accounts.size() > 1) {
                throw new RuntimeException("结果集不唯一");
            }
            return accounts.get(0);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
  
      @Override
    public void update(Account account) {
        String sql = "update account set name=?,money=? where id=? ";
        try {
            queryRunner.update(connectionUtils.getConnection(),sql, account.getName(), account.getMoney(), account.getId());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
  }

AccountService.java

public interface AccountService {
​
    /**
     * 转账
     * @param sourceName        转出账户名称
     * @param targetName        转入账户名称
     * @param money             转账金额
     */
    void transfer(String sourceName,String targetName,Float money);
}
AccountServiceImpl.java

@Service("accountService")
public class AccountServiceImpl implements AccountService {
​
    @Autowired
    private AccountDao accountDao;
​
    @Autowired
    private TransactionManager transactionManager;
​
    @Override
    public void transfer(String sourceName, String targetName, Float money) {
            //查询出原金额
            Account source = accountDao.queryByName(sourceName);
            Account target = accountDao.queryByName(targetName);
            //更新金额
            source.setMoney(source.getMoney() - money);
            target.setMoney(target.getMoney() + money);
            //提交数据
            accountDao.update(source);
            //模拟异常
            int a=1/0;
            accountDao.update(target);
    }
}

ConnectionUtils.java

/**
 * 连接的工具类,用于绑定连接到ThreadLocal上
 */
@Component
public class ConnectionUtils {
​
    private ThreadLocal<Connection> tl = new ThreadLocal<>();
​
    @Autowired
    private DataSource dataSource;
​
​
    /**
     * 从当前线程获取连接
     * @return
     */
    public Connection getConnection(){
       try{
           //1.获取当前线程的连接
           Connection conn = tl.get();
           //2.判断当前线程上有没有
           if(conn == null){
               //3.从数据源中获取一个
               conn = dataSource.getConnection();
               //4.绑定到当前线程上
               tl.set(conn);
           }
           //5.返回线程上的连接
           return tl.get();
       }catch (Exception e){
           throw new RuntimeException(e);
       }
    }
​
    /**
     * 线程和连接的解绑
     */
    public void remove(){
        tl.remove();
    }
}

TransactionManager.java

/**
 * 事务管理器
 */
@Component
public class TransactionManager {
​
    @Autowired
    private ConnectionUtils connectionUtils;
​
​
    //开启事务
    public void beginTransaction(){
        try {
            connectionUtils.getConnection().setAutoCommit(false);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
​
    //提交事务
    public void commit(){
        try {
            connectionUtils.getConnection().commit();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
​
    //回滚事务
    public void rollback(){
        try {
            connectionUtils.getConnection().rollback();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
​
​
    //释放资源
    public void close(){
        try {
            connectionUtils.getConnection().close();
            connectionUtils.remove();//解绑
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

beans.xml

<?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.itheima"></context:component-scan>
​
    <!-- 引入数据库的配置文件 -->
    <context:property-placeholder location="druid.properties"/>
    <!-- 数据库连接池配置 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${driverClassName}"></property>
        <property name="url" value="${url}"></property>
        <property name="username" value="${username}"></property>
        <property name="password" value="${password}"></property>
    </bean>
​
    <!-- 配置QueryRunner-->
    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
    </bean>
​
    <bean id="logger" class="com.itheima.utils.TransactionManager"></bean>
​
​
    <aop:config>
        <aop:pointcut expression="execution(* com.itheima.service.impl.*.*(..))" id="pt1"/>
​
        <aop:aspect id="logAdvice" ref="logger">
            <aop:before method="beginTransaction" pointcut-ref="pt1"/>
            <aop:after-throwing method="rollback" pointcut-ref="pt1"/>
            <aop:after-returning method="commit" pointcut-ref="pt1"/>
            <aop:after method="close" pointcut-ref="pt1"/>
        </aop:aspect>
    </aop:config>
​
</beans>

druid.properties

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/spring_day01
username=root
password=123456

accountTest.java

public class AccountTest {
    AccountService accountService=null;
    @Before
    public void beforeMethod(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("bean.xml");
        accountService = (AccountService) applicationContext.getBean("accountService");
    }
​
    @Test
    public void testTransfer(){
        accountService.transfer("aaa","bbb",100f);
    }
}

2.注解版

TransactionManager.java

/**
 * 事务管理器
 */
@Component
@Aspect  //表示当前类是一个切面类
public class TransactionManager {
​
    @Autowired
    private ConnectionUtils connectionUtils;
​
    @Pointcut("execution(* com.itheima.service.impl.*.*(..))")
    private void pointcutExcution() {
    }
​
    /**
     * 环绕通知
     * @param joinPoint
     * @return
     */
    @Around("pointcutExcution()")
    public Object aroundMethod(ProceedingJoinPoint joinPoint) {
        Object result = null;
        try {
            beginTransaction();
            Object[] args = joinPoint.getArgs();
            result = joinPoint.proceed(args);
            commit();
        } catch (Throwable e) {
            e.printStackTrace();
            rollback();
        } finally {
            close();
        }
        return result;
    }
​
    //开启事务
    public void beginTransaction() {
        try {
            connectionUtils.getConnection().setAutoCommit(false);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
​
    //提交事务
    public void commit() {
        try {
            connectionUtils.getConnection().commit();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
​
    //回滚事务
    public void rollback() {
        try {
            connectionUtils.getConnection().rollback();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
​
    //释放资源
    public void close() {
        try {
            connectionUtils.getConnection().close();
            connectionUtils.remove();//解绑
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

beans.xml

<!-- 开启Spring对注解aop的支持--> 
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值