j2ee中在service层开启事务的思路

来源:互联网 发布:公司电脑网络部署方案 编辑:程序博客网 时间:2024/04/28 09:27

分析:
1.jdbc的conn对象提供了setAutoCommit方法可以关闭自动提交,在Dao层增删改查时可以方便的开事务
2.有些业务需求,如平台充值程序,需要先更新一个资金表表示支付资金,然后再查出一张充值卡发送给用户。
  这两个动作是业务上要求必须加事物的,所以需要在service层加事物。
3.由于service层即使拿到conn,开了事物,这个开了事物的conn也传不到dao,dao也调用不成,即使可以传到,
  Dao的实现就依赖了Service,不利于代码解耦。
4.ThreadLocal是一个Key为当前线程名的Map,我们可以调用它的Set方法直接往进放东西。
  所以每个线程可以方便的往进存入和取出数据。
5.我们将Utils层的getConn方法重写,每次Dao调用getConn时先从ThreadLocal中拿出。
  如果有就直接返回,如果没有,就从数据源(c3p0)中拿一个,放到ThreadLocal中,再返回。
6.此时在Service层也可以拿到当前线程的conn,开事务,但是这样做代码耦合度太高,所以在Utils中
  写一个专门用于开事物的方法和提交事物的方法供Service层调用。
7.static的Map中存的记录如果存的是对象引用,如果不删除,那么这些对象实例是永久存在的,不会被垃圾回收机制回收。
  反过来说,java中的对象的释放只能靠垃圾回收机制。所以当conn.close后被返回到连接池,Map的引用依然指向它,只是不能使用而已。
8.由于线程的命名是有规律可循的,比如Thread1,Thread2,当Thread1释放后下一个线程又有可能起这个名。
  所以ThreadLocal根据线程名来取的话很可能拿到的是一个以前也叫Thread1的线程遗留下的已经colse的conn
9.针对以上现象,我们要在关闭conn时从ThreadLocal中remove这个conn.

代码实现
package cn.itcast.utils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.DataSource;

import cn.itcast.dao.DaoException;

import com.mchange.v2.c3p0.ComboPooledDataSource;

/*
 * java 有垃圾回收机制   有没有内存泄漏的问题?
 *
 * 有  静态的集合
 *
 */
public class JdbcUtils {

 private JdbcUtils(){}
 private static DataSource ds ;
 
 private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
 
 // 静态代码块读配置文件
 static {
  try {
   
   ComboPooledDataSource cpds = new ComboPooledDataSource();
   
   ds = cpds;
   
  } catch (Exception e) {
   throw new ExceptionInInitializerError(e);
  }
  
  
  
 }
 
 // 返回数据连接池
 public static DataSource getDataSource() {
  return ds;
 }
 
 // 返回一个连接
 public static Connection getConnection() throws SQLException {
  // 看当先线程是否绑定了 connectin ,如果没绑定 绑定一个
  // 如果绑定了  就返回线程上绑定的那个
  // 从 ThreadLocale 中获得连接对象
  Connection conn = tl.get();  // 获得当先线程绑定的 Connection
  if(conn==null) {
   // 说明当前线程未绑定连接, 创建连接 并且绑定
   conn = ds.getConnection();
   tl.set(conn);
  }
  return conn;
 }
 
 public static void release(Connection conn, Statement stmt, ResultSet rs) {
  
  // 关的顺序和获得的顺序相反
  if(rs!=null) {
   try {
    rs.close();
   } catch (SQLException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   rs = null;
  }
  
  if(stmt!=null) {
   try {
    stmt.close();
   } catch (SQLException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   stmt = null;
  }
  
  if(conn!=null) {
   try {
    conn.close();
   } catch (SQLException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   conn = null;
  }
  
 }
 
 // 开启事务
 public static void startTransaction() {
  try {
   // 为当前线程绑定的Connection 开启事务
   Connection conn = getConnection();
   
   conn.setAutoCommit(false);
  } catch (SQLException e) {
   throw new DaoException(e);
  }
 }
 
 // 提交事务
 public static void commit() {
  try {
   // 判断一下线程上是否有连接
   if(tl.get()!=null) {
    // 提交当前线程上绑定的 Connection
    Connection conn = getConnection();
    
    conn.commit();
   }
  } catch (SQLException e) {
   throw new DaoException(e);
  }
  
 }
 // 回滚事务
 
 // 关闭连接
 public static void close() {
  // 关闭当前线程上的连接
  try {
   // 判断一下线程上是不是真的有连接
   if(tl.get()!=null) {
    // 提交当前线程上绑定的 Connection
    Connection conn = getConnection();
    conn.close();
    // 将连接从 tl 中移除   移除当前线程绑定的 Connection
    tl.remove();
   }
  } catch (SQLException e) {
   throw new DaoException(e);
  }
 }
}

package cn.itcast.service;

import java.sql.Connection;
import java.sql.SQLException;

import cn.itcast.dao.AccountDao;
import cn.itcast.domain.Account;
import cn.itcast.domain.Person;
import cn.itcast.utils.JdbcUtils;

// service设计些什么样的方法 取决于项目实现的具体功能
public class BusinessService {

 // 实现一个转账的功能
 public boolean transfer(String from, String to, float money) {

  try {
   AccountDao dao = new AccountDao();
   
   int  num = 1/0;
   // 开启事务
   JdbcUtils.startTransaction();

   // 根据from和to查出account
   Account fromAccount = dao.find(from);
   Account toAccount = dao.find(to);

   // 将两个账户 一个加 一个减
   fromAccount.setMoney(fromAccount.getMoney() - money);
   toAccount.setMoney(toAccount.getMoney() + money);

   // 找 dao 做更新操作
   boolean b1 = dao.update(fromAccount);

   int i = 1 / 0;

   boolean b2 = dao.update(toAccount);

   // 提交事务
   JdbcUtils.commit();
   return b1 & b2;
  } finally {
   // 关闭连接
   JdbcUtils.close();
  }
 }
}

原创粉丝点击