以AOP的方式实现对事务的控制
相关坐标的导入pom.xml
<?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>org.example</groupId>
<artifactId>day02_XmlIocCase</artifactId>
<version>1.0-SNAPSHOT</version>
<name>day02_XmlIocCase</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.7</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>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!--添加配置跳过测试-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<!--添加配置跳过测试-->
</plugins>
</pluginManagement>
</build>
</project>
1.用户实体类
/*
* 用户实体类
* */
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 +
'}';
}
}
2.持久层接口
/*
* 账户的持久层接口
* */
public interface IAccountDao {
// 查询所有
List<Account> findAll();
// 根据id查询
Account findById(Integer accountId);
// 插入数据
void insert(Account account);
// 更新数据
void update(Account account);
// 根据Id删除数据
void delete(Integer accountId);
// 根据姓名查找
Account findByName(String name);
}
3.持久层实现类
/*
* 永久层实现类
* */
public class AccountDaoImpl implements IAccountDao {
private QueryRunner queryRunner;
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
public void setQueryRunner(QueryRunner queryRunner) {
this.queryRunner = queryRunner;
}
@Override
public List<Account> findAll() {
try{
return queryRunner.query(connectionUtils.getThreadConnection(),"select *from account", new BeanListHandler<Account>(Account.class));
}catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public Account findById(Integer accountId) {
try{
return queryRunner.query(connectionUtils.getThreadConnection(),"select *from account where id = ?", new BeanHandler<Account>(Account.class), accountId);
}catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void insert(Account account) {
try{
queryRunner.update(connectionUtils.getThreadConnection(),"insert into account(name,money)values (?,?)", account.getName(),account.getMoney());
}catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void update(Account account) {
try{
queryRunner.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 delete(Integer accountId) {
try{
queryRunner.update(connectionUtils.getThreadConnection(),"delete from account where id = ?", accountId);
}catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public Account findByName(String name) {
try{
List<Account> accounts = queryRunner.query(connectionUtils.getThreadConnection(),"select *from account where name = ?", new BeanListHandler<Account>(Account.class), name);
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);
}
}
}
4.连接工具类
/**
* 连接的工具类,它用于从数据源中获取一个连接,并且实现和线程的绑定
*/
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();
}
}
5.事务控制工具类
/**
* 和事务管理相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放连接
*/
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();
}
}
}
6.业务层接口
/*
* 业务层接口
* */
public interface AccountService {
// 查询所有
List<Account> findAll();
// 根据id查询
Account findById(Integer accountId);
// 插入数据
void insert(Account account);
// 更新数据
void update(Account account);
// 根据Id删除数据
void delete(Integer accountId);
// 转账操作
void transAccount(String sendName, String targetName, Float money);
}
7.业务层实现类
/*
* 业务层接口的实现类
* */
public class IAccountServiceImpl implements AccountService {
private IAccountDao accountDao;
// 通过set方法来让spring自动注入accountDao
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public List<Account> findAll() {
return accountDao.findAll();
}
@Override
public Account findById(Integer accountId) {
return accountDao.findById(accountId);
}
@Override
public void insert(Account account) {
accountDao.insert(account);
}
@Override
public void update(Account account) {
accountDao.update(account);
}
@Override
public void delete(Integer accountId) {
accountDao.delete(accountId);
}
@Override
public void transAccount(String sendName, String targetName, Float money) {
System.out.println("11111");
// 1.查询转账人信息
Account account1 = accountDao.findByName(sendName);
// 2.查询接受人信息
Account account2 = accountDao.findByName(targetName);
// 3.转账人减去对应金额
float money1 = account1.getMoney();
account1.setMoney(money1 - money);
// int a = 1/0;
// 4.接受人加上对应的金额
float money2 = account2.getMoney();
account2.setMoney(money2 + money);
// 5.刷新转账人和接受人信息
accountDao.update(account1);
accountDao.update(account2);
System.out.println("转账完毕");
}
}
8.配置bean.xml
<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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 配置service -->
<bean id="accountService" class="org.example.service.impl.IAccountServiceImpl">
<!-- 注入dao -->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置dao对象 -->
<bean id="accountDao" class="org.example.dao.impl.AccountDaoImpl">
<!-- 注入QueryRunner -->
<property name="queryRunner" ref="queryRunner"></property>
<property name="connectionUtils" ref="connectionUtils"></property>
</bean>
<!-- 配置queryRunner -->
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<!-- 注入数据源 -->
<!-- <constructor-arg name="ds" ref="dataSource"></constructor-arg>-->
</bean>
<!-- 配置dataSource -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 连接数据库 -->
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://121.41.229.122:3306/spring"></property>
<property name="user" value="root"></property>
<property name="password" value="123456"></property>
</bean>
<!-- 配置Connection的工具类 ConnectionUtils -->
<bean id="connectionUtils" class="org.example.utils.ConnectionUtils">
<!-- 注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务管理器-->
<bean id="txManager" class="org.example.utils.TransactionManager">
<!-- 注入ConnectionUtils -->
<property name="connectionUtils" ref="connectionUtils"></property>
</bean>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切面 -->
<aop:aspect id="manager" ref="txManager">
<!-- 配置切入点信息 -->
<aop:pointcut id="pt" expression="execution(* org.example.service.*.*(..))"/>
<!-- 前置通知,开启事务 -->
<aop:before method="beginTransaction" pointcut-ref="pt"></aop:before>
<!-- 配置后置通知,提交事务 -->
<aop:after-returning method="commit" pointcut-ref="pt"></aop:after-returning>
<!-- 配置异常通知,回滚事务-->
<aop:after-throwing method="rollback" pointcut-ref="pt"></aop:after-throwing>
<!-- 配置最终通知,释放连接 -->
<aop:after method="release" pointcut-ref="pt"></aop:after>
</aop:aspect>
</aop:config>
</beans>
9.测试
/**
* Unit test for simple App.
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
"classpath:bean.xml"})
public class AppTest
{
/**
* Rigorous Test :-)
*/
@Test
public void shouldAnswerWithTrue()
{
assertTrue( true );
}
@Autowired
@Qualifier("accountService")
private AccountService as;
@Test
public void aopControl() {
//3、使用
as.transAccount("aaa","bbb",100f);
}
}
最终转账成功,如果中间发生错误,事务回滚,不提交。