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>