SpringAopTransation面向切口提交事务
分析过程
- 目的:在面向数据库处理sql时:银行转账 如果在转账过程中发生错误正在处理的事务是不能提交的(前转了但是没有收到),避免这种问题发生,采用事务方式处理,如果发生错误就回滚,如果正常就提交;
- 服务层正常写业务,AOP通知中切入事务方式来处理服务层,由代理对象对服务层代码进行增强,实现提交或回滚
- 需要用到的技术
- spring-context IOC容器
- spring-test 测试
- c3p0 数据库连接池
- comment-dbutils 数据库管理工具
- mysql 数据库
- junit 测试
- aspectjweaver AOP切面编程
具体实现过程
-
创建maven项目,导入依赖
<?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>cn.kgc</groupId> <artifactId>springAopTransaction</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <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>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>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.36</version> </dependency> <!--增删改查数据库的工具--> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.6</version> </dependency> <!--spring面向切面使用的包--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency> </dependencies> </project>
-
创建实力类
-
dao层及daoImpl
package cn.kgc.dao; import cn.kgc.pojo.Account; public interface AccountDao { Account getByName(String name); void update(Account account); }
package cn.kgc.dao.impl; import cn.kgc.dao.AccountDao; import cn.kgc.pojo.Account; import cn.kgc.utils.ConnectionUtils; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import java.sql.SQLException; @Repository public class AccountDaoImpl implements AccountDao { @Autowired QueryRunner queryRunner;//这个对象中没有配 数据源 数据源由connectionUtils提供 @Autowired ConnectionUtils connectionUtils; @Override public Account getByName(String name) { System.out.println("查找账户"); String sql = "select * from account where name = ?"; try { //因为queryRunner没有数据源 所以调用query方法时参数需要有一个连接 return queryRunner.query(connectionUtils.getThreadConnection(), sql, new BeanHandler<>(Account.class), name); } catch (SQLException e) { e.printStackTrace(); return null; } } @Override public void updata(Account account) { System.out.println("上转保存业务"); String sql = "update account set money = ? where name = ?"; try { queryRunner.update(connectionUtils.getThreadConnection(), sql, account.getMoney(), account.getName()); } catch (SQLException e) { e.printStackTrace(); } } }
-
配置文件applicationConfig.xml
-
引入注解 (spring-context)
-
配置bean QueryRunner (dbutils)
-
配置数据源dataSource (c3p0)
-
配置spring AOP(aspectjweaver)
-
<?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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="cn.kgc"/> <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner"></bean> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}"/> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="user" value="${jdbc.user}"/> <property name="password" value="${jdbc.password}"/> </bean> <context:property-placeholder location="jdbc.properties"/> <!--配置切面--> <aop:config> <aop:aspect ref="transationManager"> <aop:pointcut id="pointcut" expression="execution(* cn.kgc.service.*.*(..))"/> <aop:before method="beginTranscation" pointcut-ref="pointcut"/> <aop:after-returning method="commit" pointcut-ref="pointcut"/> <aop:after-throwing method="rollback" pointcut-ref="pointcut"/> <aop:after method="relase" pointcut-ref="pointcut"/> </aop:aspect> </aop:config> </beans>
-
-
编写Serverice层
package cn.kgc.service; public interface AccountService { public void transaction(String fromName, String toName, double money); }
package cn.kgc.service.impl; import cn.kgc.dao.AccountDao; import cn.kgc.pojo.Account; import cn.kgc.service.AccountService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class AccountServiceImpl implements AccountService { @Autowired AccountDao accountDao; @Override public void transation(String fromName, String toName, double money) { System.out.println("转账进行中"); Account fromAccount = accountDao.getByName(fromName); Account toAccount = accountDao.getByName(toName); fromAccount.setMoney(fromAccount.getMoney() - money); toAccount.setMoney(toAccount.getMoney() + money); accountDao.updata(fromAccount); accountDao.updata(toAccount); } }
-
连接数据源获得连接的工具类ConnectionUntils
package cn.kgc.utils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; @Component public class ConnectionUtils { private ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); @Autowired private DataSource dataSource; //获得连接(当前线程绑定的连接) public Connection getThreadConnection() { Connection connection = tl.get(); if (connection == null) { try { connection = dataSource.getConnection(); tl.set(connection); } catch (SQLException e) { e.printStackTrace(); } } return tl.get(); } //把线程和连接解绑 public void remove() { tl.remove(); } }
-
AOP切入的通知类
package cn.kgc.utils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.sql.SQLException; @Component public class TransactionManager { @Autowired ConnectionUtils connectionUtils; //开启事务获得连接 public void beginTransaction(){ System.out.println("开启事务获得连接"); try { connectionUtils.getThreadConnection().setAutoCommit(false); } catch (SQLException e) { e.printStackTrace(); } } //提交事务保存结果 public void commit(){ System.out.println("提交事务"); try { connectionUtils.getThreadConnection().commit(); } catch (SQLException e) { e.printStackTrace(); } } //回滚事务 public void rollback(){ System.out.println("发生错误回滚"); try { connectionUtils.getThreadConnection().rollback(); } catch (SQLException e) { e.printStackTrace(); } } //释放连接 public void release(){ System.out.println("最终释放连接"); try { //还原自动提交 connectionUtils.getThreadConnection().setAutoCommit(true); //归还连接 connectionUtils.getThreadConnection().close(); //解绑线程 connectionUtils.remove(); } catch (SQLException e) { e.printStackTrace(); } } }
-
测试
package cn.kgc; import cn.kgc.service.AccountService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class TestSpringTransation { @Autowired AccountService accountService; @Test public void testTransation(){ accountService.transation("吕布", "马超", 500); } }
-