使用ThreadLocal实现Java嵌套事务

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:

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

理解嵌套事务

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

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

A9F7VS2.png

使用简单的POJO实现

新建如下接口:

public interface TransactionManager {
 
    Connection getConnection();
    void beginTransaction();
    void commit();
    void rollback();
}

新建如下事务管理类:

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管理栈的连接。

public class TransactionManagerThreadLocal implements TransactionManager {
     
    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,在线程中创建嵌套的事务。

测试

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

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

原文链接: javacodegeeks

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
3月前
|
Java Spring
Java事务&事务失效场景
Java事务&事务失效场景
61 0
|
3月前
|
存储 Java 数据安全/隐私保护
探索Java中神奇的ThreadLocal:为什么它是多线程编程的重要工具?
探索Java中神奇的ThreadLocal:为什么它是多线程编程的重要工具?
|
6月前
|
存储 Java
java之线程死锁和ThreadLocal的使用
java之线程死锁和ThreadLocal的使用
|
3天前
|
存储 Java
Java的ThreadLocal使用
Java的ThreadLocal使用
11 1
|
23天前
|
安全 Java
java中线程经常被问到ThreadLocal你懂吗?
java中线程经常被问到ThreadLocal你懂吗?
7 0
|
2月前
|
存储 Java 数据库连接
详解Java中ThreadLocal类型
详解Java中ThreadLocal类型
29 1
|
3月前
|
存储 监控 Java
Java 中的 ThreadLocal:概念、应用及代码示例
Java 中的 ThreadLocal:概念、应用及代码示例
36 0
|
7月前
|
存储 安全 Java
【Java】线程数据共享和安全 -ThreadLocal
今天我要为大家推荐一个Java中非常实用且神奇的工具——ThreadLocal。它可以让我们在多线程环境下,轻松地实现线程私有的数据存储。它可以帮助我们在多线程环境下轻松解决变量共享和线程安全的问题。
|
8月前
java202304java学习笔记第六十五天-ssm-声明式控制-事务参数的配置1
java202304java学习笔记第六十五天-ssm-声明式控制-事务参数的配置1
22 0
|
8月前
java202304java学习笔记第六十五天-ssm-注解方式-声明式控制事务
java202304java学习笔记第六十五天-ssm-注解方式-声明式控制事务
44 0