Spring 3 Part 8: Spring Hibernate open session in view

This is the 8th part of the Spring 3 Series.
In this blog I am going to use Hibernate 3.5.2 to explain the use of Spring Transaction which fulfil the requirements of open session in view pattern. First understand some important concepts in Hibernate. First of all, it is very important to understand the object states in the Hibernate. Transient object will become a persistent object in the context of active session. This persistent object represent one row of the data table. There can be two transient object references which are not refer to the same object but can hold same data. These objects are identical only in the persistence context (persistent objects are identical) because persistence objects are identified based on the database primary key. Therefore, persistence object identifier is the same as data table identifier.  For the transient and detached object, equal() and hashcode() are the methods to be implemented to define the identifier.
finaloutput
Figure 1: Hibernate Entity Status

Transient object can be created using new operator. The main thing is it is not associated with a Hibernate session. Transient object will be destroyed by the GC when all the references are released.
Persistent object can be created using Transient object when transient object is associated with a Hibernate session by save/persist() or load() operations. Hibernate synchronize the state of the persistent object with the database.
Detached object is a persistent object without associated a Hibernate session (when session is closed). The detached object can be modified and attach back to the to a new Hibernate session later.

  persistent representation Identifier value
Transient
no
equal, hashcode
Persistent
yes
primary key
Detached
no
equal, hashcode
It is important to understand the Hibernate session factory here on SessionFactory.  SessionFactory a thread safe immutable cache of compiled mapping  for single database. SessionFactory optionaly maintains optional second-level cache for the data (for process or cluster) . This is the factory to create a Hibernate Session.
ENIMAGE1367584595211
Figure 2: Hibernate Session from SessionFactory
Hibernate Session is single threaded as well as it is design to be short live. it wrap the database connection and the factory for the org.hibernate.Transaction. Session is responsible to maintain the first level cache which hold the the persistent object graph and graph navigation (persistence context). Session wrap the connection.
This is not a web based application ( which complicate our discussion) instead standard Java application. For standard Java application, Hibernate recommended the Session handling with AOP.  The basic question is
What is the advantage of use of Spring for standard Java based Hibernate application ?
In this blog my objective to emphasis this advantage.
“ Use of Spring transactions completely avoid the use of AOP based Session handling, because Spring transactions are itself AOP implementations. “
This application is layered to 4 layers: Client, Service, DAO and Persistence. Although DAO is not recommended, for simplicity I use DAO layer
First consider the Persistence layer for the schema explained in the pervious blog: Spring 3 Part 7: Spring with Databases.
image

Here the State,
package au.com.ojitha.blogspot.spring.part8.domain;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class State implements Serializable {


    private String code;
    private String state;
    private int version;
    
    @Id
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    
    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
    }
    
    public int getVersion() {
        return version;
    }
    public void setVersion(int version) {
        this.version = version;
    }
    
}
Here the Address Entity
package au.com.ojitha.blogspot.spring.part8.domain;

import java.io.Serializable;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.NamedQuery;
import javax.persistence.OneToOne;

@Entity
@NamedQuery(name="Address.findAll", query="select a from Address a left join fetch a.state")
public class Address implements Serializable {

    @Id
    private int id;
    
    @Column(name="STREET_NUMBER")
    private String street;
    
    private String suburb;
    
    @OneToOne(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
    @JoinColumn(name="STATE")
    private State state;
    
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getStreet() {
        return street;
    }
    public void setStreet(String street) {
        this.street = street;
    }
    public String getSuburb() {
        return suburb;
    }
    public void setSuburb(String suburb) {
        this.suburb = suburb;
    }
    public State getState() {
        return state;
    }
    public void setState(State state) {
        this.state = state;
    }
    
    

}
As shown in the line 27, FetchType.LAZY configure the one to one relationship to access the State when only necessary. In other words, although you initiate the Address Entity, the state property is still null until properties of the state property is first time accessed. The most important thing is this state property should be accessed within the same session. Next consider the DAO layer, although I have two DAOs, in this example I use only AddressDAO.
image

Here the DAO interface, which hold all the CURD operations common to all the DAO implmenations
package au.com.ojitha.blogspot.spring.part8.dao;

import java.util.List;

public interface Dao<T, PK> {
    List<T> findAll();
    T findByPk(PK pk);
}
For Address, the DAO is AddressDAO
package au.com.ojitha.blogspot.spring.part8.dao;


public interface AddressDao<T, PK> extends Dao<T, PK> {
    
}
For the AddressDao , here the implementation
package au.com.ojitha.blogspot.spring.part8.dao;

import java.util.List;

import javax.annotation.Resource;

import org.hibernate.SessionFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import au.com.ojitha.blogspot.spring.part8.domain.Address;

@Repository("addressDao")
@Transactional
public class AddressDaoImpl implements AddressDao<Address, Integer> {

    @Resource(name="sessionFactory")
    SessionFactory sessionFactory;
    
