java简单实现事务控制的demo

368 阅读2分钟

小知识,大挑战!本文正在参与「程序员必备小知识」创作活动

1.实现事务控制的基本思路

1.1代理机制

最原始的做法就是在每个数据库操作的时候通过用一个链接传递实现对事务的控制。
这样的话重复代码太多,不容易控制事务。

如果用代理机制的话就可以很方便的解决这个问题。

代理的是什么?
事务统一的开启,提交,回滚,释放

1.2事务的获取要保证当前的线程处理是唯一的

这里用到了Java的ThreadLocal会保证当前的线程只有一个链接再用

2.实现过程

2.1数据库链接工具类

public class JDBCUtil {
	private JDBCUtil() {
	}

	private static ThreadLocal<Connection> threadConn = new ThreadLocal<Connection>();

	// 获取数据库连接
	public static Connection getConnection() {
		Connection conn = threadConn.get();
		if (conn == null) {
			try {
				System.out.println("创建链接");
				Class.forName("oracle.jdbc.driver.OracleDriver");//找到oracle驱动器所在的类
				String url="jdbc:oracle:thin:@127.0.0.1:1521:orcl"; //URL地址
				String username="test";
				String password="test";
				conn=DriverManager.getConnection(url, username, password);
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			threadConn.set(conn);
		}
		return conn;
	}

	// 设置事务手动提交
	public static void benigTransction(Connection conn) {
		System.out.println("事务手动提交");
		try {
			if (conn != null) {
				if (conn.getAutoCommit()) {
					conn.setAutoCommit(false);
				}
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	// 提交事务
	public static void endTransction(Connection conn) {
		System.out.println("提交事务");
		try {
			if (conn != null) {
				if (!conn.getAutoCommit()) {
					conn.commit();
				}
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	// 设置Connection的原始状态
	public static void recoverTransction(Connection conn) {
		System.out.println("恢复设置");
		try {
			if (conn != null) {
				if (conn.getAutoCommit()) {
					conn.setAutoCommit(false);
				} else {
					conn.setAutoCommit(true);
				}
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	// 发生异常回滚事务
	public static void rollback(Connection conn) {
		System.out.println("回滚设置");
		try {
			if (conn != null) {
				conn.rollback();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	// 关闭连接,并将其从当前线程删除
	public static void close() {
		System.out.println("关闭删除");
		Connection conn = threadConn.get();
		if (conn != null) {
			try {
				conn.close();
				conn = null;
				threadConn.remove();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}

2.2数据库事务控制的动态代理类

public class DaoProxy implements MethodInterceptor {

	public Object intercept(Object object, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
		// 用于接收参数
		// 如果是以下方法开头,则代理事务
		Object result=null;
		if (method.getName().startsWith("add") ||
			method.getName().startsWith("del")||
			method.getName().startsWith("update")) {
			Connection conn = JDBCUtil.getConnection();
			try {
				// 手动提交事务
				JDBCUtil.benigTransction(conn);
				result=proxy.invokeSuper(object, objects);
				// 提交事务
				JDBCUtil.endTransction(conn);
			} catch (Exception e) {
				e.printStackTrace();
				// 回滚事务
				JDBCUtil.rollback(conn);
			} finally {
				// 还原状态
				JDBCUtil.recoverTransction(conn);
				JDBCUtil.close();
			}
		}else {
			result=proxy.invokeSuper(object, objects);
		}
		return result;
	}

}

2.3对sql提交的基类

public class BaseDao {

	public int insert(String sql,List<Object> values) throws SQLException {
		Connection con=JDBCUtil.getConnection();
		PreparedStatement pre=con.prepareStatement(sql);
		for(int index=0;index<values.size();index++) {
		   Object obj=values.get(index);
		   if(obj.getClass().isInstance(Integer.class)) {
			   pre.setInt(index+1, (Integer)obj);
		   }else if(obj.getClass().isInstance(Double.class)) {
			   pre.setDouble(index+1, (Double)obj);
		   }else if(obj.getClass().isInstance(BigDecimal.class)) {
			   pre.setBigDecimal(index+1, (BigDecimal)obj);
		   }else if(obj.getClass().isInstance(java.sql.Date.class)) {
			   pre.setDate(index+1, (java.sql.Date)obj);
		   }else if(obj.getClass().isInstance(java.util.Date.class)) {
			   pre.setDate(index+1, new java.sql.Date(((java.util.Date)obj).getTime()));
		   }else {
			   pre.setString(index+1, obj.toString());
		   }
		}
		return pre.executeUpdate();
	}
}

2.4数据添加的测试DAO

public class DAOTest extends BaseDao{

	public void addTest(String test) throws SQLException {
		String id=UUID.randomUUID().toString().replace("-", "");
		String addSql="insert into TEST (ID,TEST) values (?,?)";
		List<Object> values=new ArrayList<Object>();
		values.add(id);
		values.add(test);
        this.insert(addSql, values);
	}
	
	
	public void addTestError(String test) throws SQLException {
		String id=UUID.randomUUID().toString().replace("-", "");
		String addSql="insert into TEST (ID,TEST) values (?,?)";
		List<Object> values=new ArrayList<Object>();
		values.add(id);
		values.add(test);
        this.insert(addSql, values);
        throw new SQLException("回滚测试");
	}
}

2.4效果

public class TestDao {

	public static void main(String[] args) throws SQLException {
		DaoProxy daoProxy = new DaoProxy();
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(DAOTest.class);
		enhancer.setCallback(daoProxy);
		DAOTest dt=(DAOTest)enhancer.create();
		dt.addTest("123");
		dt.addTestError("456");
	}
}

image.png

image.png