纯JDBC、Hibernate、Spring的AOP宣言式事务管理小结

纯JDBC、Hibernate、Spring的AOP声明式事务管理小结

引言:

最近在中心的课程学到了Spring框架的声明式事务管理章节,今天敲个小例子将我目前所知道的三种事务的管理方式做一个简单的对比,顺便巩固一下基础知识。

三种方式:纯JDBC、Hibernate、 Spring的AOP声明式事务管理。

都用相同的需求:在一个方法中一次保存两个用户信息,需要保证都成功插入数据库。否则,一方异常或失败,数据回滚至原始状态。比较典型的示例应该是银行转账业务。

 

(一)纯JDBC

 

纯JDBC主要是在持久化操作之前将数据提交事务的模式更改为不自动提交,

关键代码:conn.setAutoCommit(false);   默认情况下,新连接处于自动提交模式。

在数据库关闭之前,如果一切操作正常,显示提交事务:conn.commit();

如果发生了异常,显示回滚事务:conn.rollback();

 

/**
 * conn.setAutoCommit(true|false)
 * 纯JDBC事务管理方式测试类
 */
package com.accp.transactiontest.test;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.ParseException;

import com.accp.transactiontest.dao.base.ConnectionManager;
import com.accp.transactiontest.entity.Userinfo;

/**
 * @author Maxpin by 2011-07-25
 * 
 */
public class JDBCTest {
	/**
	 * 一次必须保存两个用户信息的方法
	 * 
	 * @param user1
	 *            用户1
	 * @param user2
	 *            用户2
	 */
	public static void saveUserinfo(Userinfo user1, Userinfo user2) {
		Connection conn = null;
		PreparedStatement pstmt = null;
		try {
			// 获取数据库连接
			conn = ConnectionManager.getConnection();
			/*
			 * 设置不使用自动提交事务的方式
			 */
			conn.setAutoCommit(false);
			// 用户1
  			String sql = "insert into USERINFO(USERID,USERNAME,AGE,POINT) values(SEQ_ID.NEXTVAL,?,?,?)"; // 构建SQL语句
			pstmt = conn.prepareStatement(sql); // 创建命令对象
			pstmt.setString(1, user1.getUsername()); // 设置占位符参数
			pstmt.setShort(2, user1.getAge());
			pstmt.setString(3, user1.getPoint());
			pstmt.executeUpdate(); // 执行操作
			// 用户2
			sql = "insert into USERINFO(USERID,USERNAME,AGE,POINT) values(SEQ_ID.NEXTVAL,?,?,?)";
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, user2.getUsername());
			pstmt.setShort(2, user2.getAge());
			pstmt.setString(3, user2.getPoint());
			pstmt.executeUpdate();
			/*
			 * 显示提交事务
			 */
			conn.commit();
		} catch (Exception ex) {
			ex.printStackTrace();
			try {
				/*
				 * 异常则显示回滚事务
				 */
				conn.rollback();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		} finally {
			// 关闭数据库连接
			ConnectionManager.closeConnection(null, pstmt, conn);
		}
	}

	/**
	 * main方法
	 * 
	 * @param args
	 * @throws ParseException
	 */
	public static void main(String[] args) throws ParseException {
		saveUserinfo(new Userinfo("程七七1", (short) 21, "1,3"), 
					 new Userinfo("程七七2", (short) 22, "4,2"));
	}
}

 

  

(二)Hibernate

 

Hibernate在持久化操作增、删、改时需要显示开启一个事务Transaction tx = session.beginTransaction(); (查询可不用)。

对于单独的事务管理方面来说,Hibernate同样需要显示提交事务tx.commit();,显示回滚事务tx.rollback();

 

如下Demo的main方法里的 用户2 的名字设置大于数据库中的字段最大值,因此会有异常。try/catch中捕获异常后显示回滚,正好做到需求描述的要求。

 

/**
 * tx.commit()|tx.rollback();
 * Hibernate事务管理方式测试类
 */
package com.accp.transactiontest.test;

import org.hibernate.Session;
import org.hibernate.Transaction;

import com.accp.transactiontest.dao.base.HibernateUtil;
import com.accp.transactiontest.entity.Userinfo;

/**
 * @author Maxpin by 2011-07-25
 * 
 */
public class HibernateTest {
	/**
	 * 一次必须保存两个用户信息的方法
	 * 
	 * @param user1
	 *            用户1
	 * @param user2
	 *            用户2
	 */
	public static void saveUserinfo(Userinfo user1, Userinfo user2) {
		Session session = HibernateUtil.getSession(); // 得到Session
		Transaction tx = session.beginTransaction();// 显示开启一个事务
		try {
			session.save(user1); // 保存用户1
			session.save(user2); // 保存用户2
			/*
			 * 显示提交事务
			 */
			tx.commit();
		} catch (Exception ex) {
			ex.printStackTrace();
			/*
			 * 异常则显示回滚事务
			 */</span>
			tx.rollback();
		} finally {
			HibernateUtil.closeSession(); // 关闭Session
		}
	}

	/**
	 * main方法
	 * @param args
	 */
	public static void main(String[] args) {
		saveUserinfo(new Userinfo("周八八1", (short) 21, "1,3"), 
					 new Userinfo("周八八八八八八八八八2", (short) 22, "4,2"));
	}

}

 

 

(三)Spring的AOP

 

这里只贴出了 Spring 框架的 applicationContext.xml配置文件中的代码段。(其他JavaBean略)

通过使用代理拦截所有的方法调用。如果方法名位于事务配置中,代理就是起到通知的作用。

它会在目标方法调用前开启事务,然后在一个 try/catch 块中执行目标方法。

如果目标方法正常完成,代理就会提交事务;

如果目标方法抛出运行时异常,代理就会进行回滚。

 

配置如下:

 

<!-- 1、导入tx和aop命名空间 -->
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:tx="http://www.springframework.org/schema/tx" 
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/tx
		http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

  

<!-- 2、为txManager(HibernateTransactionManager)注入sessionFaction -->
	<bean id="txManager"
		class="org.springframework.orm.hibernate3.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory" />
	</bean>
	<!-- 3、定义txManager的事务通知txAdvice -->
	<tx:advice id="txAdvice" transaction-manager="txManager">
		<!-- 定义属性,声明事务规则 -->
		<tx:attributes>
			<!-- 查询操作为只读事务 -->
			<tx:method name="get*" read-only="true" />
			<!-- 增删改及其他操作没有事务就创建新事务,否则用已有事务 -->
			<tx:method name="save*" propagation="REQUIRED" />
			<tx:method name="*" propagation="REQUIRED" read-only="true" />
		</tx:attributes>
	</tx:advice>
	<!-- aop面相切面编程的配置文件 -->
	<aop:config>
		<!-- 设置切入点 -->
		<aop:pointcut id="bizMethod" expression="execution(* com.accp.transactiontest2.biz.impl.*.*(..))" />
		<!-- 组合事务通知和切入点 -->
		<aop:advisor advice-ref="txAdvice" pointcut-ref="bizMethod"/>
	</aop:config>