动态代理实现转账事务控制

动态代理实现转账事务控制

public interface IAccountService {
   void update(Account account) throws SQLException;
   // 添加一个转账方法
   void transfer(String sourceName, String targetName, Float money) throws SQLException;
}
public class AccountServiceImpl implements IAccountService {
   IAccountDao dao; // 无依赖
   /*如果不使用动态代理就放开这些注释*/
   //TransactionManager txManger;

   //public void setTransactionManager(TransactionManager txManger) {
   //    this.txManger = txManger;
   // }
   // set 注入
   public void setDao(IAccountDao dao) {
       this.dao = dao;
   }
   @Override
   public void insert(Account account) throws SQLException {
       dao.insert(account);
   }
   @Override
   public void update(Account account) throws SQLException {
       dao.update(account);
   }
   // 转账
   @Override
   public void transfer(String sourceName, String targetName, Float money) throws SQLException {
       // try {
       //开启事务
       //  txManger.begin();

       Account accountByName1 = dao.findAccountByName(sourceName);
       Account accountByName2 = dao.findAccountByName(targetName);
       accountByName1.setMoney(accountByName1.getMoney() - money);
       accountByName2.setMoney(accountByName2.getMoney() + money);
       dao.update(accountByName1);
       int i = 1 / 0;
       dao.update(accountByName2);
       // 提交事务
       //  txManger.commit();
       // } catch (SQLException e) {
       //  e.printStackTrace();
       ///  txManger.rollback();
       //  throw new RuntimeException();
       //  } finally {
       //  txManger.close();
       //  }

   }
}
// 一个用于创建service代理对象的工厂:
//  此时只用它创建AccountServiceImpl的代理对象
public class BeanFactory {
   private TransactionManager transactionManager;

   public void setTransactionManager(TransactionManager transactionManager) {
       this.transactionManager = transactionManager;
   }
   // 原始对象
   private IAccountService accountService;

   public void setAccountService(IAccountService accountService) {
       this.accountService = accountService;
   }
   // 创建账户业务层实现类的代理对象
   public IAccountService getAccountService() {
       // 创建代理对象
       IAccountService proxyAccountService = (IAccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {
           @Override
           public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
               // 执行被代理对象的任何方法,都会经过该方法此处添加事务控制
               Object rtValue = null;
               try {
                   // 开启事务
                   transactionManager.begin();
                   rtValue = method.invoke(accountService, args);
                   transactionManager.commit();
                   return rtValue;
               } catch (Exception e) {
                   transactionManager.rollback();
                   e.printStackTrace();
                   throw new RuntimeException(e);
               } finally {
                   transactionManager.close();
               }
           }
       });
       return proxyAccountService;
   }
}
// 事务管理器
public class TransactionManager {
   private ConnectionUtil connectionUtil;
   public void setConnectionUtil(ConnectionUtil connectionUtil) {
       this.connectionUtil = connectionUtil;
   }
   // 开启事务
   public void begin() {
       // 从当前线程获取连接实现开启事务
       try {
           connectionUtil.getThreadConnection().setAutoCommit(false);
       } catch (SQLException e) {
           e.printStackTrace();
       }
   }
   // 提交事务
   public void commit() {
       try {
           connectionUtil.getThreadConnection().commit();
       } catch (SQLException e) {
           e.printStackTrace();
       }
   }
   // 回滚事务
   public void rollback() {
       try {
           connectionUtil.getThreadConnection().rollback();
       } catch (SQLException e) {
           e.printStackTrace();
       }
   }
   // 释放连接
   public void close() {
       try {
           // 关闭连接 换回池中
           connectionUtil.getThreadConnection().close();
           // 解绑线程
           connectionUtil.remove();
       } catch (SQLException e) {
           e.printStackTrace();
       }
   }
}
public class ConnectionUtil {
   /**
    * 一个用于管理连接的工具类 用于实现连接和线程的绑定
    */
   //创建ThreadLocal对象
   private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
   // 创建一个连接池
    private DataSource dataSource;
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
   }
   /**
    * 获取当前线程上绑定的连接
    */
   public Connection getThreadConnection() {
       try {
           Connection conn = tl.get();
           if (conn == null) {
               // 从数据源中获取一个连接
              conn = dataSource.getConnection();
               // 和线程局部变量的绑定
               tl.set(conn);
           }
           // 返回线程上的连接
           return tl.get();
       } catch (Exception e) {
           throw new RuntimeException();
       }
   }
   /**
    * 把连接和当前线程解绑
    */
   public void remove() {
       tl.remove();
   }
}
public class AccountDaoImpl implements IAccountDao {

   private QueryRunner qr;
   private ConnectionUtil connectionUtil;

