POJO中使用ThreadLocal实现Java嵌套事务

标签: 基础技术 Java ThreadLocal 事务 | 发表时间:2014-06-10 08:00 | 作者:人晓
出处:http://www.importnew.com

大多嵌套事务都是通过EJB实现的,现在我们尝试实现对POJO的嵌套事务。这里我们使用了ThreadLocal的功能。

理解嵌套事务

事务是可以嵌套的。所以内层事务或外层事务可以在不影响其他事务的条件下进行回滚或提交。

新建的事务嵌套在外层事务中。如果内层事务完成(不论是回滚或是提交),外层的事务就可以进行回滚或提交,这样的操作并不会影响内层事务。首先关闭最内层的事务,并逐步移动到外层事务。

使用简单的POJO实现

新建如下接口:

importjava.sql.Connection;

public interface TransactionManager {

    Connection getConnection();
    void beginTransaction();
    void commit();
    void rollback();
}

新建如下事务管理类:

importjava.sql.Connection;
importjava.sql.DriverManager;
importjava.sql.SQLException;
importjava.util.Stack;

public class TransactionManagerStackImpl implements TransactionManager {
    
    private Stack<Connection>connections = new Stack<Connection>();

    @Override
    public Connection getConnection() {

        if (connections.isEmpty()) {
            this.addConn();
        }

        return connections.peek();
    }

    @Override
    public void beginTransaction() {
        this.addConn();
    }

