引入
转账案例:不加事务管理,出现异常时,转账错误
/**
* 账户的业务层实现类
*/
public class AccountServiceImpl implements IAccountService{
@Autowired
private IAccountDao accountDao;
@Autowired
private TransactionManager txManager;
public void setTxManager(TransactionManager txManager) {
this.txManager = txManager;
}
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 Account findAccountByName(String accountName) {
return accountDao.findAccountByName(accountName);
}
@Override
public void transfer(String sourceName, String targetName, Float money) {
//根据名称查询转出账户
Account source = accountDao.findAccountByName(sourceName);
//根据名称查询转入账户
Account target = accountDao.findAccountByName(targetName);
//转出账户减钱
source.setMoney(source.getMoney()-money);
//转入账户加钱
target.setMoney(target.getMoney()+money);
//更新转出账户
accountDao.updateAccount(source);
//模拟异常
int i = 1/0;
//更新转入账户
accountDao.updateAccount(target);
}
}
加事务管理,代码过于冗余
/**
* 账户的业务层实现类
*
* 事务控制应该都是在业务层
*/
public class AccountServiceImpl_OLD implements IAccountService{
private IAccountDao accountDao;
private TransactionManager txManager;
public void setTxManager(TransactionManager txManager) {
this.txManager = txManager;
}
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public List<Account> findAllAccount() {
try {
//1.开启事务
txManager.beginTransaction();
//2.执行操作
List<Account> accounts = accountDao.findAllAccount();
//3.提交事务
txManager.commit();
//4.返回结果
return accounts;
}catch (Exception e){
//5.回滚操作
txManager.rollback();
throw new RuntimeException(e);
}finally {
//6.释放连接
txManager.release();
}
}
@Override
public Account findAccountById(Integer accountId) {
try {
//1.开启事务
txManager.beginTransaction();
//2.执行操作
Account account = accountDao.findAccountById(accountId);
//3.提交事务
txManager.commit();
//4.返回结果
return account;
}catch (Exception e){
//5.回滚操作
txManager.rollback();
throw new RuntimeException(e);
}finally {
//6.释放连接
txManager.release();
}
}
@Override
public void saveAccount(Account account) {
try {
//1.开启事务
txManager.beginTransaction();
//2.执行操作
accountDao.saveAccount(account);
//3.提交事务
txManager.commit();
}catch (Exception e){
//4.回滚操作
txManager.rollback();
}finally {
//5.释放连接
txManager.release();
}
}
@Override
public void updateAccount(Account account) {
try {
//1.开启事务
txManager.beginTransaction();
//2.执行操作
accountDao.updateAccount(account);
//3.提交事务
txManager.commit();
}catch (Exception e){
//4.回滚操作
txManager.rollback();
}finally {
//5.释放连接
txManager.release();
}
}
@Override
public void deleteAccount(Integer acccountId) {
try {
//1.开启事务
txManager.beginTransaction();
//2.执行操作
accountDao.deleteAccount(acccountId);
//3.提交事务
txManager.commit();
}catch (Exception e){
//4.回滚操作
txManager.rollback();
}finally {
//5.释放连接
txManager.release();
}
}
@Override
public Account findAccountByName(String accountName) {
try {
//1.开启事务
txManager.beginTransaction();
//2.执行操作
Account account = accountDao.findAccountByName(accountName);
//3.提交事务
txManager.commit();
//4.返回结果
return account;
}catch (Exception e){
//5.回滚操作
txManager.rollback();
throw new RuntimeException(e);
}finally {
//6.释放连接
txManager.release();
}
}
@Override
public void transfer(String sourceName, String targetName, Float money) {
try {
//1.开启事务
txManager.beginTransaction();
//2.执行操作
//根据名称查询转出账户
Account source = accountDao.findAccountByName(sourceName);
//根据名称查询转入账户
Account target = accountDao.findAccountByName(targetName);
//转出账户减钱
source.setMoney(source.getMoney()-money);
//转入账户加钱
target.setMoney(target.getMoney()+money);
//更新转出账户
accountDao.updateAccount(source);
//异常
int i = 1/0;
//更新转入账户
accountDao.updateAccount(target);
//3.提交事务
txManager.commit();
}catch (Exception e){
//4.回滚操作
txManager.rollback();
}finally {
//5.释放连接
txManager.release();
}
}
}
事务管理工具类
/**
* 和事务管理相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放连接
*/
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 rollback(){
try {
connectionUtils.getThreadConnection().rollback();
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 提交事务
*/
// 后置通知
public void commit(){
try {
connectionUtils.getThreadConnection().commit();//还回连接池中
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 释放连接
*/
// 最终通知
public void release(){
try {
connectionUtils.getThreadConnection().close();
// 从当前线程解绑连接对象
connectionUtils.removeConnection();
}catch (Exception e){
e.printStackTrace();
}
}
}
基于接口的动态代理回顾
/**
* 模拟一个消费者
*/
public class Client {
public static void main(String[] args) {
final Producer producer = new Producer();//被代理对象
/**
* 动态代理:
* 特点:字节码随用随创建,随用随加载
* 作用:不修改源码的基础上对方法增强
* 分类:
* 基于接口的动态代理
* 基于子类的动态代理
* 基于接口的动态代理:
* 涉及的类:Proxy
* 提供者:JDK官方
* 如何创建代理对象:
* 使用Proxy类中的newProxyInstance方法
* 创建代理对象的要求:
* 被代理类最少实现一个接口,如果没有则不能使用
* newProxyInstance方法的参数:
* ClassLoader:类加载器
* 它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
* Class[]:字节码数组
* 它是用于让代理对象和被代理对象有相同方法。固定写法。
* InvocationHandler:用于提供增强的代码
* 它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
* 此接口的实现类都是谁用谁写。
*/
IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
producer.getClass().getInterfaces(),
new InvocationHandler() {
/**
* 作用:执行被代理对象的任意接口方法
* 接口中的所有方法都会被其拦截并执行
* 方法参数的含义
* @param proxy 代理对象的引用
* @param method 当前执行的方法
* @param args 当前执行方法所需的参数
* @return 和被代理对象方法有相同的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//提供增强的代码
Object returnValue = null;
//1.获取方法执行的参数
Float money = (Float)args[0];
//2.判断当前方法是不是销售
if("saleProduct".equals(method.getName())) {
// money*0.8会将我们的数据从float类型转成double类型
returnValue = method.invoke(producer, money*0.8f);
}
return returnValue;
}
});
proxyProducer.saleProduct(10000f); //代理对象 执行增强的方法
}
}
/**
* 对生产厂家要求的接口
*/
public interface IProducer {
/**
* 销售
* @param money
*/
public void saleProduct(float money);
/**
* 售后
* @param money
*/
public void afterService(float money);
}
/**
* 一个生产者
*/
public class Producer implements IProducer{
/**
* 销售
* @param money
*/
public void saleProduct(float money){
System.out.println("销售产品,并拿到钱:"+money);
}
/**
* 售后
* @param money
*/
public void afterService(float money){
System.out.println("提供售后服务,并拿到钱:"+money);
}
}
使用动态代理实现事务控制
创建Service的代理对象的工厂
/**
* 用于创建Service的代理对象的工厂
*/
public class BeanFactory {
private IAccountService accountService;
private TransactionManager txManager;
public final void setTxManager(TransactionManager txManager) {
this.txManager = txManager;
}
public void setAccountService(IAccountService accountService) {
this.accountService = accountService;
}
/**
* 获取Service代理对象
* @return
*/
public IAccountService getAccountService() {
return (IAccountService)Proxy.newProxyInstance(accountService.getClass().getClassLoader(),
accountService.getClass().getInterfaces(),
new InvocationHandler() {
/**
* 添加事务的支持
*
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判断是否查询方法,如果是,则不需要事务支持,直接放行即可
if ("findAllAccount".equals(method.getName())) {
return method.invoke(accountService, args);
}
Object rtValue = null;
try {
//1.开启事务
txManager.beginTransaction();
//2.执行操作
rtValue = method.invoke(accountService, args);
//3.提交事务
txManager.commit();
//4.返回结果
return rtValue;
} catch (Exception e) {
//5.回滚操作
txManager.rollback();
throw new RuntimeException(e);
} finally {
//6.释放连接
txManager.release();
}
}
});
}
}
bean.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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--配置代理的service-->
<bean id="proxyAccountService" factory-bean="beanFactory" factory-method="getAccountService"></bean>
<!--配置beanfactory-->
<bean id="beanFactory" class="com.sz.factory.BeanFactory">
<!-- 注入service -->
<property name="accountService" ref="accountService"></property>
<!-- 注入事务管理器 -->
<property name="txManager" ref="txManager"></property>
</bean>
<!--配置Service-->
<bean id="accountService" class="com.sz.service.impl.AccountServiceImpl">
<!--注入Dao-->
<!--set方式注入 会先调用默认构造函数 如果仅仅自定义了有参构造函数将其覆盖 set注入则报错-->
<property name="accountDao" ref="accountDao"></property>
</bean>
<!--配置Dao对象-->
<bean id="accountDao" class="com.sz.dao.impl.AccountDaoImpl">
<!-- 注入QueryRunner -->
<property name="runner" ref="runner"></property>
<!-- 注入ConnectionUtils -->
<property name="connectionUtils" ref="connectionUtils"></property>
</bean>
<!-- 配置Connection的工具类 ConnectionUtils -->
<bean id="connectionUtils" class="com.sz.utils.ConnectionUtils">
<!-- 注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置QueryRunner-->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<!--<constructor-arg name="ds" ref="dataSource"></constructor-arg>-->
</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/spring_account"></property>
<property name="user" value="root"></property>
<property name="password" value="123"></property>
</bean>
<!-- 配置事务管理器-->
<bean id="txManager" class="com.sz.utils.TransactionManager">
<!-- 注入ConnectionUtils -->
<property name="connectionUtils" ref="connectionUtils"></property>
</bean>
</beans>
持久层实现类
/**
* 账户的业务层实现类
*/
//@Service("accountService")
public class AccountServiceImpl implements IAccountService{
@Autowired
private IAccountDao accountDao;
@Autowired
private TransactionManager txManager;
public void setTxManager(TransactionManager txManager) {
this.txManager = txManager;
}
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 Account findAccountByName(String accountName) {
return accountDao.findAccountByName(accountName);
}
@Override
public void transfer(String sourceName, String targetName, Float money) {
// try {
// //1.开启事务
// txManager.beginTransaction();
// //2.执行操作
// //根据名称查询转出账户
// Account source = accountDao.findAccountByName(sourceName);
// //根据名称查询转入账户
// Account target = accountDao.findAccountByName(targetName);
// //转出账户减钱
// source.setMoney(source.getMoney()-money);
// //转入账户加钱
// target.setMoney(target.getMoney()+money);
// //更新转出账户
// accountDao.updateAccount(source);
// //异常
// //int i = 1/0;
// //更新转入账户
// accountDao.updateAccount(target);
// //3.提交事务
// txManager.commit();
// }catch (Exception e){
// //4.回滚操作
// txManager.rollback();
// }finally {
// //5.释放连接
// txManager.release();
// }
//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.2更新转出账户
accountDao.updateAccount(source);
//模拟异常
//int i = 1/0;
//2.5更新转入账户
accountDao.updateAccount(target);
}
}