现在的位置: 首页 > 综合 > 正文

Sessions and transactions

2018年01月25日 ⁄ 综合 ⁄ 共 26218字 ⁄ 字号 评论关闭

This page explains common techniques to deal with theSession and transactions in Hibernate applications. Refer to the Hibernate reference documentation and the "Transactions and Concurrency" chapter for more information.
This page describes Hibernate 3.1.x and code shown here does not work in older versions.

 

Unit of Work

A particular unit of work is grouping data access operations. We usually refer to the HibernateSession as a unit of work because the scope of a
Session is exactly that, in almost all cases. (TheSession is also many other things, for example, a cache and a primary API.) To begin a unit of work you open aSession. To end a unit of work you close a
Session. Usually you alsoflush a Session at the end of a unit of work to execute the SQL DML operations (UPDATE, INSERT, DELETE) that synchronize the in-memorySession state with the database.

 

A Session executes also SQL queries, whenever the developer triggers a query with the API or through loading on demand (lazy loading). Alternatively, think of theSession as a gateway to your database, a map of
managed entity instances that are automatically dirty checked, and a queue of SQL DML statements that are created and flushed by Hibernate automatically.

 

Transactions

Transactions also group data access operations, in fact, every SQL statement, be it queries or DML, has to execute inside a database transaction. There can be no communication with a database outside of a database transaction.
(Note that there are such things as read-only transactions, that can be used to improve cleanup time in a database engine if it is not smart enough to optimize its own operations.)

 

One approach is the auto-commit mode, where every single SQL statement is wrapped in a very short transaction. This mode is never appropriate for an application, but only for ad-hoc execution of SQL with an operator console. Hibernate
disables or expects the environment (in J2EE/JEE) to disable auto-commit mode, as applications are not executing ad-hoc SQL but a planned sequence of statements. (There are ways to enable auto-commit behavior in Hibernate but it is by definition slower than
regular transactions and less safe. If you want to know more about auto-commit mode, readthis.)

 

The right approach is to define clear transaction boundaries in your application by beginning and committing transactions either programmatically, or if you have the machinery to do this, declaratively (e.g. on service/command
methods). If an exception occurs the transaction has to be rolled back (or declaratively, is rolled back).

 

The scope of a unit of work

A single Hibernate Session might have the same scope as a single database transaction.

 

This is the most common programming model used for thesession-per-request implementation pattern. A single
Session and a single database transaction implement the processing of a particular request event (for example, a Http request in a web application). Donever use the
session-per-operation anti-pattern! (There are extremely rare exceptions when session-per-operation might be appropriate, you will not encounter these if you are just learning Hibernate.)

 

Another programming model is that of long Conversations, e.g. an application that implements a multi-step dialog, for example awizard dialog, to interact with the user in several request/response cycles.

 

One way to implement this is the session-per-request-with-detached-objects pattern. Once persistent objects are considereddetached during user think-time and have to be reattached to a new
Session after they have been modified.

 

 

 

The session-per-conversation pattern is however recommended. In this case a singleSession has a bigger scope than a single database transaction and it might span several database transactions. Each request event
is processed in a single database transaction, but flushing of theSession would be delayed until the end of the conversation and the last database transaction, to make the conversation atomic. TheSession is held in disconnected state, with
no open database connection, during user think-time. Hibernate's automatic optimistic concurrency control (with versioning) is used to provide conversation isolation.

 

 

Hibernate supports several convenience APIs that make implementation of all transaction and conversation strategies easier, with any transaction processing system you might deploy on.

 

Transaction demarcation with JTA

Hibernate works in any environment that uses JTA, in fact, we recommend to use JTA whenever possible as it is the standard Java transaction interface. You get JTA built-in with all J2EE/JEE application servers, and eachDatasource
you use in such a container is automatically handled by a JTATransactionManager. But this is not the only way to get JTA, you can use a standalone implementation (e.g.JOTM)
in any plain JSE environment. Another example is
JBoss Seam
, it comes bundled with a demo application that uses an embeddable version of the JBoss JCA/JTA/JNDI services, hence provides JTA in any deployment situation.

 

