对于在Dao层,一个DML操作一个事务,升级到Service层,一个用户,一个事务

原先的连接Connection,只能是来一次,新创建一个连接connection。这样如果事务在Dao层已经默认提交,在service层出错时,对于俩张关联会有俩种不同的结果。为了解决这样的问题,我们将事务提升到service层。用到  threadlocation。

package com.bjsxt.util;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class DBUtil2 {
	static String driver;
	static String url;
	static String user;
	static String password;
	//定义一个threadLocal变量,存放collection,可以保证在同于个线程中,多个不同层次,不同方法,都是用的是同一个collection。
	private static ThreadLocal<Connection> threadLocal=new ThreadLocal<Connection>();
	//读取属性文件properties并获取内容
	static{
		//准备一个空的map,没有key-value
		Properties prop = new Properties();
		
		//读取文件,并将文件键值对存入Properties对象
		//InputStream is = new FileInputStream(new File("C:\Users\Administrator\workspace\java_empmgr2\src\conn.properties"));
		InputStream is = DBUtil2.class.getResourceAsStream("/jdbc.properties"); //classpath
		try {
			prop.load(is);
		} catch (IOException e) {
			e.printStackTrace();
		}
		//从prop中根据key获取四个参数的值
		driver = prop.getProperty("driver");
		//driver = prop.get("driver");
		url = prop.getProperty("url");
		user = prop.getProperty("username");
		password = prop.getProperty("password");
		
		//加载驱动
		try {
			Class.forName(driver);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 获取数据库连接
	 * @return
	 */
	public static  Connection getConnection(){
		Connection conn = null;
		//不是直接创建新的连接,而是次用threadLocal中获取
		conn = threadLocal.get();
		if (conn==null) {
			//当前线程第一次获取连接,需要建立数据库的连接
			try{			
				//建立数据库连接			
				conn = DriverManager.getConnection(url, user, password);
			}catch(SQLException e){
				e.printStackTrace();
			}
			//再放入threadlocal
			threadLocal.set(conn);
		}
		
		return conn;
		
	}
	
	/**
	 * 关闭数据库资源
	 * @param rs
	 * @param stmt
	 * @param conn
	 */
	public static void closeAll(ResultSet rs ,Statement stmt,Connection conn){
		//关闭数据库资源
		try {
			if(rs!=null){
				rs.close();
			}				
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
		try {
			if(stmt != null){
				stmt.close();
			}				
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
		try {
			if(conn != null){
				threadLocal.set(null);//将conn从threadLocal中移除
				conn.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * DML:insert  update  delete
	 */
	public static int executeUpdate(String  sql,Object ... params) {
		Connection conn =  null;
		PreparedStatement pstmt = null;
		int n = 0;
		try{
			//获取数据库连接
			conn = DBUtil2.getConnection();
			
			//使用手枪发送SQL命令并得到结果			
			pstmt = conn.prepareStatement(sql);
			
			for(int i=0;i<params.length;i++){
				pstmt.setObject(i+1, params[i]);
			}		
			n = pstmt.executeUpdate();
			
			
		}catch(SQLException e){
			//处理异常
			e.printStackTrace();
			//throw e;
			//抛出异常给上级(调用者)
			//throw new RuntimeException(e.toString());
			throw new MyException(e.toString());
		}finally{
			//关闭数据库资源
			DBUtil2.closeAll(null, pstmt, null);//采用业务层事务后,dao层不关闭连接
		}
				
		//返回数据
		return n;
	}
	public static void main(String[] args) {
		Connection conn = getConnection();
		System.out.println(conn);
	}
}

比原先的工具类不同的是。用到 DBUtil2时,我们将关闭connection都放在service层统一关闭。

 

public void add(Expense expense) {
		//获取序列的下一个值,
		ExpenseDao expDao = new ExpenseDaoImpl();
		int expId = expDao.nextVal();
		//开启手动提交事务
		Connection conn=DBUtil2.getConnection();
		try {
			//将自动提交事务关闭(此时并没有事务提交)
			conn.setAutoCommit(false);	
			
			//添加一条报销单(主单)信息
			expense.setExpId(expId);
			expDao.save(expense);
					
			//添加多条报销单所属的明细的信息
			ExpenseItemDao itemDao = new ExpenseItemDaoImpl();
			List<ExpenseItem> itemList = expense.getItemList();
			for(int i=0;i<itemList.size();i++){
				ExpenseItem item = itemList.get(i);
				item.setExpId(expId);
				itemDao.save(item);
			}
			//结束事务(提交或者回滚)
			conn.commit();//提交事务
		} catch (Exception e) {
			try {
				//回滚事务
				conn.rollback();//会清空缓存
			} catch (SQLException e1) {
				e1.printStackTrace();
				throw new MyException(e1.toString());
			}
			throw new MyException(e.toString());
		}
		finally{
			DBUtil2.closeAll(null, null, conn);
		}
	}

 

posted @ 2019-12-11 08:56  sakura-yxf  阅读(245)  评论(0)    收藏  举报