Hibernate的Transaction SessionFactory

来源:互联网 发布:魔力宝贝宠物数据 编辑:程序博客网 时间:2024/05/07 06:01

Sessions and transactions

This page explains common techniques to deal with the Sessionand transactions in Hibernate applications. Refer to the Hibernatereference documentation and the "Transactions and Concurrency" chapterfor more information. This page describes Hibernate 3.1.x and codeshown here does not work in older versions.

  • Unit of Work
  • Transactions
  • The scope of a unit of work
  • Transaction demarcation with JTA
  • Transaction demarcation with plain JDBC
  • Transaction demarcation with EJB/CMT
  • Custom transaction interceptors
  • Implementing long Conversations
  • What about the SessionFactory?
  • This is all very difficult, can't this be done easier?

Unit of Work

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

Transactions

Transactions also group dataaccess operations, in fact, every SQL statement, be it queries or DML,has to execute inside a database transaction. There can be nocommunication with a database outside of a database transaction. (Notethat 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.)

Oneapproach is the auto-commit mode, where every single SQL statement iswrapped in a very short transaction. This mode is never appropriate foran application, but only for ad-hoc execution of SQL with an operatorconsole. Hibernate disables or expects the environment (in J2EE/JEE) todisable auto-commit mode, as applications are not executing ad-hoc SQLbut a planned sequence of statements. (There are ways to enableauto-commit behavior in Hibernate but it is by definition slower thanregular transactions and less safe.)