Hibernate can automatically bind the "current"
Session
to the current JTA transaction. This enables an easy implementation of the session-per-request strategy with the
getCurrentSession() method on yourSessionFactory:

try {
    UserTransaction tx = (UserTransaction)new InitialContext()
                            .lookup("java:comp/UserTransaction");
                            
    tx.begin();
 
    // Do some work
    factory.getCurrentSession().load(...);
    factory.getCurrentSession().persist(...);
 
    tx.commit();
}
catch (RuntimeException e) {
    tx.rollback();
    throw e; // or display error message
}

 

The advantage of the built-in support should become clear as soon as you write non-trivial applications: you can separate the transaction demarcation code from your data access code. The "current session" refers to a HibernateSession
bound by Hibernate behind the scenes, to the transaction scope. ASession is opened when
getCurrentSession() is called for the first time and closed when the transaction ends. It is also flushed automatically before the transaction commits. You can callgetCurrentSession() as often and anywhere you want as long as the transaction
runs. To enable this strategy in your Hibernate configuration:

  • set hibernate.transaction.manager_lookup_class to a lookup strategy for your JEE container
  • set hibernate.transaction.factory_class to org.hibernate.transaction.JTATransactionFactory

 

See the Hibernate reference documentation for more configuration details.

 

This does not mean that all Hibernate Sessions are closed when a transaction is committed! Only the Session that you obtained withsf.getCurrentSession() is flushed and closed automatically. If you decide to usesf.openSession()
and manage the Session yourself, you have to flush() and close() it. So a less convenient alternative, without any "current"Session, is this:

UserTransaction tx = (UserTransaction)new InitialContext()
                            .lookup("java:comp/UserTransaction");
 
Session session = factory.openSession();
 
try {
    tx.begin();
 
    // Do some work
    session.load(...);
    session.persist(...);
 
    session.flush();
 
    tx.commit();
}
catch (RuntimeException e) {
    tx.rollback();
    throw e; // or display error message
}
finally {
    session.close();
}

 

If you manage the Session yourself, code is more difficult to layer. You can't easily move data access operations into a different layer than transaction andSession demarcation.

 

Transaction demarcation with plain JDBC

If you don't have JTA and don't want to deploy it along with your application, you will usually have to fall back to JDBC transaction demarcation. Instead of calling the JDBC API you better use Hibernate'sTransaction and
the built-in session-per-request functionality:

try {
    factory.getCurrentSession().beginTransaction();
 
    // Do some work
    factory.getCurrentSession().load(...);
    factory.getCurrentSession().persist(...);
 
    factory.getCurrentSession().getTransaction().commit();
}
catch (RuntimeException e) {
    factory.getCurrentSession().getTransaction().rollback();
    throw e; // or display error message
}

 

Because Hibernate can't bind the "current session" to a transaction, as it does in a JTA environment, it binds it to the current Java thread. It is opened whengetCurrentSession() is called for the first time, but in a
"proxied" state that doesn't allow you to do anything except start a transaction. When the transaction ends, either through commit or roll back, the "current"Session is closed automatically. The next call to
getCurrentSession() starts a new proxiedSession, and so on. In other words, the session is bound to the thread behind the scenes, but scoped to a transaction, just like in a JTA environment. This thread-bound strategy works in every JSE application
- note that you should use JTA and a transaction-bound strategy in a JEE environment (or install JTA with your JSE application, this is a modular service).

 

To enable the thread-bound strategy in your Hibernate configuration:

  • set hibernate.transaction.factory_class to org.hibernate.transaction.JDBCTransactionFactory
  • set hibernate.current_session_context_class to thread

 

This does not mean that all Hibernate Sessions are closed when a transaction is committed! Only the Session that you obtained withsf.getCurrentSession() is flushed and closed automatically. If you decide to usesf.openSession()
and manage the Session yourself, you have to close() it. So a less convenient alternative, without any "current"
Session, is this:

