ThreadLocal 与 jdbc 事务操作

解密ThreadLocal类

转:http://qifuguang.me/2015/09/02/[Java%E5%B9%B6%E5%8F%91%E5%8C%85%E5%AD%A6%E4%B9%A0%E4%B8%83]%E8%A7%A3%E5%AF%86ThreadLocal/


事务操作

在了解完ThreadLocal类的特性之后,之后我们可以根据该类特性实现在事务的操作中Connection的不变问题。

创建一个过滤器TranactionFilter
public class TranactionFilter implements Filter {
    	public TranactionFilter() { }
	public void destroy() { }

	
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		
		Connection connection = null;
		
		try {
			
			//1. 获取连接
			connection = JDBCUtils.getConnection();
			
			//2. 开启事务
			connection.setAutoCommit(false);
			
			//3. 利用 ThreadLocal 把连接和当前线程绑定
			ConnectionContext.getInstance().bind(connection);
			
			//4. 把请求转给目标 Servlet
			chain.doFilter(request, response);
			
			//5. 提交事务
			connection.commit();
		} catch (Exception e) {
			e.printStackTrace();
			
			//6. 回滚事务
			try {
				connection.rollback();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
			
			HttpServletResponse resp = (HttpServletResponse) response;
			HttpServletRequest req = (HttpServletRequest) request;
			resp.sendRedirect(req.getContextPath() + "/error-1.jsp");
			
		} finally{
			//7. 解除绑定
			ConnectionContext.getInstance().remove();
			
			//8. 关闭连接
			JDBCUtils.release(connection);
			
		}
		
	}

	public void init(FilterConfig fConfig) throws ServletException {
		// TODO Auto-generated method stub
	}

}

在web.xml中配置
<filter>
    <display-name>TranactionFilter</display-name>
    <filter-name>TranactionFilter</filter-name>
    <filter-class>bookstore.filter.TranactionFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>TranactionFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

ConnectionContext类 的实现

public class ConnectionContext {
	//私有化构造器,
	private ConnectionContext(){}
	//单例模式,只创建一个对象
	private static ConnectionContext instance = new ConnectionContext();
	
	public static ConnectionContext getInstance() {
		return instance;
	}
	
	private ThreadLocal<Connection> connectionThreadLocal = 
			new ThreadLocal<>();
	//把参数connection与当前线程绑定		
	public void bind(Connection connection){
		connectionThreadLocal.set(connection);
	}
	//获取绑定到线程中的connection
	public Connection get(){
		return connectionThreadLocal.get();
	}
	//删除线程中的局部属性( 即connection )
	public void remove(){
		connectionThreadLocal.remove();
	}
	
}


在bookservice类中操作具体的业务,调用不同的DAO实现
public class BookService {
	
	private BookDAO bookDAO = new BookDAOImpl();
	private AccountDAO accountDAO = new AccountDAOIml();
	private TradeDAO tradeDAO = new TradeDAOImpl();
	private UserDAO userDAO = new UserDAOImpl();
	private TradeItemDAO tradeItemDAO = new TradeItemDAOImpl();

	//业务方法. 
	public void cash(ShoppingCart shoppingCart, String username,
			String accountId) {
		
		//1. 更新 mybooks 数据表相关记录的 salesamount 和 storenumber
		bookDAO.batchUpdateStoreNumberAndSalesAmount(shoppingCart.getItems());
		//当发生异常,整组业务操作全部rollBack回滚;
		int i = 10 / 0;
		
		//2. 更新 account 数据表的 balance
		accountDAO.updateBalance(Integer.parseInt(accountId), shoppingCart.getTotalMoney());
		
		//3. 向 trade 数据表插入一条记录
		Trade trade = new Trade();
		trade.setTradeTime(new Date(new java.util.Date().getTime()));
		trade.setUserId(userDAO.getUser(username).getUserId());
		tradeDAO.insert(trade);
		
		//4. 向 tradeitem 数据表插入 n 条记录
		Collection<TradeItem> items = new ArrayList<>();
		for(ShoppingCartItem sci: shoppingCart.getItems()){
			TradeItem tradeItem = new TradeItem();
			
			tradeItem.setBookId(sci.getBook().getId());
			tradeItem.setQuantity(sci.getQuantity());
			tradeItem.setTradeId(trade.getTradeId());
			
			items.add(tradeItem);
		}
		tradeItemDAO.batchSave(items);
		
		//5. 清空购物车
		shoppingCart.clear();
	}
	
}

在DAO中的连接数据库接口的实现方法中connection始终是同一个,即可保证事务安全

业务方法调用层次结构及DAO层获取connection的实现

比如
//2. 更新 account 数据表的 balance
accountDAO.updateBalance(Integer.parseInt(accountId), shoppingCart.getTotalMoney());
转到
public class AccountDAOIml extends BaseDAO<Account> implements AccountDAO {
	@Override
	public void updateBalance(Integer accountId, float amount) {
		String sql = "UPDATE account SET balance = balance - ? WHERE accountId = ?";
		update(sql, amount, accountId); //重写的BaseDao中update方法
	}

}
再转
public void update(String sql, Object... args) {
		Connection connection = null;
		
		try {
			connection = ConnectionContext.getInstance().get();//ConnectionContext是个单例类
			queryRunner.update(connection, sql, args);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值