大多嵌套事务都是通过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

原文链接: javacodegeeks 翻译: ImportNew.com人晓
译文链接: http://www.importnew.com/11049.html

[转]POJO中使用ThreadLocal实现Java嵌套事务的更多相关文章

  1. Java中的ThreadLocal深入理解

    提到ThreadLocal,有些Android或者Java程序员可能有所陌生,可能会提出种种问题,它是做什么的,是不是和线程有关,怎么使用呢?等等问题,本文将总结一下我对ThreadLocal的理解和 ...

  2. 理解Java中的ThreadLocal

    提到ThreadLocal,有些Android或者Java程序员可能有所陌生,可能会提出种种问题,它是做什么的,是不是和线程有关,怎么使用呢?等等问题,本文将总结一下我对ThreadLocal的理解和 ...

  3. Java中的ThreadLocal详解

    一.ThreadLocal简介 多线程访问同一个共享变量的时候容易出现并发问题,特别是多个线程对一个变量进行写入的时候,为了保证线程安全,一般使用者在访问共享变量的时候需要进行额外的同步措施才能保证线 ...

  4. java POJO中 Integer 和 int 的不同,用int还是用Integer

    https://www.jianshu.com/p/ff535284916f [int和Integer的区别] int是java提供的8种原始类型之一,java为每个原始类型提供了封装类,Intege ...

  5. Java ThreadLocal Example(java中的ThreadLocal例子)

    Java ThreadLocal is used to create thread local variables. We know that all threads of an Object sha ...

  6. Java ThreadLocal (Java代码实战-006)

    ThreadLocal解决什么问题 由于 ThreadLocal 支持范型,如 ThreadLocal< StringBuilder >,为表述方便,后文用 变量 代表 ThreadLoc ...

  7. 在MySql中如何定义像Java中类型的Boolean类型

    在MySql中如何定义像Java中类型的Boolean类型数据..其实,mysql中 是没有直接定义成Boolean这种数据类型.它只能定义成 tinyint(1) ;如果长度是1,tinyint(1 ...

  8. 关于Mybatis中表中字段名和POJO中字段名不同的解决方法

    项目结构: POJO中: package com.domain; /** * @author mzy * 定义orders表对应的实体类 */ public class Order { /** * C ...

  9. Kotlin中变量不同于Java: var 对val(KAD 02)

    原文标题:Variables in Kotlin, differences with Java. var vs val (KAD 02) 作者:Antonio Leiva 时间:Nov 28, 201 ...

随机推荐

  1. LightOJ::1077 -----奇妙的最大公约数

    题目:http://www.lightoj.com/volume_showproblem.php?problem=1077 题意:在平面上, 给出两个点的坐标 例如:(x, y) 其中x, y 都是整 ...

  2. Python标准库之Sys模块使用详解

    sys 模块提供了许多函数和变量来处理 Python 运行时环境的不同部分. 处理命令行参数 在解释器启动后, argv 列表包含了传递给脚本的所有参数, 列表的第一个元素为脚本自身的名称. 使用sy ...

  3. 记CVTE2014年春季招聘实习生求职历程

    进度:目前已经过了网测 明天一面,好紧张,人生第一次实习面试.据说只有一分钟. 网测; 首先在http://exam.cvte.com/ 注册了账号,然后填写相关的个人信息,这里要仔细填写,因为它最后 ...

  4. apt-get下载的文件

    1. http://kurenai.elastos.org/2013/05/02/ubuntu-apt-get%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/ http:// ...

  5. bootstrap 图片轮播效果

    <!DOCTYPE html> <html> <head> <link rel="stylesheet" href="http: ...

  6. GFS Google File System(中文翻译)

    Google文件系统 GFS是一个可扩展的分布式文件系统,用于大型的.分布式的.对大量数据进行访问的应用.它运行于廉价的普通硬件上,但可以提供容错功能.它可以给大量的用户提供总体性能较高的服务. 1. ...

  7. 简析 addToBackStack使用和Fragment执行流程

    在使用Fragment的时候我们一般会这样写: FragmentTransaction transaction = getSupportFragmentManager().beginTransacti ...

  8. Python 学习笔记 - 10.类(Class) 1

    定义 Python 的 Class 比较特别,和我们习惯的静态语言类型定义有很大区别. 1. 使用一个名为 __init__ 的方法来完成初始化.2. 使用一个名为 __del__ 的方法来完成类似析 ...

  9. date 笔记

    1 语法 # date --help 用法:date [选项]... [+格式] 或:date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]] 1.1   ...

  10. 3.5 EF Code First总结

    1. 主键约定 属性名为“ID”(不区分大小写)或类名的后面跟有“ID”. 2. 关系约定 模型之间的关系,EF根据针对类型定义的导航属性来推断关系. 3. 连接字符串约定 (1)默认配置 如果连接字 ...