The right approach is todefine clear transaction boundaries in your application by beginningand committing transactions either programmatically, or if you have themachinery to do this, declaratively (e.g. on service/command methods).If an exception occurs the transaction has to be rolled back (ordeclaratively, 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 the session-per-request implementation pattern. A single Session and a single database transaction implement the processing of a particular request event.

Another programming model is that of long Conversations, e.g. an application that implements a multi-step dialog, for example a wizard 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 considered detached 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 single Sessionhas a bigger scope than a single database transaction and it might spanseveral database transactions. Each request event is processed in asingle database transaction, but flushing of the Session would be delayed until the end of the conversation and the last database transaction, to make the conversation atomic. The Sessionis held in disconnected state, with no open database connection, duringuser think-time. Hibernate's automatic optimistic concurrency control(with versioning) is used to provide conversation isolation.

Hibernatesupports several convenience APIs that make implementation of alltransaction and conversation strategies easier, with any transactionprocessing system you might deploy on.

Transaction demarcation with JTA

Hibernateworks in any environment that uses JTA, in fact, we recommend to useJTA whenever possible as it is the standard Java transaction interface.You get JTA built-in with all J2EE/JEE application servers, and each Datasource you use in such a container is automatically handled by a JTA TransactionManager. 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 embeddableversion of the JBoss JCA/JTA/JNDI services, hence provides JTA in anydeployment 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 your SessionFactory:

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 assoon as you write non-trivial applications: you can separate thetransaction demarcation code from your data access code. The "currentsession" refers to a Hibernate Session bound by Hibernate behind the scenes, to the transaction scope. A Session is opened when getCurrentSession()is called for the first time and closed when the transaction ends. Itis also flushed automatically before the transaction commits. You cancall getCurrentSession() 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.

Transaction demarcation with plain JDBC

Ifyou don't have JTA and don't want to deploy it along with yourapplication, you will usually have to fall back to JDBC transactiondemarcation. 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 atransaction, as it does in a JTA environment, it binds it to thecurrent Java thread. This thread-bound strategy works in every JSEapplication - note that you should use JTA and a transaction-boundstrategy in a JEE environment (or install JTA with your JSEapplication, 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

Transaction demarcation with EJB/CMT

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

 // Do some work
factory.getCurrentSession().load(...);
factory.getCurrentSession().persist(...);

Instead of coding the begin, commit, and rollback of yourtransactions into your application you could use a declarativeapproach. For example, you might declare that some of your service orcommand methods require a database transaction to be started when theyare called. The transaction ends when the method returns; if anexception is thrown, the transaction will be rolled back.

Declarativetransaction demarcation is a standard feature of EJB, also known ascontainer-managed transactions (CMT). In EJB 2.x you would use XMLdeployment descriptors to create your transaction assembly. In EJB 3.xyou can use JDK 5.0 annotation metadata directly in your source code, amuch less verbose approach. To enable CMT transaction demarcation forEJBs in 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.CMTTransactionFactory

Hibernate binds the current Sessionto the current (JTA) system transaction that is controlled by your EJBtransaction assembly. When the system transaction commits (the EJBtransaction completes), the Session is automatically flushed and closed.

Custom transaction interceptors

To removetransaction demarcation from your data access code you might want towrite your own interceptor that can begin and end a transactionprogrammatically (or even declaratively). This is a lot easier than itsounds, after all, you only have to move three methods into a differentpiece of code that runs every time a request has to be processed. Ofcourse more sophisticated solutions would also need to handletransaction propagation, e.g. if one service method calls another one.Typical interceptors are a servlet filter, or an AOP interceptor thatcan be applied to any Java method or class. You can find examples ofboth in the CaveatEmptor demo application.

Furthermore,writing or using a flexible interceptor for transaction demarcationalso enables you to implement another popular Hibernate pattern, Open Session in View.

Implementing long Conversations

Clearly, in a long Conversation with a session-per-conversation we can't use Hibernate's built-in support for session-per-request with the automatic binding and handling of Session and transactions (or threads) just out-of-the-box. A single Sessionshould not be automatically closed when the transaction ends. It shouldnot even be flushed automatically, which would happen when thetransaction is committed. We need to do three things:

  • Extend the life of a Session by not closing it at the end of a transaction
  • Store the disconnected Session between transactions
  • Set a FlushMode.NEVER on each new Session so it will only synchronize its state when flush() is called explicitely

One way to implement session-per-conversation is to do so yourself, completely, without using the getCurrentSession() functionality in your code. This is practically an open field, you can open, flush, close, begin, end, and bind the Session and transactions in any way you like. However, this is not very easy to do.

A much better approach is to extend the getCurrentSession() feature. You'd still have to implement your own storage of a disconnected Session and take care of flushing, but your data access code can then use getCurrentSession()everywhere and any change to the conversation or transactional contextis transparent to your persistence layer. The difference is that you tell Hibernate what "current" means. To customize this you have to write your own CurrentSessionContext implementation, or extend the thread-local or JTA-bound strategies. Examples of both can be found in the the CaveatEmptor demo application.

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, ifyou run in a JEE environment, or use an embedded service in JSE, youcould simply look it up from JNDI, where Hibernate can bind it onstartup. Another solution is to keep it in a global static singletonafter startup. You can in fact solve both the problem of SessionFactorylookup and Hibernate startup with the same piece of code, a trivialhelper class (this is from the tutorial in chapter 1, Hibernatereference 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 in the CaveatEmptor demo application.

Note:There are many variations of much more complex HibernateUtil classesfloating around the net. However, for Hibernate 3.1, the code shownabove is the only code that is needed. If you use JNDI you might wantto have a look at HibernateUtil in the latest CaveatEmptor. Every otherHibernateUtil is obsolete for Hibernate 3.1.

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

Hibernatecan only do so much as a persistence service, managing the persistenceservice is however the responsibility of the applicationinfrastructure, or framework. The EJB3 programming model makestransaction and persistence context management very easy, use the Hibernate EntityManagerto get this API. Either run your EJBs inside a full J2EE applicationserver (previews available from several vendors) or in a lightweightembeddable EJB3 container, JBoss Embeddable EJB3, in any Java environment. The JBoss Seamframework has built-in support for automatic context management,including persistence and conversations, with only a few annotations inyour source code.

原创粉丝点击