Session session = factory.openSession();
Transaction tx = null;
try {
    tx = session.beginTransaction();
 
    // Do some work
    session.load(...);
    session.persist(...);
 
    tx.commit(); // Flush happens automatically
}
catch (RuntimeException e) {
    tx.rollback();
    throw e; // or display error message
}
finally {
    session.close();
}

 

If you manage the Session yourself, code is more difficult to layer. You can't easily move data access operations into a different layer than transaction andSession demarcation.

 

Transaction demarcation with EJB/CMT

Our goal really is to remove any transaction demarcation code from the data access code:

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void doSomeWork() {
    // Do some work
    factory.getCurrentSession().load(...);
    factory.getCurrentSession().persist(...);
}

 

Instead of coding the begin, commit, and rollback of your transactions into your application you could use a declarative approach. For example, you might declare that some of your service or command methods require a database transaction
to be started when they are called. The transaction ends when the method returns; if an exception is thrown, the transaction will be rolled back. The Hibernate "current"Session has the some scope as the transaction (flushed and closed at commit) and
is internally also bound to the transaction. It propagates into all components that are called in one transactions.

 

Declarative transaction demarcation is a standard feature of EJB, also known as container-managed transactions (CMT). In EJB 2.x you would use XML deployment descriptors to create your transaction assembly. In EJB 3.x you can use
JDK 5.0 annotation metadata directly in your source code, a much less verbose approach. To enable CMT transaction demarcation for EJBs in Hibernate configuration:

  • set hibernate.transaction.manager_lookup_class to a lookup strategy for your JEE container
  • set hibernate.transaction.factory_class toorg.hibernate.transaction.CMTTransactionFactory

 

Custom transaction interceptors

To remove transaction demarcation from your data access code you might want to write your own interceptor that can begin and end a transaction programmatically (or even declaratively). This is a lot easier than it sounds, after
all, you only have to move three methods into a different piece of code that runs every time a request has to be processed. Of course more sophisticated solutions would also need to handle transaction propagation, e.g. if one service method calls another one.
Typical interceptors are a servlet filter, or an AOP interceptor that can be applied to any Java method or class.

For an implementation with a servlet filter see
Open Session in View
.

For an implementation with JBoss AOP see
Session handling with AOP
.

 

Implementing long Conversations

If you'd like to design your application with a
session-per-conversation
strategy, you need to manage the "current"Session yourself. An example with a servlet filter is shown with the
Open Session in View pattern.

 

Implementing data access objects (DAOs)

Writing DAOs that call Hibernate is incredibly easy and trivial. You don't need a framework. You don't need to extend some "DAOSupport" superclass from a proprietary library. All you need to do is keep your transaction demarcation
(begin and commit) as well as any Session handling code outside of the DAO implementation. For example, aProductDAO class has a
setCurrentSession() method or constructor, or it looks up the "current" HibernateSession internally. Where this current
Session comes from is not the responsibility of the DAO! How a transaction begins and ends is
not the responsibility of the DAO! All the data access object does is use the currentSession to execute some persistence and query operations. For a pattern that follows these rules, seeGeneric
Data Access Objects
.

 

What about the
SessionFactory
?

In the examples above you can see access to the
SessionFactory
. How do you get access to the factory everywhere in your code? Again, if you run in a JEE environment, or use an embedded service in JSE, you could simply look it up from JNDI, where Hibernate can bind it on startup. Another solution is
to keep it in a global static singleton after startup. You can in fact solve both the problem ofSessionFactory lookup and Hibernate startup with the same piece of code, a trivial helper class (this is from the tutorial in chapter 1, Hibernate reference
documentation):

 

public class HibernateUtil {
 
    private static final SessionFactory sessionFactory;
 
