Working with lazy associations

Persistent Object: An object with an id existing in the database that we pulled to the session.


Transient Object: An object with no id that has not yet been persisted.

Detached Object: It is the object that is formed as a result of closing the Session to which the persistent object is connected. The object's id field is full and probably corresponds to a record in the database.


! After hibernate3, the default value lazy is set to "true".


"By default, Hibernate3 uses lazy select fetching for collections and lazy proxy fetching for single-valued associations."


Lazy initialization exception

Fires if there is access to the detached object after commit. For example:

{

   s = sessions.openSession();
   Transaction tx = s.beginTransaction();

   Hibernate.initialize();

   User u = (User) s.createQuery("from User u where u.name=:userName").setString("userName", userName).uniqueResult();

   Map permissions = u.getPermissions();

   tx.commit();
   s.close();

   Integer accessLevel = (Integer) permissions.get("accounts"); // Error! Attempting to access detached object.

}

"Hibernate does not support lazy initialization for detached objects"


If we use non-lazy links (lazy="false") too much, we draw too many objects into memory. Instead, it may make more sense to use join fetch (non-lazy fetch) in the tansactions we want.

Hibernate Session and Transaction Management

Hibernate's auto-commit mode in transaction management is false by default.

There are patterns that are generally used in Hibernate Transaction management.

1) DB Transaction and Hibernate Session start and end simultaneously (session-per-request).
2) Chaining sessions and transactions with detached objects (session-per-request-with-detached-objects).
3) Multiple transaction management (session-per-conversation) connected to a session.

Transaction Demarcation with JTA

Hibernate uses JTA on J2EE/JEE application servers by default. Today, all J2EE/JEE application servers host JTA as built-in and any datasource used on these servers is automatically managed by JTATransactionManager.

The code below exemplifies this. The session-per-request pattern is obtained by connecting the current session to the transaction of the JTA.

Code Example 1:

embedded transaction-session management (built-in management)

try {
 UserTransaction tx = (UserTransaction)new InitialContext().lookup("java:comp/UserTransaction");

 tx.begin();

 factory.getCurrentSession().load(...); // hibernate session otomatik olarak açılır
 factory.getCurrentSession().persist(...);

 tx.commit(); // hibernate session otomatik olarak kapanır
}
catch (RuntimeException e) {
 tx.rollback(); // hibernate session otomatik olarak kapanır
 throw e;
}

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

Code Example 2:

Case where session management is done manually

UserTransaction tx = (UserTransaction)new InitialContext().lookup("java:comp/UserTransaction");

Session session = factory.openSession();

try {
 tx.begin();

 session.load(...);
 session.persist(...);

 session.flush();

 tx.commit();
}
catch (RuntimeException e) {
 tx.rollback();
 throw e;
}
finally {
 session.close();
}

Transaction Demarcation with JDBC

When JTA is not desired to be used, session-per-request pattern can be performed with Hibernate's Transactions without the need for JDBC API as follows:

Code Example 3:

try {
 // proxied session otomatik olarak açılır ve current Java thread'e bağlanır.
 factory.getCurrentSession().beginTransaction(); 
  
 factory.getCurrentSession().load(...);
 factory.getCurrentSession().persist(...);
 factory.getCurrentSession().getTransaction().commit();
}
catch (RuntimeException e) {
 factory.getCurrentSession().getTransaction().rollback();
 throw e; 
}

Hibernare cannot automatically connect current session to transaction like JTA, instead it connects current java thread in proxied state state.

This thread-bound strategy works in all JSE applications. In JEE applications, it is better to use JTA's transaction-bound strategy, and even in JSE applications, JTA can be used after installing it.

The following settings are required to use the thread-bound strategy.

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

Transaction Demarcation with EJB/CMT

It is possible to configure using only annotation. Transaction is opened automatically and if there is no error in the method, it is committed, in case of error, rollback is made.

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

In EJB2x only xml conf. There was, however, it is possible to manage transactions in a declarative manner with annotations in EJB3x.

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


Transaction Demarcation with Interceptor

It is possible to completely remove transaction codes from within the data access code. For this, a filter (open session in view pattern) or AOP interceptor running on each request is sufficient.

Hibernate DB Flushing

 If the flushing method is AUTO, hibernate flushes in 3 cases:


1) when transaction is committed

2) before query is executed (if something is pulled from the database, commits are made first so that the updated data can be pulled)

3) when flush() method is called explicitly


Hibenate's flushing mechanism works as a write-behind. That is, changes are written as late as possible. However, there is an exception where write-behind is not performed. This is the case where the ID generation method is set automatically (auto-generated, auto-increment id). Hibernate doesn't want objects to be idless (non-transient objects can't be idless). If auto-generated id is used, it has to insert to get the id of the object.


Let's say it uses sequence (or a custom piece of code) for the id generation method. In this case, the insert process can be left for later. The id of the object can be retrieved from the sequence in the database with a query. If the object with the same id is requested by the same client or another client, the insert operation is performed and then the object is pulled from the database in its current form.


The point to be noted is that the operations that update the data are kept in a queue with the logic of the queue. For example, if first insert, then delete, then select is performed, hibernate inserts the object first, then deletes it, and the select operation returns empty as it should. In this case, we can say that hibernate runs these processes as batch updates.


Data updates are handled by the second level cache.


Hibernate changes the order of queued jobs during batch update to avoid foreign-key constraint. The most prioritized SQL commands are insert commands. The order of priority is as follows.


1) inserts

2) updates

3) deletes from collections

4) inserts to collections

5) delete


Let's say we first delete a child object with id x. Then we tried to add the child object with the same id again. First, the insert operation runs and the id constraint is taken. This operation cannot be performed. The best thing to do here is to call the flush method after each delete.


One of the other two flushing methods is to flush only during the transaction commit. The last one is to perform database synchronization only when the flush() method is called. The main drawback of these two methods is: database queries return the old version of dirty data.