   public void setConnectionUtil(ConnectionUtil connectionUtil) {
       this.connectionUtil = connectionUtil;
   }

   public void setQr(QueryRunner qr) {
       this.qr = qr;
       System.out.println(qr);
   }
   @Override
   public void update(Account account) throws SQLException {
       String sql = "update account set name = ? , money = ? where id = ?";
       int i = qr.update(connectionUtil.getThreadConnection(),sql, account.getName(), account.getMoney(), account.getId());
       if (i > 0) {
           System.out.println("OK!");
       }

   }
   @Override
   public Account findAccountByName(String name) {
       /*List<Account> query = null;
       try {
           String sql = "select * from account where name = ? ";
           query = qr.query(sql, new BeanListHandler<Account>(Account.class), name);

       } catch (SQLException e) {
           e.printStackTrace();
       }
       assert query != null;
       return query.get(0);
   }
*/
       try {
           List<Account> accounts = qr.query(connectionUtil.getThreadConnection(),"select * from account where name = ? ", new BeanListHandler<Account>(Account.class), name);
           if (accounts.isEmpty()) {
               return null;
           }
           if (accounts.size() > 1) {
               throw new RuntimeException("结果集不唯一");
           }
           return accounts.get(0);
       } catch (Exception e) {
           throw new RuntimeException(e);
       }
   }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
   <!-- 配置accountService的代理对象-->
   <!--使用创建bean对象的第三种方法创建-->
   <!--getAccountService是accountService类型 所以在测试类需要加上 @Qualifier("proxyAccountService") -->
   <bean id="proxyAccountService" factory-bean="beanFactory" factory-method="getAccountService"/>
   <!--配置service set注入-->
   <!-- AccountServiceImpl impl = new AccountServiceImpl() -->
   <bean id="accountService" class="fun.chenqi.service.impl.AccountServiceImpl">
       <property name="dao" ref="accountDao"/>
       <!-- todo 如果不使用动态代理实现业务则放开这个注释
            <property name="transactionManager" ref="txManager"/>
       -->
   </bean>
   <!--配置dao-->
   <!--  AccountDaoImpl impl = new AccountDaoImpl()  -->
   <bean id="accountDao" class="fun.chenqi.dao.impl.AccountDaoImpl">
       <property name="qr" ref="runner"/>
       <property name="connectionUtil" ref="connectionUtil"/>
   </bean>
   <!--配置connectionUtil-->
   <bean id="connectionUtil" class="fun.chenqi.util.ConnectionUtil">
       <property name="dataSource" ref="dataSource"/>
   </bean>
   <!--配置事务管理器-->
   <bean id="txManager" class="fun.chenqi.util.TransactionManager">
       <property name="connectionUtil" ref="connectionUtil"/>
   </bean>
   <!--配置beanFactory-->
   <bean id="beanFactory" class="fun.chenqi.util.BeanFactory">
       <property name="transactionManager" ref="txManager"/>
       <property name="accountService" ref="accountService"/>
   </bean>
   <!-- 配置 QueryRunner -->
   <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
       <constructor-arg name="ds" ref="dataSource"/>
   </bean>
   <!-- 配置数据源 -->
   <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
       <property name="driverClass" value="com.mysql.jdbc.Driver"/>
       <property name="jdbcUrl" value="jdbc:mysql:///chenqi"/>
       <property name="user" value="root"/>
       <property name="password" value="111"/>
   </bean>
</beans>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:bean.xml"})
public class AccountTest {
   @Autowired
   @Qualifier("proxyAccountService")
   private IAccountService service;
@Test
   public void zhuan() {
    ac.getBean("proxyAccountService");
       try {
           service.transfer("aaa", "bbb", 1000F);
       } catch (SQLException e) {
           e.printStackTrace();
       }
   }
}
<dependencies>
   <dependency>
       <groupId>mysql</groupId>
       <artifactId>mysql-connector-java</artifactId>
       <version>5.1.6</version>
   </dependency>
   <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.12</version>
   </dependency>
   <!--dbutis工具类-->
   <dependency>
       <groupId>commons-dbutils</groupId>
       <artifactId>commons-dbutils</artifactId>
       <version>1.4</version>
   </dependency>
   <!--spring-->
   <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-context</artifactId>
       <version>5.0.4.RELEASE</version>
   </dependency>
   <dependency>
       <groupId>commons-dbcp</groupId>
       <artifactId>commons-dbcp</artifactId>
       <version>1.4</version>
   </dependency>
   <dependency>
       <groupId>c3p0</groupId>
       <artifactId>c3p0</artifactId>
       <version>0.9.1.2</version>
   </dependency>
   <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-test</artifactId>
       <version>5.0.8.RELEASE</version>
   </dependency>
</dependencies>

发表评论