    static {
        try {
            // Create the SessionFactory from hibernate.cfg.xml
            sessionFactory = new Configuration().configure().buildSessionFactory();
        } catch (Throwable ex) {
            // Make sure you log the exception, as it might be swallowed
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }
 
    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
 
}

 

A more sophisticated version if HibernateUtil that can also switch automatically between JNDI and static singleton can be found inthe
CaveatEmptor demo application
.

Note: There are many variations of much more complex HibernateUtil classes floating around the net. However, for Hibernate 3.1, the code shown above is the only code that is needed. If you use JNDI you might want to have a
look at HibernateUtil in the latest CaveatEmptor. Every other HibernateUtil is obsolete for Hibernate 3.1.

 

This is all very difficult, can't this be done easier?

Hibernate can only do so much as a persistence service, managing the persistence service is however the responsibility of the application infrastructure, or framework. The EJB3 programming model makes transaction and persistence
context management very easy, use the
Hibernate EntityManager
to get this API. Either run your EJBs inside a full J2EE application server (previews available from several vendors) or in a lightweight embeddable EJB3 container,JBoss
Embeddable EJB3
, in any Java environment. The
JBoss Seam
framework has built-in support for automatic context management, including persistence and conversations, with only a few annotations in your source code.

Ibernate中的Session
  Session是JAVA应用程序和Hibernate进行交互时使用的主要接口,它也是持久化操作核心API, 
  注意这里的Session的含义,它与传统意思上web层的HttpSession并没有关系,Hibernate Session之与Hibernate,相当于JDBC Connection相对与JDBC。 
  Session对象是有生命周期的,它以Transaction对象的事务开始和结束边界 
  Session作为贯穿Hibernate的持久化管理器核心,提供了众多的持久化的方法,如save(), update ,delete ,find(Hibernate 3中已经取消了此方法)等,通过这些方法我们可以透明的完成对象的增删改查(CRUD-- create read update delete),这里所谓的透明是指,Session在读取,创建和删除影射的实体对象的实例时,这一系列的操作将被转换为对数据库表中数据的增加,修改,查询和删除操作。

       SessionFactory负责创建Session,SessionFactory是线程安全的,多个并发线程可以同时访问一个SessionFactory 并从中获取Session实例。而Session并非线程安全,也就是说,如果多个线程同时使用一个Session实例进行数据存取,则将会导致Session 数据存取逻辑混乱.因此创建的Session实例必须在本地存取空上运行,使之总与当前的线程相关。


Session有以下的特点
  1,不是线程安全的,应该避免多个线程共享同一个Session实例 
  2,Session实例是轻量级的,所谓轻量级:是指他的创建和删除不需要消耗太多资源 
  3,Session对象内部有一个缓存,被称为Hibernate第一缓存,他存放被当前工作单元中加载的对象,每个Session实例都有自己的缓存。

Hibernate Session缓存被称为Hibernate的第一级缓存。SessionFactory的外置缓存称为Hibernate的二级缓存。这两个缓存都位于持久层,它们存放的都是数据库数据的拷贝。SessionFactory的内置缓存 存放元数据和预定义SQL, SessionFactory的内置缓存是只读缓存。

Hibernate Session缓存的三大作用:
1,减少数据库的访问频率,提高访问性能。
2,保证缓存中的对象与数据库同步,位于缓存中的对象称为持久化对象。
3,当持久化对象之间存在关联时,Session 保证不出现对象图的死锁。
Session 如何判断持久化对象的状态的改变呢?
Session 加载对象后会为对象值类型的属性复制一份快照。当Session 清理缓存时,比较当前对象和它的快照就可以知道那些属性发生了变化。

Session 什么时候清理缓存?
1,commit() 方法被调用时
2,查询时会清理缓存,保证查询结果能反映对象的最新状态。
3,显示的调用session 的 flush方法。
session 清理缓存的特例:
当对象使用 native 生成器 时 会立刻清理缓存向数据库中插入记录。


org.hibernate Interface Session
  public interface Session extends Serializable : 是一个Java application 和Hibernate之间主要的运行时接口,这是执行持久化服务的中心API 
  主要方法: 
  public Transaction beginTransaction() throws HibernateException【1】:返回和当前Session对象相互联系的Transaction对象(表示在数据库中重新开始一个事务) 
  public Transaction getTransaction(): 返回和当前session联系的Transaction对象 
  public Connection connection close() throws HibernateExcepton:结束当前的Session对象 
  public void clear() :清空Session,清除所有保存在当前Session缓存中的实体对象,终止所有正在执行的方法(eg: save() , update() ,delete() .....) 
  public Serializable save(Object object)throws HibernateException 对当前参数指定的对象进行持久化(系统会首先赋予参数对象一个标识符OID),他相当于insert语句 后面在详细介绍 
  public Connection connection() throws HibernateException 得到当前Session 中包含的Connection对象。 
  public boolean contains(Object object):判断参数给出的对象(持久化类)是否在当前Session的缓存中 
  public void evict(Object object) throws HibernateException :将参数给出的Object从当前Session对象类中删除,使这个对象从持久态变成游离态,这种状态的改变不会引起对数据库的同步,后面详细介绍 
  public Object load(Class theclass ,Serializable id) throws HibernateException 返回第一个参数指定类对应的表中,第二个参数指定的行(第二个参数就是要取得对象的OID,他对应表中主键列的值) 
  public void update(Object object) throws HibernateException :更新一个对象到数据库中,后面在详细介绍 
  public void delete (Object object)throws HibernateException:从数据库中删除和参数指定的对象对应的记录 
  public Object get(Class class,Serializable id) throws HibernateException:和load()方法一样区别在于,如果数据库表中没有对应的记录,get()方法返回null,load()方法将报异常
Transaction
  Transanction接口是Hibernate的数据库事务接口,用于管理事务,他对底层的事务作出了封装,用户可以使用Transanction对象定义自己的对数据库的原子操作,底层事务包括:JDBC API ,JTA(Java Transaction API)。。。。。 
  一个Transaction对象的事务可能会包括多个对数据库进行的操作 
  org.hibernate Interface Transaction 
  public interface Transaction
常用方法:
  public void commit() throws HibernateException 刷新当前的Session以及结束事务的工作,这个方法将迫使数据库对当前的事务进行提交 
  public void rollback() throws HibernateException :强迫回滚当前事务 
  public boolean isActive() throws HibernateException: 这个事务是否存活 
  ----------------------------------------------------------------------------------------