    @Override
    public void commit() {
        try {
            if (connections.peek() != null&& !connections.peek().isClosed()) {
                System.out.println(connections.peek().toString() +"--Commit---");
                connections.peek().commit();
                connections.pop().close();
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void rollback() {
        try {

            if (connections.peek() != null&& !connections.peek().isClosed()) {
                System.out.println(connections.peek().toString() +"--Rollback---");
                connections.peek().rollback();
                connections.pop().close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

    private void addConn() {
        try {
            Connection con = this.getMysqlConnection();
            con.setAutoCommit(false);
            connections.push(con);
            System.out.println(con.toString() +"--Conection---");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        
    }

    private Connection getMysqlConnection() {
        return getConnection("com.mysql.jdbc.Driver", "jdbc:mysql://localhost:3306/testdb", "test", "test12345");
    }

    private Connection getConnection(String driver, String connection,
            String user, String password) {

        try {
            Class.forName(driver);
            return DriverManager.getConnection(connection, user, password);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }

        returnnull;

    }
}

到这里,我们创建了一个栈(Stack)

private Stack<Connection> connections = new Stack<Connection>();

事务遵循栈“先进后出”的原则,通过栈存储事务的连接:

public void beginTransaction()

beginTransaction()用于开启一个新的事务,并将连接加入到栈中。自动提交设置为否:

public Connection getConnection()

getConnection()获得当前事务的连接。如果连接为空,则创建新的连接并将其加入到栈:

public void commit()

提交当前的事务,之后关闭连接,并将其从栈中移除:

 
public void rollback()

回滚当前的事务,之后关闭连接,并将其从栈中移除。

上面的TransactionManagerStackImpl类为单线程创建了嵌套事务。

多线程的嵌套事务

在多线程的应用中,每个线程都有其独立的事务和嵌套事务。

我们使用ThreadLocal管理栈的连接。

importjava.sql.Connection;

public class TransactionManagerThreadLocalimplementsTransactionManager {
    
    private static final ThreadLocal<TransactionManager>tranManager = newThreadLocal<TransactionManager>() {
        
    protected TransactionManager initialValue() {
        System.out.println(this.toString() + "--Thread Local Initialize--");
    return new TransactionManagerStackImpl();
        }
      };

    @Override
    public void beginTransaction() {
        tranManager.get().beginTransaction();
    }

    @Override
    public void commit() {
        tranManager.get().commit();
    }

    @Override
    public void rollback() {
        tranManager.get().rollback();
    }

    @Override
    public Connection getConnection() {
        returntranManager.get().getConnection();
    }
}

这里初始化TransactionManagerStackImpl,在线程中创建嵌套的事务。

测试

测试上面的方法,提交内层事务,回滚外层事务。

importjava.sql.Connection;

public class NestedMain implements Runnable {
    
    private int v = 0;
    private String name;
    
    NestedMain(int v, String name) {
        this.v = v;
        this.name = name;
    }

    public static void main(String[] args) throws Exception{
        
        for (inti = 0; i< 3; i++) {
            NestedMain main = newNestedMain(i * 10, "Ravi" + i);
            new Thread(main).start();
        }
    }

    @Override
    public void run() {
        
        try {
            TransactionManagerThreadLocal local = new TransactionManagerThreadLocal();
            
            // Transaction 1 ( outer )
            local.beginTransaction();
            Connection con = local.getConnection();
            String sql = "INSERT INTO test_tran (emp_id, name) VALUES ('1"+v+"', '"+ name+v+"')";
            this.insert(con, sql);
    
                // Transaction 2 ( Inner )
                local.beginTransaction();
                con = local.getConnection();
                sql = "INSERT INTO test_tran (emp_id, name) VALUES ('2"+v+"', '"+ name+v+"')";
                this.insert(con, sql);
                local.commit(); // Committing 2

            local.rollback(); // Rollback 1 Outer

        } catch (Exception e) {
            e.printStackTrace();
        }

结果

com.ttit.TransactionManagerThreadLocal$1@1270b73--Thread Local Initialize--
com.ttit.TransactionManagerThreadLocal$1@1270b73--Thread Local Initialize--
com.ttit.TransactionManagerThreadLocal$1@1270b73--Thread Local Initialize--
com.mysql.jdbc.JDBC4Connection@10dd1f7--Conection---
com.mysql.jdbc.JDBC4Connection@1813fac--Conection---
com.mysql.jdbc.JDBC4Connection@136228--Conection---
com.mysql.jdbc.JDBC4Connection@1855af5--Conection---
com.mysql.jdbc.JDBC4Connection@e39a3e--Conection---
com.mysql.jdbc.JDBC4Connection@1855af5--Commit---
com.mysql.jdbc.JDBC4Connection@e39a3e--Commit---
com.mysql.jdbc.JDBC4Connection@9fbe93--Conection---
com.mysql.jdbc.JDBC4Connection@9fbe93--Commit---
com.mysql.jdbc.JDBC4Connection@10dd1f7--Rollback---
com.mysql.jdbc.JDBC4Connection@1813fac--Rollback---
com.mysql.jdbc.JDBC4Connection@136228--Rollback---

|  name       | emp_id           
| ------------- |:-------------:
| Ravi220        | 220
| Ravi00      | 20      
|Ravi110 | 210      

内层事务回滚,外层事务提交的情况:

com.ttit.TransactionManagerThreadLocal$1@1270b73--Thread Local Initialize--
com.ttit.TransactionManagerThreadLocal$1@1270b73--Thread Local Initialize--
com.ttit.TransactionManagerThreadLocal$1@1270b73--Thread Local Initialize--
com.mysql.jdbc.JDBC4Connection@9f2a0b--Conection---
com.mysql.jdbc.JDBC4Connection@136228--Conection---
com.mysql.jdbc.JDBC4Connection@1c672d0--Conection---
com.mysql.jdbc.JDBC4Connection@9fbe93--Conection---
com.mysql.jdbc.JDBC4Connection@1858610--Conection---
com.mysql.jdbc.JDBC4Connection@9fbe93--Rollback---
com.mysql.jdbc.JDBC4Connection@1858610--Rollback---
com.mysql.jdbc.JDBC4Connection@1a5ab41--Conection---
com.mysql.jdbc.JDBC4Connection@1a5ab41--Rollback---
com.mysql.jdbc.JDBC4Connection@9f2a0b--Commit---
com.mysql.jdbc.JDBC4Connection@136228--Commit---
com.mysql.jdbc.JDBC4Connection@1c672d0--Commit---
...
|  name       | emp_id           
| ------------- |:-------------:
| Ravi00         | 10
| Ravi220      | 120      
|Ravi110 | 110 

相关文章

相关 [pojo 中使 threadlocal] 推荐:

POJO中使用ThreadLocal实现Java嵌套事务

- - ImportNew
大多嵌套事务都是通过EJB实现的,现在我们尝试实现对POJO的嵌套事务. 这里我们使用了ThreadLocal的功能. 所以内层事务或外层事务可以在不影响其他事务的条件下进行回滚或提交. 新建的事务嵌套在外层事务中. 如果内层事务完成(不论是回滚或是提交),外层的事务就可以进行回滚或提交,这样的操作并不会影响内层事务.

ThreadLocal介绍

- - ITeye博客
一、java.lang.ThreadLocal. 一个实例就是一个容器,所有可以访问到这个实例的线程都可以在这个容器中存储一个该线程独立使用的变量. 这个实例里面其实是一个Map结构的属性,存储以线程对象为KEY,变量为VALUE的数据. 二、ThreadLocal有这样几个方法:. 返回当前线程对应的那个变量.

正确理解ThreadLocal

- - Java - 编程语言 - ITeye博客
转自: http://www.iteye.com/topic/103804. 首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的. 另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本.

ThreadLocal的内存泄露

- - zzm
ThreadLocal的目的就是为每一个使用ThreadLocal的线程都提供一个值,让该值和使用它的线程绑定,当然每一个线程都可以独立地改变它绑定的值. 如果需要隔离多个线程之间的共享冲突,可以使用ThreadLocal,这将极大地简化你的程序.. 关于的ThreadLocal更多内容,请参考《 ThreadLocal》.

(转)ThreadLocal的内存泄漏问题

- - 编程语言 - ITeye博客
原文:http://www.godiscoder.com/?p=479. 在最近一个项目中,在项目发布之后,发现系统中有内存泄漏问题. 表象是堆内存随着系统的运行时间缓慢增长,一直没有办法通过gc来回收,最终于导致堆内存耗尽,内存溢出. 开始是怀疑ThreadLocal的问题,因为在项目中,大量使用了线程的ThreadLocal保存线程上下文信息,在正常情况下,在线程开始的时候设置线程变量,在线程结束的时候,需要清除线程上下文信息,如果线程变量没有清除,会导致线程中保存的对象无法释放.

ThreadLocal解决dateFormat多线程错误

- - 研发管理 - ITeye博客
出处  http://www.blogjava.net/killme2008/archive/2011/07/10/354062.html.     上周在线上系统发现了两个bug,值得记录下查找的过程和原因. 以后如果还有查找bug比较有价值的经历,我也会继续分享.     第一个bug的起始,是在线上日志发现一个频繁打印的异常——java.lang.ArrayIndexOutOfBoundsException.

HibernateTools实现pojo类 数据库schma mapping映射的相互转换

- - CSDN博客架构设计推荐文章
利用HibernateTools,用POJO类,Mapping映射文件,数据库表有其中的一项,就能生成其他两项.     在使用Hibernate开发系统持久层时,按照一般开发流程,首先会从业务分析中获得业务实体,从而获得系统中真正需要的实体类,然后写Hibernate的mapping映射文件,最后根据映射文件,生成数据库表,以上这三步是Hibernate开发的起始.

HibernateTools实现pojo类 数据库schma mapping映射的相互转换 二

- - CSDN博客Web前端推荐文章
接着上一篇博客: HibernateTools实现pojo类 数据库schma mapping映射的相互转换. 思路二:由数据库表,生成Mapping映射文件和POJO类.    虽然可以实现,但个人觉着先设计数据库,然后再生成类不符合Hibernate的面对对象持久化的思维方式. 好了,还是说步骤吧,首先在test数据库建立两张表,分别为course表和teacher表.

java 使用动态代理 - ThreadLocal实现事务管理

- - ITeye博客
动态代理:JDK动态代理只能对实现了接口的类进入代理,采用JDK动态代理必须实现InvocationHandler接口,采用Proxy 类创建相应的代理类.. 下面使用Model2(MVC)使用代理事务查询用户基本信息,使用DB2数据库:. 建立表: create table T_USER (. constraint P_KEY_1 primary key (USER_ID) ); 初始化数据: insert into t_user(user_id, user_name, password) values('root', '系统管理员', 'root');.

ThreadLocal原理及其实际应用 - format丶

- - 博客园_首页
java猿在面试中,经常会被问到1个问题:. java实现同步有哪几种方式. 大家一般都会回答使用synchronized, 那么还有其他方式吗. 答案是肯定的, 另外一种方式也就是本文要说的ThreadLocal. ThreadLocal介绍. ThreadLocal, 看名字也能猜到, "线程本地", "线程本地变量".