大多嵌套事务都是通过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. python tools: iPython Notebook

    Introducing IPython Notebook IPython isn't a different programming language, it's just a set of comp ...

  2. 数据库mysql中having 和where的区别

    having的用法 having字句可以让我们筛选成组后的各种数据,where字句在聚合前先筛选记录,也就是说作用在group by和having字句前.而 having子句在聚合后对组记录进行筛选. ...

  3. SQL笔记(1)索引/触发器

    --创建聚集索引 create clustered index ix_tbl_test_DocDate on tbl_test(DocDate) GO --创建非聚集索引 create nonclus ...

  4. MongoDB入门教程之C#驱动操作实例

    实体类: using MongoDB.Bson; namespace WindowsFormsApp { class User { //public ObjectId _id; //BsonType. ...

  5. 【T-SQL系列】常用函数—聚合函数

    聚合函数平均值AVG.标准偏差STDEV.方差VAR.最大值MAX.最小值MIN.合计SUM.次数COUNT.极差值MAX-MIN.变异系数STDEV/AVG*100 什么是统计统计 就是通过样本特性 ...

  6. [转载] Docker网络原则入门:EXPOSE,-p,-P,-link

    原文: http://dockone.io/article/455 如果你已经构建了一些多容器的应用程序,那么肯定需要定义一些网络规则来设置容器间的通信.有多种方式可以实现:可以通过--expose参 ...

  7. openerp安装记录及postgresql数据库问题解决

    ubuntu-14.04下openerp安装记录1.安装PostgreSQL 数据库    a.安装         sudo apt-get install postgresql    安装后ubu ...

  8. Java将其他数据格式转换成json字符串格式

    package com.wangbo.util; import java.beans.IntrospectionException; import java.beans.Introspector; i ...

  9. java里有没有专门判断List里有重复的数据

    public static void main(String[] args)     {         List<String> list = new ArrayList<Stri ...

  10. Python的lambda表达式

    使用lambda来创建匿名函数,而用def创建的方法是有名称的,除了从表面上的方法名不一样外,python lambda还有哪些和def不一样呢? 1 python lambda会创建一个函数对象,但 ...