1. 程式人生 > >十六、DBUtils事務使用

十六、DBUtils事務使用

要保證QueryRunner操作事務時能夠生效就必須要儲存呼叫api時使用的Connection物件是同一個.為了減少程式碼的耦合性,這裡可以使用ThreadLocal類來綁定當前執行緒的Connection物件.

關於事務的操作以及Connection物件的唯一性操作可以寫到一個工具類中

package blog.csdn.net.mchenys.utils;

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

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class DataSourceUtils {
	// c3p0連線池
	private static ComboPooledDataSource ds = new ComboPooledDataSource();
	// 當前執行緒關聯的資料庫連線物件
	private static ThreadLocal<Connection> tl = new ThreadLocal<>();

	/**
	 * 從執行緒中獲取連線
	 * 
	 * @return
	 * @throws SQLException
	 */
	public static Connection getConnection() throws SQLException {
		// 從執行緒中獲取conneciton
		Connection conn = tl.get();
		if (conn == null) {
			conn = ds.getConnection();
			// 和當前執行緒繫結
			tl.set(conn);
		}
		return conn;
	}

	/**
	 * 取資料來源
	 * @return
	 */
	public static DataSource getDataSource() {
		return ds;
	}

	/**
	 * 釋放資源
	 * @param st
	 * @param rs
	 */
	public static void closeResource(Statement st, ResultSet rs) {
		closeResultSet(rs);
		closeStatement(st);
	}

	/**
	 * 釋放資源
	 * @param conn
	 * @param st
	 * @param rs
	 */
	public static void closeResource(Connection conn, Statement st, ResultSet rs) {
		closeResource(st, rs);
		closeConn(conn);
	}

	/**
	 * 釋放 connection
	 * @param conn
	 */
	public static void closeConn(Connection conn) {
		if (conn != null) {
			try {
				conn.close();
				// 和執行緒解綁
				tl.remove();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			conn = null;
		}
	}

	/**
	 * 釋放 statement 
	 * @param st
	 */
	public static void closeStatement(Statement st) {
		if (st != null) {
			try {
				st.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			st = null;
		}
	}

	/**
	 * 釋放結果集
	 * @param rs
	 */
	public static void closeResultSet(ResultSet rs) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			rs = null;
		}
	}

	/**
	 * 開啟事務
	 * @throws SQLException
	 */
	public static void startTransaction() throws SQLException {
		getConnection().setAutoCommit(false);
	}

	/**
	 * 事務提交且釋放連線
	 */
	public static void commitAndClose() {
		Connection conn = null;
		try {
			conn = getConnection();
			// 事務提交
			conn.commit();
			// 關閉資源
			conn.close();
			// 解除繫結
			tl.remove();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 事務回滾且釋放資源
	 */
	public static void rollbackAndClose() {
		Connection conn = null;
		try {
			conn = getConnection();
			// 事務回滾
			conn.rollback();
			// 關閉資源
			conn.close();
			// 解除版定
			tl.remove();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}

下面以新增訂單和訂單項的操作為例演示下在dbutils下的事務事務使用步驟.

需求:提供一個方法生成一條訂單,該訂單可以包含有多個訂單項,當訂單或者訂單項添加出錯的時候回滾所有操作,必須要等這2個操作都完成才算生成訂單成功.

Service層操作

package blog.csdn.net.mchenys.service.impl;

import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;

import blog.csdn.net.mchenys.dao.OrderDao;
import blog.csdn.net.mchenys.dao.impl.OrderDaoImpl;
import blog.csdn.net.mchenys.domain.Order;
import blog.csdn.net.mchenys.domain.OrderItem;
import blog.csdn.net.mchenys.domain.PageBean;
import blog.csdn.net.mchenys.domain.Product;
import blog.csdn.net.mchenys.service.OrderService;
import blog.csdn.net.mchenys.utils.DataSourceUtils;

/**
 * 訂單模組
 * 
 * @author mChenys
 *
 */
public class OrderServiceImpl implements OrderService {
        ....

	// 新增訂單
	public void add(Order order) throws Exception {
		try {
			// 開啟事務
			DataSourceUtils.startTransaction();

			OrderDao dao = new OrderDaoImpl();
			// 新增一條訂單記錄
			dao.add(order);

			// 新增多條訂單項記錄
			for (OrderItem item : order.getItems()) {
				dao.addItem(item);
			}
			// 完成事務
			DataSourceUtils.commitAndClose();
		} catch (SQLException e) {
			e.printStackTrace();
			// 回滾事務
			DataSourceUtils.rollbackAndClose();
			throw e;
		}
	}

	....

}

DAO層操作

package blog.csdn.net.mchenys.dao.impl;

import java.util.List;
import java.util.Map;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

import blog.csdn.net.mchenys.dao.OrderDao;
import blog.csdn.net.mchenys.domain.Order;
import blog.csdn.net.mchenys.domain.OrderItem;
import blog.csdn.net.mchenys.utils.DataSourceUtils;

public class OrderDaoImpl implements OrderDao {

        ....

	@Override
	public void add(Order order) throws Exception {

		// 手動管理事務則不能通過構造方法傳入connection
		QueryRunner qr = new QueryRunner();
		String sql = "insert into orders values(?,?,?,?,?,?,?,?)";
		// connection需要保持同一個.從DataSourceUtils中獲取
		qr.update(DataSourceUtils.getConnection(), sql, order.getOid(), order.getOrdertime(), order.getTotal(),
				order.getState(), order.getAddress(), order.getName(), order.getTelephone(), order.getUser().getUid());
	}

	@Override
	public void addItem(OrderItem orderItem) throws Exception {

		// 手動管理事務,connection需要保持同一個.從DataSourceUtils中獲取
		QueryRunner qr = new QueryRunner();
		String sql = "insert into orderitem values(?,?,?,?,?)";
		qr.update(DataSourceUtils.getConnection(), sql, orderItem.getItemid(), orderItem.getCount(),
				orderItem.getSubtotal(), orderItem.getProduct().getPid(), orderItem.getOrder().getOid());

	}

	....

}