spring aop实现事务控制
首先导入依赖
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.itheima</groupId> <artifactId>day04_eesy_02account</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency> </dependencies> </project>
定义一个Account类,对应数据库中的account表
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.itheima.domain; import java.io.Serializable; /** * 账户的实体类 */ public class Account implements Serializable { private Integer id; private String name; private Float money; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Float getMoney() { return money; } public void setMoney(Float money) { this.money = money; } @Override public String toString() { return "Account{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + '}'; } }
定义工具类ConnectionUtils,getThreadConnection()方法用于获得一个当前线程的数据库连接
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.itheima.utils; import javax.sql.DataSource; import java.sql.Connection; /** * 连接的工具类,它用于从数据源中获取一个连接,并且实现和线程的绑定 */ public class ConnectionUtils { private ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); private DataSource dataSource; public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } /** * 获取当前线程上的连接 * @return */ public Connection getThreadConnection() { try{ //1.先从ThreadLocal上获取 Connection conn = tl.get(); //2.判断当前线程上是否有连接 if (conn == null) { //3.从数据源中获取一个连接,并且存入ThreadLocal中 conn = dataSource.getConnection(); tl.set(conn); } //4.返回当前线程上的连接 return conn; }catch (Exception e){ throw new RuntimeException(e); } } /** * 把连接和线程解绑 */ public void removeConnection(){ tl.remove(); } }
利用ConnectionUtils实现持久层的CURD操作
package com.itheima.dao.impl; import com.itheima.dao.IAccountDao; import com.itheima.domain.Account; import com.itheima.utils.ConnectionUtils; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import org.apache.commons.dbutils.handlers.BeanListHandler; import java.util.List; /** * 账户的持久层实现类 */ public class AccountDaoImpl implements IAccountDao { private QueryRunner runner; private ConnectionUtils connectionUtils; public void setRunner(QueryRunner runner) { this.runner = runner; } public void setConnectionUtils(ConnectionUtils connectionUtils) { this.connectionUtils = connectionUtils; } @Override public List<Account> findAllAccount() { try{ return runner.query(connectionUtils.getThreadConnection(),"select * from account",new BeanListHandler<Account>(Account.class)); }catch (Exception e) { throw new RuntimeException(e); } } @Override public Account findAccountById(Integer accountId) { try{ return runner.query(connectionUtils.getThreadConnection(),"select * from account where id = ? ",new BeanHandler<Account>(Account.class),accountId); }catch (Exception e) { throw new RuntimeException(e); } } @Override public void saveAccount(Account account) { try{ runner.update(connectionUtils.getThreadConnection(),"insert into account(name,money)values(?,?)",account.getName(),account.getMoney()); }catch (Exception e) { throw new RuntimeException(e); } } @Override public void updateAccount(Account account) { try{ runner.update(connectionUtils.getThreadConnection(),"update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId()); }catch (Exception e) { throw new RuntimeException(e); } } @Override public void deleteAccount(Integer accountId) { try{ runner.update(connectionUtils.getThreadConnection(),"delete from account where id=?",accountId); }catch (Exception e) { throw new RuntimeException(e); } } @Override public Account findAccountByName(String accountName) { try{ List<Account> accounts = runner.query(connectionUtils.getThreadConnection(),"select * from account where name = ? ",new BeanListHandler<Account>(Account.class),accountName); if(accounts == null || accounts.size() == 0){ return null; } if(accounts.size() > 1){ throw new RuntimeException("结果集不唯一,数据有问题"); } return accounts.get(0); }catch (Exception e) { throw new RuntimeException(e); } } }
定义工具类TransactionManager,用于进行事务的开启,提交,回滚,释放连接等操作
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.itheima.utils; /** * 和事务管理相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放连接 */ public class TransactionManager { private ConnectionUtils connectionUtils; public void setConnectionUtils(ConnectionUtils connectionUtils) { this.connectionUtils = connectionUtils; } /** * 开启事务 */ public void beginTransaction(){ try { connectionUtils.getThreadConnection().setAutoCommit(false); }catch (Exception e){ e.printStackTrace(); } } /** * 提交事务 */ public void commit(){ try { connectionUtils.getThreadConnection().commit(); }catch (Exception e){ e.printStackTrace(); } } /** * 回滚事务 */ public void rollback(){ try { connectionUtils.getThreadConnection().rollback(); }catch (Exception e){ e.printStackTrace(); } } /** * 释放连接 */ public void release(){ try { connectionUtils.getThreadConnection().close();//还回连接池中 connectionUtils.removeConnection(); }catch (Exception e){ e.printStackTrace(); } } }
实现业务层的代码
package com.itheima.service.impl; import com.itheima.dao.IAccountDao; import com.itheima.domain.Account; import com.itheima.service.IAccountService; import java.util.List; /** * 账户的业务层实现类 * * 事务控制应该都是在业务层 */ public class AccountServiceImpl implements IAccountService{ private IAccountDao accountDao; public void setAccountDao(IAccountDao accountDao) { this.accountDao = accountDao; } @Override public List<Account> findAllAccount() { return accountDao.findAllAccount(); } @Override public Account findAccountById(Integer accountId) { return accountDao.findAccountById(accountId); } @Override public void saveAccount(Account account) { accountDao.saveAccount(account); } @Override public void updateAccount(Account account) { accountDao.updateAccount(account); } @Override public void deleteAccount(Integer acccountId) { accountDao.deleteAccount(acccountId); } @Override public void transfer(String sourceName, String targetName, Float money) { System.out.println("transfer...."); //2.1根据名称查询转出账户 Account source = accountDao.findAccountByName(sourceName); //2.2根据名称查询转入账户 Account target = accountDao.findAccountByName(targetName); //2.3转出账户减钱 source.setMoney(source.getMoney()-money); //2.4转入账户加钱 target.setMoney(target.getMoney()+money); //2.5更新转出账户 accountDao.updateAccount(source); // int i=1/0; //2.6更新转入账户 accountDao.updateAccount(target); } }
bean.xml进行ioc配置,对各个对象进行注入
注入逻辑
dataSource->connectionUtils->transactionManager:工具类的注入关系
QueryRunner,connectionUtils->accountDao->accountService:实现类的注入关系
<!-- 配置Service --> <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"> <!-- 注入dao --> <property name="accountDao" ref="accountDao"></property> </bean> <!--配置Dao对象--> <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl"> <!-- 注入QueryRunner --> <property name="runner" ref="runner"></property> <!-- 注入ConnectionUtils --> <property name="connectionUtils" ref="connectionUtils"></property> </bean> <!--配置QueryRunner--> <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"></bean> <!-- 配置数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <!--连接数据库的必备信息--> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy?useSSL=false&serverTimezone=GMT%2B8"></property> <property name="user" value="root"></property> <property name="password" value="nimahaoma49"></property> </bean> <!-- 配置Connection的工具类 ConnectionUtils --> <bean id="connectionUtils" class="com.itheima.utils.ConnectionUtils"> <!-- 注入数据源--> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置事务管理器--> <bean id="txManager" class="com.itheima.utils.TransactionManager"> <!-- 注入ConnectionUtils --> <property name="connectionUtils" ref="connectionUtils"></property> </bean>
配置aop,切入点:所有业务层的方法,通知类:TransactionManager,即给每个业务层的方法都增加一层事务控制
<!--配置aop--> <aop:config> <!--配置通用的切入点表达式--> <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"></aop:pointcut> <aop:aspect id="txAdvice" ref="txManager"> <!--配置前置通知:开启事务--> <aop:before method="beginTransaction" pointcut-ref="pt1"></aop:before> <!--配置后置通知:提交事务--> <aop:after-returning method="commit" pointcut-ref="pt1"></aop:after-returning> <!--配置异常通知:回滚事务--> <aop:after-throwing method="rollback" pointcut-ref="pt1"></aop:after-throwing> <!--配置最终通知:释放链接--> <aop:after method="release" pointcut-ref="pt1"></aop:after> </aop:aspect> </aop:config>