1. 程式人生 > >Hibernate事務管理

Hibernate事務管理

User類:

public class User implements Serializable{
	public User(){}
	
	private Integer id;
	private String name;
	private Integer age;
	private static final long serialVersionUID = 1L;
	
	public Integer getId() {
		return id;
	}
	
	public void setId(Integer id) {
		this.id = id;
	}
	
	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public Integer getAge() {
		return age;
	}
	
	public void setAge(Integer age) {
		this.age = age;
	}
}

User.hbm.xml:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
		"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
		"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >

<hibernate-mapping>
    <class name="com.po.User" 
        table="TEST_USER">
        <id name="id" column="id" type="java.lang.Integer">
            <generator class="assigned"/>
        </id>
        <property name="name"
				column="name"
            	type="java.lang.String"
            	not-null="true"
           	 	unique="true"
            	length="20"/>
        <property name="age"
            	column="age"
            	type="java.lang.Integer"
            	not-null="true"
            	unique="false"
            	length="0"/>
    </class>
</hibernate-mapping>

hibernate.cfg.xml:

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
		"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
		"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
	<session-factory>
		<property name="show_sql">true</property>
		
		<property name="hibernate.connection.driver_class">
			oracle.jdbc.driver.OracleDriver
		</property>
		<property name="hibernate.connection.url">
			jdbc:oracle:thin:@192.168.58.1:1521:123
		</property>
		<property name="hibernate.connection.username">123</property>
		<property name="hibernate.connection.password">123</property>
		<property name="dialect">org.hibernate.dialect.OracleDialect</property>
		
		<mapping resource="com/po/User.hbm.xml"/>
	</session-factory>
</hibernate-configuration>

測試類:

public class Test {
	public static void main(String[] args) {
		User user = new User();
		user.setId(1);
		user.setName("111");
		user.setAge(10);
		Configuration conf = new Configuration().configure();
		SessionFactory sf = conf.buildSessionFactory();
		Session sess = sf.openSession();
		Transaction t = sess.beginTransaction();//說明一
		try{
			sess.save(user);
			t.commit();//說明二
		}catch(Exception e){
			t.rollback();
		}finally{
			if(sess.isOpen()){
				sess.close();
			}
		}
	}
}

說明一:Hibernate本身沒有實現自己的事務管理功能,而是對底層JDBC事務或JTA事務的輕量級封裝

org.hibernate.impl.SessionImpl類(該類是會話的實現類):

public Transaction beginTransaction() throws HibernateException {
       errorIfClosed();
    if ( rootSession != null ) {
       log.warn( "Transaction started on non-root session" );
    }
    Transaction result = getTransaction();
    result.begin();
    return result;
}
 
public Transaction getTransaction() throws HibernateException {
    errorIfClosed();
    return jdbcContext.getTransaction();
}

org.hibernate.jdbc.JDBCContext類:

public Transaction getTransaction() throws HibernateException {
    if (hibernateTransaction==null) {
       hibernateTransaction = owner.getFactory().getSettings()
              .getTransactionFactory().createTransaction( this, owner );
    }
    return hibernateTransaction;
}

TransactionFactory有很多實現類:


選擇最基本的org.hibernate.transaction.JDBCTransactionFactory觀察一下:

public Transaction createTransaction(JDBCContext jdbcContext, Context transactionContext)throws HibernateException {
	return new JDBCTransaction( jdbcContext, transactionContext );
}

org.hibernate.transaction.JDBCTransaction類的說明:

Transaction implementation based on transaction management through a JDBC Connection.
This the Hibernate's default transaction strategy.

事務開始時,會禁用自動提交:

public void begin() throws HibernateException {
	if (begun) {
		return;
	}
	if (commitFailed) {
		throw new TransactionException("cannot re-start transaction after failed commit");
	}

	log.debug("begin");

	try {
		toggleAutoCommit = jdbcContext.connection().getAutoCommit();
		if ( log.isDebugEnabled() ) {
			log.debug("current autocommit status: " + toggleAutoCommit);
		}
		if (toggleAutoCommit) {
			log.debug("disabling autocommit");
			jdbcContext.connection().setAutoCommit(false);//關閉事務自動提交
		}
	}
	catch (SQLException e) {
		log.error("JDBC begin failed", e);
		throw new TransactionException("JDBC begin failed: ", e);
	}

	callback = jdbcContext.registerCallbackIfNecessary();

	begun = true;
	committed = false;
	rolledBack = false;

	if ( timeout>0 ) {
		jdbcContext.getConnectionManager().getBatcher().setTransactionTimeout(timeout);
	}

	jdbcContext.afterTransactionBegin(this);
}

開啟log日誌也能看出很多端倪:

[2013-08-09 16:37:55] DEBUG  -> begin
[2013-08-09 16:37:55] DEBUG  -> opening JDBC connection
[2013-08-09 16:37:55] DEBUG  -> current autocommit status: false
[2013-08-09 16:37:55] DEBUG  -> generated identifier: 1, using strategy: org.hibernate.id.Assigned
[2013-08-09 16:37:55] DEBUG  -> commit

說明二不需要顯式的呼叫flush()方法,事務提交時會根據session的FlushMode自動觸發session的flush

還是通過最基本的JDBCTransaction類看一下:

事務提交完成之後又恢復了事務的自動提交

public void commit() throws HibernateException {
	if (!begun) {
		throw new TransactionException("Transaction not successfully started");
	}

	log.debug("commit");

	if ( !transactionContext.isFlushModeNever() && callback ) {
		transactionContext.managedFlush(); //根據FlushMode(通常為AUTO)重新整理Session

	notifySynchronizationsBeforeTransactionCompletion();
	if ( callback ) {
		jdbcContext.beforeTransactionCompletion( this );
	}

	try {
		commitAndResetAutoCommit();//在該方法中重新開啟了事務自動提交
		log.debug("committed JDBC Connection");
		committed = true;
		if ( callback ) {
			jdbcContext.afterTransactionCompletion( true, this );
		}
        notifySynchronizationsAfterTransactionCompletion( Status.STATUS_COMMITTED );
	}
	catch (SQLException e) {
		log.error("JDBC commit failed", e);
		commitFailed = true;
		if ( callback ) {
			jdbcContext.afterTransactionCompletion( false, this );
		}
	    notifySynchronizationsAfterTransactionCompletion( Status.STATUS_UNKNOWN );
		throw new TransactionException("JDBC commit failed", e);
	}
	finally {
		closeIfRequired();
	}
}

private void commitAndResetAutoCommit() throws SQLException {
	try {
		jdbcContext.connection().commit();
	}
	finally {
		toggleAutoCommit();
	}
}

private void toggleAutoCommit() {
	try {
		if (toggleAutoCommit) {
			log.debug("re-enabling autocommit");
			jdbcContext.connection().setAutoCommit( true );//重新開啟事務自動提交
		}
	}
	catch (Exception sqle) {
		log.error("Could not toggle autocommit", sqle);
		//swallow it (the transaction _was_ successful or successfully rolled back)
	}
}

JDBC事務預設自動提交,但是如果Hibernate的事務策略使用JDBC,在事務開始之前,Hibernate會關閉事務自動提交,在事務結束之後,重新開啟事務自動提交