2.1对于事务的传播级别为requiredNew,也就是被嵌套这要开启一个新的事务的话这个jdbc本身就支持
Connection connection = ds.getConnection();
connection.setAutoCommit(false);
PreparedStatement ps = connection.prepareStatement("insert into business_action(tx_id, name, state,gmt_create, gmt_modified) values (?,?,?,now(),now())");
ps.setString(1, "tx-outer");
ps.setString(2, "outertest");
ps.setString(3, "I");
ps.executeUpdate();
//开启嵌套事务
Connection connection2 = ds.getConnection();
connection2.setAutoCommit(false);
PreparedStatement ps2 =connection2.prepareStatement("insert intobusiness_action(tx_id, name, state, gmt_create, gmt_modified) values (?,?,?,now(),now())");
ps2.setString(1, "tx-inner");
ps2.setString(2, "inner");
ps2.setString(3, "I");
ps2.executeUpdate();
System.out.println("xxxx");
//内层事务提交
connection2.commit();
connection.commit();
2.1对于事务的传播级别为required级别,也就是外层应经开始了事务就复用,没有开启那么开启一个事务, 这种场景还是有点小麻烦的
1)如何判断外层有无事务开启
2) 最外层的事务会做真正的提交或者回滚操作, 那么如何判断当前事务是不是最外层事务
3) 如果最外层自己业务正常,那么如何判断嵌套业务是否正常是否需要回滚
下面来看个简单的代码示例来解决上面那些问题
public class JdbcNestTransactionUtil {
private static ThreadLocal<ConnecitonHolder>CONNECITON_HOLDER = new ThreadLocal<ConnecitonHolder>();
public static Connection startTransaction(){
ConnecitonHolder holder = CONNECITON_HOLDER.get();
if (null == holder) {
try {
holder = newConnecitonHolder(DatasourceUtil.getDataSource().getConnection());
CONNECITON_HOLDER.set(holder);
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
holder.count++;
}
try {
holder.conn.setAutoCommit(false);
} catch (SQLException e) {
throw new RuntimeException(e);
}
return holder.conn;
}
public static void commitOrRollback() {
ConnecitonHolder holder = CONNECITON_HOLDER.get();
if (null == holder) {
throw new RuntimeException("数据库链接为空,无法commmitOrRollback");
}
if (holder.rollback) {
rollback();
} else {
commit();
}
}
private static void commit() {
ConnecitonHolder holder = CONNECITON_HOLDER.get();
if (null == holder) {
throw new RuntimeException("数据库链接为空,无法commmit");
}
if (holder.count == 0) {
try {
holder.conn.commit();
holder.conn.close();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
CONNECITON_HOLDER.remove();
}
} else {
holder.count--;
}
}
private static void rollback() {
ConnecitonHolder holder = CONNECITON_HOLDER.get();
if (null == holder) {
throw new RuntimeException("数据库链接为空,无法rollback");
}
if (holder.count == 0) {
try {
holder.conn.rollback();
holder.conn.close();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
CONNECITON_HOLDER.remove();
}
} else {
holder.count--;
}
}
public static void setRollbackOnly() {
ConnecitonHolder holder = CONNECITON_HOLDER.get();
if (null == holder) {
throw new RuntimeException("数据库链接为空,无法commmit");
}
holder.rollback = true;
}
private static class ConnecitonHolder {
private Connection conn;
private int count;
private boolean rollback;
public ConnecitonHolder(Connectionconn) {
this.conn = conn;
}
}
}
JdbcNestTransactionUtil工具类封装了事务操作,使得业务代码使用简单
1) 使用TheadLocal绑定事务,一个线程开启一个事务, 这样嵌套中的事物一旦当前线程开启了事务就不在再开启事物了
2) 利用一个int变量来保存事务的嵌套的深度,以此来判断出最外层
3) rollback变量设置是否回滚, 无论那层业务异常,一旦设置true,最外层做真正回滚来保证整个业务的事物性
下面来看下如何使用
public void methodA() {
Connection conn =JdbcNestTransactionUtil.startTransaction();
try {
数据库操作
} catch (SQLException e) {
JdbcNestTransactionUtil.setRollbackOnly();
} finally {
JdbcNestTransactionUtil.commitOrRollback();
}
}
public void methodB() {
Connection conn = JdbcNestTransactionUtil.startTransaction();
try {
数据库操作
} catch (SQLException e) {
JdbcNestTransactionUtil.setRollbackOnly();
} finally {
JdbcNestTransactionUtil.commitOrRollback();
}
}
methodA, methodB都是事务性操作,
testJdbcNestTx方法中开启了事务同时也调用的methodA,methodB方法是个嵌套事务示例。
public void testJdbcNestTx() {
Connection conn =JdbcNestTransactionUtil.startTransaction();
try {
methodA();
其他数据库操作
methodB();
} catch (SQLException e) {
JdbcNestTransactionUtil.setRollbackOnly();
} finally {
JdbcNestTransactionUtil.commitOrRollback();
}
}
上面只是简单示例,spring在jdbc事务之上做了层封装,支持更加复杂的数据操作,下面我们队spring的事务做个简单的介绍。