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. bzoj2144 【国家集训队2011】跳跳棋

    Description 跳跳棋是在一条数轴上进行的.棋子只能摆在整点上.每个点不能摆超过一个棋子.我们用跳跳棋来做一个简单的游戏:棋盘上有3颗棋子,分别在a,b,c这三个位置.我们要通过最少的跳动把他 ...

  2. MySQL常见建表选项以约束

    一.CREATE TABLE 选项 1.在定义列的时候,指定列选项 1)DEFAULT <literal>:定义列的默认值 当插入一个新行到表中并且没有给该列明确赋值时,如果定义了列的默认 ...

  3. Kafka官方文档

    Apache Kafka是 一个分布式消息发布订阅系统.它最初由LinkedIn公司基于独特的设计实现为一个分布式的提交日志系统( a distributed commit log),,之后成为Apa ...

  4. Python库的安装方法

    Python库的安装方法 Python的解释器CPython是开源的,我们可以下载查看其源代码,同时,Python语言的各种库也都是开源的.利用Python语言编程,可用的库有很多,在Python官方 ...

  5. 1008 Elevator

    Problem Description The highest building in our city has only one elevator. A request list is made u ...

  6. OC类的介绍

    类的本质 类的本质其实也是一个对象(类对象) 类对象 类对象再程序运行时一直存在 类对象是一种数据结构,存储类的基本信息:类大小,类名称,类的版本以及消息与函数的映射表等 类对象所保存的信息在程序编译 ...

  7. 运用三角不等式加速Kmeans聚类算法

    运用三角不等式加速Kmeans聚类算法 引言:最近在刷<数据挖掘导论>,第九章, 9.5.1小节有提到,可以用三角不等式,减少不必要的距离计算,从而达到加速聚类算法的目的.这在超大数据量的 ...

  8. Python 基础之基本数据类型

    首先,Python中的变量不需要声明.每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建.在Python中,变量就是变量,它没有类型,我们所说的"类型"是变量所指的内存中对象 ...

  9. 为Distinct准备的通用对比器

    使用Linq过滤重复对象的时候,我们使用Distinct. 但是Distinct对int long等值类型才有效果,对于对象我们需要自己写个对象. 以下利用泛型封装了两个类: CommonCompar ...

  10. maven 聚合工程的创建和打包

    ---恢复内容开始--- 使用eclipse创建maven项目 第一步:创建父工程hg-parent,如图; 右击空白处,new创建新maven工程: 搜索maven项目 父工程使用pom打包方式 第 ...