  Session:当中包含一个Connection对象 
  Connection c =session.getConnection(); 
  Session的缓存用于临时保存持久化的对象,等到一定时候,再将缓存中的对象保存到数据库中。 
  应用程序事务:如果一个Session中包含有多个Transaction(数据库事务),这些Transaction的集合称为应用程序事务 
  标准使用形式: 
  Configuration config=new Configuration().configure("hibernate.cfg.xml"); 
  SessionFactory sessionfactory=config.buildSessionFactory(); 
  Session session=sessionfactory.openSession(); 
  Transaction tx=session.beginTransaction(); 
  try 
  { 
  session.save(); 
  tx.commit(); 
  } 
  catch(Exception e) 
  { 
  if(tx!=null) tx.rollback(); 
  } 
  finally 
  { 
  session.close (); 
  }

 

保证session的线程安全

ThreadLocal,在很多种Session 管理方案中都用到了它.ThreadLocal 是Java中一种较为特殊的线程绑定机制,通过ThreadLocal存取的数据,总是与当前线程相关,也就是说,JVM 为每个运行的线程,绑定了私有的本地实例存取空间,从而为多线程环境常出现的并发访问问题提供了一种隔离机制,ThreadLocal并不是线程本地化的实现,而是线程局部变量。

也就是说每个使用该变量的线程都必须为该变量提供一个副本,每个线程改变该变量的值仅仅是改变该副本的值,而不会影响其他线程的该变量的值,ThreadLocal是隔离多个线程的数据共享,不存在多个线程之间共享资源,因此不再需要对线程同步。

请看一下代码:
public class HibernateUtil {  
 
public static final SessionFactory sessionFactory;  
public static final ThreadLocal session = new ThreadLocal();  
static{  
try{  
Configuration configuration=new Configuration().configure();   
sessionFactory = configuration.buildSessionFactory();  
}catch (Throwable ex){  
System.err.println("Initial SessionFactory creation failed." + ex);  
throw new ExceptionInInitializerError(ex);  
}  
}  
public static Session currentSession() throws HibernateException{  
Session s = (Session) session.get();  
if (s == null)  
{  
s = sessionFactory.openSession();  
session.set(s);  
}  
return s;  
}  
 
public static void closeSession() throws HibernateException {  
Session s = (Session) session.get();  
if (s != null)  
s.close();  
session.set(null);  
}  
}

