ThreadLocal来管理事务
ThreadLocal (扩展)
1 ThreadLocal API
ThreadLocal类只有三个方法:
l void set(T value):保存值;
l T get():获取值;
l void remove():移除值。
2 ThreadLocal的内部是Map
ThreadLocal内部其实是个Map来保存数据。虽然在使用ThreadLocal时只给出了值,没有给出键,其实它内部使用了当前线程做为键。
class MyThreadLocal<T> { private Map<Thread,T> map = new HashMap<Thread,T>(); public void set(T value) { map.put(Thread.currentThread(), value); } public void remove() { map.remove(Thread.currentThread()); } public T get() { return map.get(Thread.currentThread()); } } |
Service事务(扩展)
在Service中使用ThreadLocal来完成事务,为将来学习Spring事务打基础!
1 DAO中的事务
在DAO中处理事务真是“小菜一碟”。
public void xxx() { Connection con = null; try { con = JdbcUtils.getConnection(); con.setAutoCommitted(false); QueryRunner qr = new QueryRunner(); String sql = …; Object[] params = …; qr.update(con, sql, params); sql = …; Object[] params = …; qr.update(con, sql, params); con.commit(); } catch(Exception e) { try { if(con != null) } catch(Exception e) {} } finally { try { con.close(); } catch(Exception e) {} } } |
2 Service才是处理事务的地方
我们要清楚一件事,DAO中不是处理事务的地方,因为DAO中的每个方法都是对数据库的一次操作,而Service中的方法才是对应一个业务逻辑。也就是说我们需要在Service中的一方法中调用DAO的多个方法,而这些方法应该在一起事务中。
怎么才能让DAO的多个方法使用相同的Connection呢?方法不能再自己来获得Connection,而是由外界传递进去。
public void daoMethod1(Connection con, …) } public void daoMethod2(Connection con, …) } |
在Service中调用DAO的多个方法时,传递相同的Connection就可以了。
public class XXXService() { private XXXDao dao = new XXXDao(); public void serviceMethod() { Connection con = null; try { con = JdbcUtils.getConnection(); con.setAutoCommitted(false); dao.daoMethod1(con, …); dao.doaMethod2(con, …); com.commint(); } catch(Exception e) { try { con.rollback(); } catch(Exception e) {} } finally { try { con.close(); } catch(Exception e) {} } } } |
但是,在Service中不应该出现Connection,它应该只在DAO中出现,因为它是JDBC的东西,JDBC的东西是用来连接数据库的,连接数据库是DAO的事儿!!!但是,事务是Service的事儿,不能放到DAO中!!!
3 修改JdbcUtils
我们把对事务的开启和关闭放到JdbcUtils中,在Service中调用JdbcUtils的方法来完成事务的处理,但在Service中就不会再出现Connection这一“禁忌”了。
DAO中的方法不用再让Service来传递Connection了。DAO会主动从JdbcUtils中获取Connection对象,这样,JdbcUtils成为了DAO和Service的中介!
我们在JdbcUtils中添加beginTransaction()和rollbackTransaction(),以及commitTransaction()方法。这样在Service中的代码如下:
public class XXXService() { private XXXDao dao = new XXXDao(); public void serviceMethod() { try { JdbcUtils.beginTransaction(); dao.daoMethod1(…); dao.daoMethod2(…); JdbcUtils.commitTransaction(); } catch(Exception e) { JdbcUtils.rollbackTransaction(); } } } |
DAO
public void daoMethod1(…) { Connection con = JdbcUtils.getConnection(); } public void daoMethod2(…) { Connection con = JdbcUtils.getConnection(); } |
在Service中调用了JdbcUtils.beginTransaction()方法时,JdbcUtils要做准备好一个已经调用了setAutoCommitted(false)方法的Connection对象,因为在Service中调用JdbcUtils.beginTransaction()之后,马上就会调用DAO的方法,而在DAO方法中会调用JdbcUtils.getConnection()方法。这说明JdbcUtils要在getConnection()方法中返回刚刚准备好的,已经设置了手动提交的Connection对象。
在JdbcUtils中创建一个Connection
con属性,当它为null时,说明没有事务!当它不为null时,表示开启了事务。
l 在没有开启事务时,可以调用“开启事务”方法;
l 在开启事务后,可以调用“提交事务”和“回滚事务”方法;
l getConnection()方法会在con不为null时返回con,再con为null时,从连接池中返回连接。
beginTransaction()
判断con是否为null,如果不为null,就抛出异常!
如果con为null,那么从连接池中获取一个Connection对象,赋值给con!然后设置它为“手动提交”。
getConnection()
判断con是否为null,如果为null说明没有事务,那么从连接池获取一个连接返回;
如果不为null,说明已经开始了事务,那么返回con属性返回。这说明在con不为null时,无论调用多少次getConnection()方法,返回的都是同个Connection对象。
commitTransaction()
判断con是否为null,如果为null,说明没有开启事务就提交事务,那么抛出异常;
如果con不为null,那么调用con的commit()方法来提交事务;
调用con.close()方法关闭连接;
con = null,这表示事务已经结束!
rollbackTransaction()
判断con是否为null,如果为null,说明没有开启事务就回滚事务,那么抛出异常;
如果con不为null,那么调用con的rollback()方法来回滚事务;
调用con.close()方法关闭连接;
con = null,这表示事务已经结束!
JdbcUtils.java
public class JdbcUtils { private static DataSource dataSource = new private static Connection con = null; public static DataSource getDataSource() { return dataSource; } public static Connection getConnection() throws if(con == null) { return dataSource.getConnection(); } return con; } public static void beginTranscation() throws if(con != null) { throw new SQLException("事务已经开启,在没有结束当前事务时,不能再开启事务!"); } con = dataSource.getConnection(); con.setAutoCommit(false); } public static void commitTransaction() throws if(con == null) { throw new SQLException("当前没有事务,所以不能提交事务!"); } con.commit(); con.close(); con = null; } public static void rollbackTransaction() throws if(con == null) { throw new SQLException("当前没有事务,所以不能回滚事务!"); } con.rollback(); con.close(); con = null; } } |
4 再次修改JdbcUtils
现在JdbcUtils有个问题,如果有两个线程!第一个线程调用了beginTransaction()方法,另一个线程再调用beginTransaction()方法时,因为con已经不再为null,所以就会抛出异常了。
我们希望JdbcUtils可以多线程环境下被使用!这说明最好的方法是为每个线程提供一个Connection,这样每个线程都可以开启自己的事务了。
还记得ThreadLocal类么?
public class JdbcUtils { private static DataSource dataSource = new private static public static DataSource getDataSource() { return dataSource; } public static Connection getConnection() throws Connection con = tl.get();[涛2] if(con == null) { return dataSource.getConnection(); } return con; } public static void beginTranscation() throws Connection con = tl.get();[涛3] if(con != null[涛4] ) { throw new SQLException("事务已经开启,在没有结束当前事务时,不能再开启事务!"); } con = dataSource.getConnection();[涛5] con.setAutoCommit(false);[涛6] tl.set(con);[涛7] } public static void commitTransaction() throws Connection con = tl.get();[涛8] if(con == null[涛9] ) { throw new SQLException("当前没有事务,所以不能提交事务!"); } con.commit();[涛10] con.close();[涛11] tl.remove();[涛12] } public static void rollbackTransaction() throws Connection con = tl.get(); if(con == null) { throw new SQLException("当前没有事务,所以不能回滚事务!"); } con.rollback(); con.close(); tl.remove(); } } |
5 转账示例
public class AccountDao { public void updateBalance(String name, double balance) throws String sql = "update account set balance=balance+? where name=?"; Connection con = JdbcUtils.getConnection(); //修改的代码.... } } |
public class AccountService { private AccountDao dao = new AccountDao(); public void transfer(String from, String try { JdbcUtils.beginTranscation(); dao.updateBalance(from, -balance); dao.updateBalance(to, balance); JdbcUtils.commitTransaction(); } catch(Exception e) { try { JdbcUtils.rollbackTransaction(); } catch (SQLException e1) { throw new RuntimeException(e); } } } } |
AccountService as = new as.transfer("zs", "ls", 100); |
tl其实是一个Map,每个线程都可以来保存自己的Connection
获取当前线程的连接
获取当前线程的连接
如果当前线程已经有了连接,说明已经开启了事务
如果当前线程没有连接,说明还没有开启事务,那么就获取一个连接
设置连接为手动提交
把连接放到tl中,当前线程就有了连接了。
获取当前线程的连接
如果当前线程没有连接,那么就不能提交事务
如果当前线程有连接,那么就提交事务
关闭连接
把连接从tl中移除,当前线程就没有连接了,表示事务结束
ThreadLocal来管理事务的更多相关文章
- 用ThreadLocal管理事务
1.适用场景 一个service,操作两个dao,要求两个dao为同一个事务,要么全成功,要么全失败.
- ThreadLocal在Spring事务管理中的应用
ThreadLocal是用来处理多线程并发问题的一种解决方案.ThreadLocal是的作用是提供线程的局部变量,在多线程并发环境下,提供了与其他线程隔离的局部变量.通常这样的设计的情况是因为这个局部 ...
- 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。
基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别. 我还是喜欢基于Schema风格的Spring事务管理,但也有很多人在用基于@Tras ...
- Spring中的事物管理,用 @Transactional 注解声明式地管理事务
事物: 事务管理是企业级应用程序开发中必不可少的技术, 用来确保数据的 完整性和 一致性. 事务就是一系列的动作, 它们被当做一个单独的工作单元. 这些动作要么全部完成, 要么全部不起作用 事务的四 ...
- 通过案例掌握Spring 管理事务的步骤及配置
案例描述 通过完成生成订单业务,掌握事务处理. 需要d_order表和d_item表 订单生成时的业务逻辑:向d_order插入1条数据的同时,向t_item中插入若干条数据 这就是一个独立的 ...
- spring是如何管理 事务的
Spring提供的事务管理可以分为两类:编程式的和声明式的.编程式的,比较灵活,但是代码量大,存在重复的代码比较多:声明式的比编程式的更灵活方便. 1.传统使用JDBC的事务管理 以往使用JDBC ...
- Spring -- <tx:annotation-driven>注解基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)的区别。
借鉴:http://jinnianshilongnian.iteye.com/blog/1508018 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional ...
- 对Spring 容器管理事务支持的总结
1.问题 Connection conn = DataSourceUtils.getConnection(); //开启事务 conn.setAutoCommit(false); try { Obje ...
- spring框架学习(六)AOP事务及spring管理事务方式之Template模板
概念 1.事务 1)事务特性:ACID 原子性 :强调事务的不可分割. 一致性 :事务的执行的前后数据的完整性保持一致. 隔离性 :一个事务执行的过程中,不应该受到其他事务的干扰. 持久性 :事务一旦 ...
随机推荐
- 内核初始化优化宏(__init, __devinit)
在内核里经常可以看到__init, __devinit这样的语句,这都是在init.h中定义的宏,gcc在编译时会将被修饰的内容放到这些宏所代表的section. 原文地址:http://blog.c ...
- 谈谈一些有趣的CSS题目(十五)-- 奇妙的 background-clip: text
开本系列,谈谈一些有趣的 CSS 题目,题目类型天马行空,想到什么说什么,不仅为了拓宽一下解决问题的思路,更涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题 ...
- Mysql net start mysql启动,提示发生系统错误 5 拒绝访问,原因所在以及解决办法
1,Mysql net start mysql启动,提示发生系统错误 5 拒绝访问 在dos下运行net start MySQL 不能启动mysql!提示发生系统错误 5:拒绝访问!切换到管理员模式 ...
- 跟着刚哥学习Spring框架--创建HelloWorld项目(一)
1.Spring框架简介 Spring是一个开源框架,Spring是在2003年兴起的一个轻量级的开源框架,由Rod johnson创建.主要对JavaBean的生命周期进行管理的轻量级框架,Spri ...
- Firefox实用插件记录
之前总结过一个软件推荐的小文,用来记录一直以来在软件开发过程中遇到的各种实用的软件.后来发现里面越来越多的记录了Firefox的插件,所以今天决定单独抽出一个页面来记录Firefox的插件.因为平时大 ...
- springboot(十二):springboot如何测试打包部署
有很多网友会时不时的问我,spring boot项目如何测试,如何部署,在生产中有什么好的部署方案吗?这篇文章就来介绍一下spring boot 如何开发.调试.打包到最后的投产上线. 开发阶段 单元 ...
- MFC六大关键技术
视频教程地址观看:http://pan.baidu.com/s/1mhKQ6kK 1.MFC六大关键技术sada)MFC程序的初始化过程:从CWinApp类派生一个应用程序类:使用派生类定义的全局变量 ...
- GROUP BY语句与HAVING语句的使用
一.GROUP BY GROUP BY语句用来与聚合函数(aggregate functions such as COUNT, SUM, AVG, MIN, or MAX.)联合使用来得到一个或多个列 ...
- Java_中建立0-10M的消息(字符串)
直接用StringBuilder,它的append方法方便快速构建字符串. StringBuilder sb1=new StringBuilder(); for(int i=0;i<1024*1 ...
- photo
我们在android开发过程中 经常有做到发图片或修改上传头像的功能 即要调用系统相册 如何调用系统相册并处理返回的数据呢?因为随着android手机系统的提高 不同系统的手机对调用相册并处理相册不同 ...