    @Transactional(readOnly=true)
    public List<Address> findAll() {
        return sessionFactory.getCurrentSession().createQuery("from Address a").list();
        //return sessionFactory.getCurrentSession().getNamedQuery("Address.findAll").list();
    }

    public Address findByPk(Integer pk) {
        // TODO Auto-generated method stub
        return null;
    }

}
In the above code, only the findAll() method is implemented. As shown in the figure 2, Session is reused using getCurrentSession() as stated in the line 22. AdddressDaoImpl doesn’t create new session because transaction was propagated from the Address service. SessionFactory is the factory for Hibernate session.
image
As shown in the above class diagram, Service layer, consist of AddressService and the Client layer consists of Client classes. AddressServiceImpl access the AddressDao which is already explained under the DAO Layer.
package au.com.ojitha.blogspot.spring.part8.service;

import java.util.List;

import au.com.ojitha.blogspot.spring.part8.domain.State;



public interface AddressService {
    /**
     * All the states from the {@link State}.
     * @return array of States.
     */
    public List<State> getAllStates();

}
And here the Implementation,
package au.com.ojitha.blogspot.spring.part8.service;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;

import org.hibernate.Hibernate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import au.com.ojitha.blogspot.spring.part8.dao.AddressDao;
import au.com.ojitha.blogspot.spring.part8.domain.Address;
import au.com.ojitha.blogspot.spring.part8.domain.State;

@Repository("addressService")
@Transactional
public class AddressServiceImpl implements AddressService {
    
    @Resource(name="addressDao")
    AddressDao<Address, Integer> addressDao;
    
    
    @Transactional(readOnly=true)
    public List<State> getAllStates(){
        List<Address> addresses = addressDao.findAll();
        List<State> states = new ArrayList<State>();
        for (Address address : addresses) {
            State state = address.getState();
            Hibernate.initialize(state);
            states.add(state);
        }
        Hibernate.initialize(states);
        return states;
    }
}
It is necessary to highlight the line 24 Transaction annotation.  In this point, new transaction is created and propagated to the DAO: same session is available in the Service as well as AddressDao. In the line 30, before session completed, need to initialize the states because transaction state is not available in the Client who is the caller as shown in the following code.
package au.com.ojitha.blogspot.spring.part8.client;

import java.util.List;

import org.springframework.context.support.GenericXmlApplicationContext;

import au.com.ojitha.blogspot.spring.part8.dao.AddressDao;
import au.com.ojitha.blogspot.spring.part8.dao.StateDao;
import au.com.ojitha.blogspot.spring.part8.domain.Address;
import au.com.ojitha.blogspot.spring.part8.domain.State;
import au.com.ojitha.blogspot.spring.part8.service.AddressService;

public class Client {

    /**
     * @param args
     */
    public static void main(String[] args) {
        GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
        ctx.load("classpath:META-INF/spring/app-context.xml");
        ctx.refresh();
        
        AddressService service = ctx.getBean("addressService", AddressService.class);
        List<State> states = service.getAllStates();
        for (State state : states) {
            System.out.println(state.getState());
        }
    }

}
Whe initialize the stats, only second “select” will be executed as shown in the following log
2013-05-04 11:57:12,084 INFO [org.hibernate.impl.SessionFactoryObjectFactory] - <Not binding factory to JNDI, no JNDI name configured>
2013-05-04 11:57:12,313 INFO [org.springframework.orm.hibernate3.HibernateTransactionManager] - <Using DataSource [org.springframework.jdbc.datasource.DriverManagerDataSource@73d55dd] of Hibernate SessionFactory for HibernateTransactionManager>
2013-05-04 11:57:12,419 DEBUG [org.hibernate.SQL] - <select address0_.id as id0_, address0_.STATE as STATE0_, address0_.STREET_NUMBER as STREET2_0_, address0_.suburb as suburb0_ from Address address0_>
Hibernate: select address0_.id as id0_, address0_.STATE as STATE0_, address0_.STREET_NUMBER as STREET2_0_, address0_.suburb as suburb0_ from Address address0_
2013-05-04 11:57:12,446 DEBUG [org.hibernate.SQL] - <select state0_.code as code1_0_, state0_.state as state1_0_, state0_.version as version1_0_ from State state0_ where state0_.code=?>
Hibernate: select state0_.code as code1_0_, state0_.state as state1_0_, state0_.version as version1_0_ from State state0_ where state0_.code=?
Capital
As a conclusion, If you keep the transactional  persistence access open up to the Service layer, then the service layer is responsible for completing all the transactions (in Hibernate words, session). If you access the persistence object out of the Spring transaction as in the above example, it will throws Hibernate’s LazyInitializationException.
The source code for this blog is available to download from GitHub.

Comments

Popular posts from this blog

How To: GitHub projects in Spring Tool Suite

Spring 3 Part 7: Spring with Databases

Parse the namespace based XML using Python