     不安全解释,例如:Servlet 运行是多线程的,而应用服务器并不会为每个线程都创建一个Servlet实例,也就是说,TestServlet在应用服务器中只有一个实例(在Tomcat中是这样,其他的应用服务器可能有不同的实现),而这个实例会被许多个线程并发调用,doGet 方法也将被不同的线程反复调用,可想而知,每次调用doGet 方法,这个唯一的TestServlet 实例的session 变量都会被重置

      在代码中,只要借助上面这个工具类获取Session 实例,我们就可以实现线程范围内的Session 共享,从而避免了在线程中频繁的创建和销毁Session 实例。不过注意在线程结束时关闭Session。同时值得一提的是,新版本的Hibernate在处理Session的时候已经内置了延迟加载机制,只有在真正发生数据库操作的时候,才会从数据库连接池获取数据库连接,我们不必过于担心Session的共享会导致整个线程生命周期内数据库连接被持续占用。

对于Web程序
而言,我们可以借助Servlet2.3规范中新引入的Filter机制,轻松实现线程生命周期内的Session管理(关于Filter的具体描述,请参考Servlet2.3规范)。Filter的生命周期贯穿了其所覆盖的Servlet(JSP也可以看作是一种特殊的Servlet)
及其底层对象。Filter在Servlet被调用之前执行,在Servlet调用结束之后结束。因此,在Filter 中管理Session 对于Web 程序而言就显得水到渠成。下面是一个通过Filter 进行Session管理的典型案例:

Java代码

