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)
{con.rollback();}

} 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
ComboPooledDataSource();

private static Connection con = null;

public static DataSource getDataSource() {

return dataSource;

}

public static Connection getConnection() throws
SQLException {

if(con == null) {

return dataSource.getConnection();

}

return con;

}

public static void beginTranscation() throws
SQLException {

if(con != null) {

throw new SQLException("事务已经开启,在没有结束当前事务时,不能再开启事务!");

}

con = dataSource.getConnection();

con.setAutoCommit(false);

}

public static void commitTransaction() throws
SQLException {

if(con == null) {

throw new SQLException("当前没有事务,所以不能提交事务!");

}

con.commit();

con.close();

con = null;

}

public static void rollbackTransaction() throws
SQLException {

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
ComboPooledDataSource();

private static
ThreadLocal<Connection> tl[涛1]  = new ThreadLocal<Connection>();

public static DataSource getDataSource() {

return dataSource;

}

public static Connection getConnection() throws
SQLException {

Connection con = tl.get();[涛2]

if(con == null) {

return dataSource.getConnection();

}

return con;

}

public static void beginTranscation() throws
SQLException {

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
SQLException {

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
SQLException {

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
SQLException {

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
to, double balance) {

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
AccountService();

as.transfer("zs", "ls", 100);


tl其实是一个Map,每个线程都可以来保存自己的Connection

获取当前线程的连接

获取当前线程的连接

如果当前线程已经有了连接,说明已经开启了事务

如果当前线程没有连接,说明还没有开启事务,那么就获取一个连接

设置连接为手动提交

把连接放到tl中,当前线程就有了连接了。

获取当前线程的连接

如果当前线程没有连接,那么就不能提交事务

如果当前线程有连接,那么就提交事务

关闭连接

把连接从tl中移除,当前线程就没有连接了,表示事务结束

ThreadLocal来管理事务的更多相关文章

  1. 用ThreadLocal管理事务

    1.适用场景 一个service,操作两个dao,要求两个dao为同一个事务,要么全成功,要么全失败.

  2. ThreadLocal在Spring事务管理中的应用

    ThreadLocal是用来处理多线程并发问题的一种解决方案.ThreadLocal是的作用是提供线程的局部变量,在多线程并发环境下,提供了与其他线程隔离的局部变量.通常这样的设计的情况是因为这个局部 ...

  3. 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。

    基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别. 我还是喜欢基于Schema风格的Spring事务管理,但也有很多人在用基于@Tras ...

  4. Spring中的事物管理,用 @Transactional 注解声明式地管理事务

    事物: 事务管理是企业级应用程序开发中必不可少的技术,  用来确保数据的 完整性和 一致性. 事务就是一系列的动作, 它们被当做一个单独的工作单元. 这些动作要么全部完成, 要么全部不起作用 事务的四 ...

  5. 通过案例掌握Spring 管理事务的步骤及配置

    案例描述  通过完成生成订单业务,掌握事务处理.  需要d_order表和d_item表  订单生成时的业务逻辑:向d_order插入1条数据的同时,向t_item中插入若干条数据  这就是一个独立的 ...

  6. spring是如何管理 事务的

    Spring提供的事务管理可以分为两类:编程式的和声明式的.编程式的,比较灵活,但是代码量大,存在重复的代码比较多:声明式的比编程式的更灵活方便.  1.传统使用JDBC的事务管理  以往使用JDBC ...

  7. Spring -- <tx:annotation-driven>注解基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)的区别。

    借鉴:http://jinnianshilongnian.iteye.com/blog/1508018 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional ...

  8. 对Spring 容器管理事务支持的总结

    1.问题 Connection conn = DataSourceUtils.getConnection(); //开启事务 conn.setAutoCommit(false); try { Obje ...

  9. spring框架学习(六)AOP事务及spring管理事务方式之Template模板

    概念 1.事务 1)事务特性:ACID 原子性 :强调事务的不可分割. 一致性 :事务的执行的前后数据的完整性保持一致. 隔离性 :一个事务执行的过程中,不应该受到其他事务的干扰. 持久性 :事务一旦 ...

随机推荐

  1. CSS3 制作网格动画效果

    在线演示      源码下载

  2. linux 下创建管理员权限账户

    1.添加用户,首先用adduser命令添加一个普通用户,命令如下: #adduser tommy //添加一个名为tommy的用户 #passwd tommy //修改密码 Changing pass ...

  3. Java设计模式随笔

    大家都知道Java23种设计模式,大神总结如下: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接 ...

  4. 生产环境中使用Docker Swarm的一些建议

    译者按: 实践中会发现,生产环境中使用单个Docker节点是远远不够的,搭建Docker集群势在必行.然而,面对Kubernetes, Mesos以及Swarm等众多容器集群系统,我们该如何选择呢?它 ...

  5. SMP-1

    项目:该项目是用web做一个捐款的管理系统. 目标:可以记录接受捐款和资助捐款的信息,可以查询捐款等. 计划时间:2016-01-01至2016-01-15 实际用时:2016-01-08至2016- ...

  6. Android RoboGuice 使用指南

    1.概述 在开发应用时一个基本原则是模块化,并且近最大可能性地降低模块之间的耦合性.在Java平台上Spring Framework 以及.Net 平台 CAB ,SCSF 和Prism (WPF,S ...

  7. 高性能linux服务器内核调优

    高性能linux服务器内核调优 首先,介绍一下两个命令1.dmesg 打印系统信息.有很多同学们服务器出现问题,看了程序日志,发现没啥有用信息,还是毫无解决头绪,这时候,你就需要查看系统内核抛出的异常 ...

  8. java基础---->hashMap的简单分析(一)

    HashMap是一种十分常用的数据结构对象,可以保存键值对.它在项目中用的比较多,今天我们就来学习一下关于它的知识. HashMap的简单使用 一.hashMap的put和get方法 Map<S ...

  9. MyBatis和Hibernate相比,优势在哪里?

    1.开发对比开发速度 hibernate的真正掌握要比Mybatis来得难些.Mybatis框架相对简单很容易上手,但也相对简陋些.个人觉得要用好Mybatis还是首先要先理解好Hibernate. ...

  10. Mybatis中的like查询

    今天要做一个模糊查询 用的Mybatis 开始写的是: select id,bookName,author,publisher,donor,status,createDate,lastUpdate f ...