   1. public class PersistenceFilter implements Filter  
   2. {  
   3. protected static ThreadLocal hibernateHolder = new ThreadLocal();  
   4. public void doFilter(ServletRequest request, ServletResponse  
   5. response, FilterChain chain)  
   6. throws IOException, ServletException  
   7. {  
   8. hibernateHolder.set(getSession());  
   9. try  
  10. {  
  11. ......  
  12. chain.doFilter(request, response);  
  13. ......  
  14. }  
  15. finally  
  16. {  
  17. Session sess = (Session)hibernateHolder.get();  
  18. if (sess != null)  
  19. {  
  20. hibernateHolder.set(null);  
  21. try  
  22. {  
  23. sess.close();  
  24. }  
  25. catch (HibernateException ex) {  
  26. throw new ServletException(ex);  
  27. }  
  28. }  
  29. }  
  30. }  
  31. ......  
  32. Hibernate Developer's Guide Version 1.0  
  33. September 2, 2004 So many open source projects. Why not Open your Documents?  
  34. } 

通过在doFilter中获取和关闭Session,并在周期内运行的所有对象(Filter链中其余的Filter,及其覆盖的Servlet 和其他对象)对此Session 实例进行重用,保证了一个Http Request处理过程中只占用一个Session,提高了整体性能表现。在实际设计中,Session的重用做到线程级别一般已经足够,企图通过HttpSession实现用户级的Session重用反而可能导致其他的问题。凡事不能过火,Session重用也一样。 

 

Hibernate中的Transaction

Hibernate是对JDBC的轻量级对象封装,Hibernate本身是不具备Transaction处理功能的,Hibernate的Transaction实际上是底层的JDBC Transaction的封装,或者是JTA Transaction的封装,下面我们详细的分析:

Hibernate可以配置为JDBCTransaction或者是JTATransaction,这取决于你在hibernate.properties中的配置:

引用
#hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory 
#hibernate.transaction.factory_class net.sf.hibernate.transaction.JDBCTransactionFactory
如果你什么都不配置,默认情况下使用JDBCTransaction,不管你准备让Hibernate使用JDBCTransaction,还是JTATransaction,我的忠告就是什么都不配,将让它保持默认状态,如下: 
在下面的分析中我会给出原因。

一、JDBC Transaction

看看使用JDBC Transaction的时候我们的代码例子:

Java代码  
Session session = sf.openSession();;   
Transaction tx = session.beginTransactioin();;   
...   
session.flush();;   
tx.commit();;   
session.close();;  
这是默认的情况,当你在代码中使用Hibernate的Transaction的时候实际上就是JDBCTransaction。那么JDBCTransaction究竟是什么东西呢?来看看源代码就清楚了:

Hibernate2.0.3源代码中的类
Java代码  
net.sf.hibernate.transaction.JDBCTransaction:   
 public void begin(); throws HibernateException {   
    log.debug("begin");;       
    try {   
        toggleAutoCommit = session.connection();.getAutoCommit();;   
        if (toggleAutoCommit); session.connection();.setAutoCommit(false);;   
    }   
    catch (SQLException e); {   
        log.error("Begin failed", e);;   
        throw new TransactionException("Begin failed with SQL exception: ", e);;   
    }      
    begun = true;   

这是启动Transaction的方法,看到 connection().setAutoCommit(false) 了吗?是不是很熟悉? 
public void commit(); throws HibernateException {      
    if (!begun); throw new TransactionException("Transaction not successfully started");;      
    log.debug("commit");;      
    try {   
        if ( session.getFlushMode();!=FlushMode.NEVER ); session.flush();;   
        try {   
            session.connection();.commit();;   
            committed = true;   
        }   
        catch (SQLException e); {   
            log.error("Commit failed", e);;   
            throw new TransactionException("Commit failed with SQL exception: ", e);;   
        }   
    }   
    finally {   
        session.afterTransactionCompletion();;   
    }   
    toggleAutoCommit();;   

这是提交方法,看到connection().commit() 了吗?下面就不用我多说了,这个类代码非常简单易懂,通过阅读使我们明白Hibernate的Transaction都在干了些什么?我现在把用Hibernate写的例子翻译成JDBC,大家就一目了然了:

Java代码  
Connection conn = ...;         <---   session = sf.openSession();;   
conn.setAutoCommit(false);;     <---   tx = session.beginTransactioin();;   
...                            <---   ...   
conn.commit();;                 <---   tx.commit();; (对应左边的两句);   
conn.setAutoCommit(true);;   
conn.close();;                  <---   session.close();; 

看明白了吧,Hibernate的JDBCTransaction根本就是conn.commit而已,根本毫无神秘可言,只不过在Hibernate中,Session打开的时候,就会自动conn.setAutoCommit(false),不像一般的JDBC,默认都是true,所以你最后不写commit也没有关系,由于Hibernate已经把AutoCommit给关掉了,所以用Hibernate的时候,你在程序中不写Transaction的话,数据库根本就没有反应。


二、JTATransaction

如果你在EJB中使用Hibernate,或者准备用JTA来管理跨Session的长事务,那么就需要使用JTATransaction,先看一个例子:

Java代码  
javax.transaction.UserTransaction tx = new InitialContext().lookup("javax.transaction.UserTransaction");  
Session s1 = sf.openSession();
...   
s1.flush();
s1.close();   
...   
Session s2 = sf.openSession();   
...   
s2.flush();   
s2.close();   
tx.commit(); 

这是标准的使用JTA的代码片断,Transaction是跨Session的,它的生命周期比Session要长。如果你在EJB中使用Hibernate,那么是最简单不过的了,你什么Transaction代码统统都不要写了,直接在EJB的部署描述符上配置某某方法是否使用事务就可以了。

现在我们来分析一下JTATransaction的源代码, net.sf.hibernate.transaction.JTATransaction:

Java代码  
public void begin(InitialContext context, ...   
  ...   
  ut = (UserTransaction); context.lookup(utName);;   
  ... 

看清楚了吗? 和我上面写的代码 tx = new InitialContext().lookup("javax.transaction.UserTransaction"); 是不是完全一样?

Java代码  
public void commit(); ...   
  ...   
  if (newTransaction); ut.commit();;   
  ...  
JTATransaction的控制稍微复杂,不过仍然可以很清楚的看出来Hibernate是如何封装JTA的Transaction代码的。

但是你现在是否看到了什么问题? 仔细想一下,Hibernate Transaction是从Session中获得的,tx = session.beginTransaction(),最后要先提交tx,然后再session.close,这完全符合JDBC的Transaction的操作顺序,但是这个顺序是和JTA的Transactioin操作顺序彻底矛盾的!!! JTA是先启动Transaction,然后启动Session,关闭Session,最后提交Transaction,因此当你使用JTA的Transaction的时候,那么就千万不要使用Hibernate的Transaction,而是应该像我上面的JTA的代码片断那样使用才行。

总结:

1、在JDBC上使用Hibernate

必须写上Hibernate Transaction代码,否则数据库没有反应。此时Hibernate的Transaction就是Connection.commit而已

2、在JTA上使用Hibernate

写JTA的Transaction代码,不要写Hibernate的Transaction代码,否则程序会报错

3、在EJB上使用Hibernate

什么Transactioin代码都不要写,在EJB的部署描述符里面配置 
Java代码  
|---CMT(Container Managed Transaction);   
|   
|---BMT(Bean Managed Transaction);   
        |   
        |----JDBC Transaction   
        |   
        |----JTA Transaction  

你说“Hibernate的JDBCTransaction根本就是conn.commit而已,根本毫无神秘可言,只不过在Hibernate中,Session打开的时候,就会自动conn.setAutoCommit(false),不像一般的JDBC,默认都是true,所以你最后不写commit也没有关系,由于Hibernate已经把AutoCommit给关掉了,所以用Hibernate的时候,你在程序中不写Transaction的话,数据库根本就没有反应” 
但sf.opengSession()时,并没有setAutoCommit(false),我想问的是,如果不编写任何事务代码,如: 
Java代码  
Session s = sf.openSession();;   
......   
s.close();;  
数据库会不会有反应(此时应该是默认AutoCommit为true)。

另外,我想问一下: 
1. s.flush()是不是必须的 
2. s.close()是不是一定要关闭 
比如你上面提到的: 
Java代码  
javax.transaction.UserTransaction tx = new InitialContext();.lookup("javax.transaction.UserTransaction");;    
Session s1 = sf.openSession();;    
...    
s1.flush();;    
s1.close();;    
..
Session s2 = sf.openSession();;    
...    
s2.flush();;    
s2.close();;    
tx.commit();;  
s1不关闭,使用s2进行操作的代码中使用s1可不可以(我觉得这样更加节约资源,不需要反复的连接、关闭)

引用
但sf.opengSession()时,并没有setAutoCommit(false),我想问的是,如果不编写任何事务代码,如: 
Session s = sf.openSession(); 
...... 
s.close(); 
数据库会不会有反应(此时应该是默认AutoCommit为true)。
不会有反应。在sf.openSession() 创建Session实例的时候,就已经调用了conn.setAutoCommit(false)了。 
引用
另外,我想问一下: 
1. s.flush()是不是必须的 
2. s.close()是不是一定要关闭 
s.flush不是必须的,s.close()会调用一次s.flush()

s.close()正常情况下应该关闭,除非你是用ThreadLocal管理Session。 
引用
s1不关闭,使用s2进行操作的代码中使用s1可不可以(我觉得这样更加节约资源,不需要反复的连接、关闭)
在这个例子中看不出来JTA的作用。 
假设 
Java代码  
Class A  {   
  find(); {   
    Session s1 = sf.openSession();;    
    ...    
    s1.flush();;    
    s1.close();;    
  }   


Java代码  
Class B  {   
  find(); {   
    Session s2 = sf.openSession();;    
    ...    
    s2.flush();;    
    s2.close();;    
  }   


Java代码  
Main {   
  
  tx = ...;   
  A.find();;   
  B.find();;   
  tx.commit();;   
}  
看明白了吗?JTA的Transaction管理是跨类调用的。

抱歉!评论已关闭.