Posts tagged ‘hibernate’

Manual transactional demarcation in spring and hibernate

Often you are forced to write code where the @Transactional in spring simply does not cut it. You want to execute certain pieces of code in different transactions all with a different propagation and isolation levels. The manual transactional demarcation in spring is stupidly simple after some upfront configuration and works uniformly by enrolling in what ever the enclosing transaction manager is. In case you are running in a JEE server it will tie in with the JTA transaction manager otherwise hibernate transaction manager.

In case you want to integrate with the JTA transaction manager and want the spring @Transactional to work simply put this int he application context:

<!-- configure JTA transaction manager -->
    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
    <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="allowCustomIsolationLevels" value="true" />
    </bean>

If you are running inside a plain servlet container like tomcat you can configure the hibernate transactions, vanilla JDBC transactions and etc. to use the hibernate transaction manager like this:

<!-- use the hibernate transaction manager -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
        <property name="dataSource" ref="dataSource" />
    </bean>

Notice that the session factory and the data source properties are both set. This then allows jdbcTemplate etc. to all participate in the same transactions as a hibernate call to save() and load().

Once configured its great any spring managed bean with the following annotations on methods will work flawlessly.

@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)

This is where the problem starts. Even though you get very granular in the transactions here - calling methods within a service implementation will not be transactionally aware because the demarcation, flushing, commits etc. happen by generating a proxy around the class and internal calls are simply by passed.

In this case you can make use of the spring TransactionTemplate and the PlatformTransactionManager class.

Using it is quite simple - inject the platformTransactionManager into a bean:

<!-- use the hibernate transaction manager -->
    <bean id="xyzService" class="XYZServiceImpl">
        <property name="platformTransactionManager" ref="transactionManager" />
    </bean>

and then directly use transaction templates by specifying custom isolation and propagation levels like this:

public class XYZServiceImpl implements XyzService {
    PlatformTransactionManager platformTransactionManager;

    public void doService() {
        TransactionTemplate template = new TransactionTemplate(platformTransactionManager);
        template.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            //annon. inner class
            template.execute(new TransactionCallback() {
            public Object doInTransaction(TransactionStatus status) {
                //your business logic here
            }
        });
    }

    public void setPlatformTransactionManager(PlatformTransactionManager platformTransactionManager) {
        this.platformTransactionManager = platformTransactionManager;
    }
}

The cool thing is that you could have parts of the same method execute in a different transactions - looking at different isolation levels. No more complex XA/JTA code, hibernate sessions will flush, transactions will commit/rollback at the demarcations you expect - spring makes it too easy.

Implementing a pagination filter in Hibernate and Spring

Database based pagination is a common requirement in database driven applications. With application frameworks like spring and hibernate and newer language features like generics development of pagination features is seamless.

Lets take a look at this example -

First lets define a data access object (DAO) - an interface used by the application to perform CRUD operations and (in our case) pagination:

public interface Dao {
    public T get(Serializable id);
    public Collection getByExample(T entity);
    public Collection getByExample(T entity, PaginationFilter filter);
    public long countByExample(T entity);
    public void delete(T entity);
    public void persist(T entity);
    public void saveOrUpdate(T entity);
}

The method to note here is

getByExample(T entity, PaginationFilter filter);

The pagination filter is an object that encapsulates:

  1. The start row
  2. The max number of rows
  3. The sorting criteria

Here is the listing of the pagination filter:

public class PaginationFilter {
    public enum Order {
        ASCENDING,
        DESCENDING
    }

    public class SortingCriteria {
        private String column;
        private Order order;

        public SortingCriteria(String column, Order order) {
            this.column = column;
            this.order = order;
        }

        public String getColumn() {
            return column;
        }

        public Order getOrder() {
            return order;
        }
    }

    private int firstResult;
    private int maxResult;
    private List sortingCriterias;

    public PaginationFilter(int firstResult, int maxResult) {
        this.firstResult = firstResult;
        this.maxResult = maxResult;

        this.sortingCriterias = new ArrayList();
    }

    public PaginationFilter addSortingCriteria(String field, Order order) {
        this.sortingCriterias.add(new SortingCriteria(field, order));
        return this;
    }

    public int getFirstResult() {
        return firstResult;
    }

    public int getMaxResult() {
        return maxResult;
    }

    public List getSortingCriterias() {
        return sortingCriterias;
    }
}

Lets now take a look at the implementation of the DAO - the rest of the methods are not very interesting so lets limit the discussion to the methods with PaginationFilter:

    public Collection getByExample(final T entity, final PaginationFilter filter) {
        if (filter == null)
            return getByExample(entity);
        return (Collection) getHibernateTemplate().execute(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                Criteria criteria = createCriteria(entity, filter, session);
                return criteria.list();
            }
        });
    }

    public long countByExample(final T entity) {
        return (Long) getHibernateTemplate().execute(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                Criteria criteria = createCriteria(entity, null, session);
                criteria.setProjection(Projections.rowCount());
                Object object = criteria.uniqueResult();
                return new Long(object.toString());
            }
        });
    }

    private Criteria createCriteria(T entity, PaginationFilter filter, Session session) {
        Criteria criteria = session.createCriteria(entity.getClass());
        criteria.add(Example.create(entity));

        if (filter != null) {
            criteria.setMaxResults(filter.getMaxResult()).setFirstResult(filter.getFirstResult());

            for (PaginationFilter.SortingCriteria sortingCriteria : filter.getSortingCriterias()) {
                criteria.addOrder(
                        sortingCriteria.getOrder() == PaginationFilter.Order.ASCENDING?
                                Order.asc(sortingCriteria.getColumn()) :
                                Order.desc(sortingCriteria.getColumn()));
            }
        }

        return criteria;
    }

That is all the code you need to implement pagination with Hibernate:

So to test this code out - let us consider a test entity:

@Entity
public class TestEntity implements Savable{
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private String address;
.... //Getters and setters
}

Here is a little test that exercises the PaginationCode with the TestEntity:

    @Test
    public void testSorting() {
        PaginationFilter filter = new PaginationFilter(0,26);
        filter.addSortingCriteria("name", PaginationFilter.Order.DESCENDING);
        TestEntity testEntity = new TestEntity();
        List list = (List) genericDao.getByExample(testEntity, filter);
        assertEquals(26, list.size());
        assertEquals("a", ((TestEntity) list.get(25)).getAddress());
        assertEquals("z", ((TestEntity) list.get(0 )).getAddress());
    }

That is it. Imagine having to write filtering/pagination code for a straight up JDBC. Not so much fun. It also amazes me how nice and clean Java generics can make your code. This sceheme works very nicely for me.

High speed database neutral batch inserts/updates using Spring and Hibernate

I have been working on hibernate for a while now and recently wanted to get some data in really fast (basically using SQL batching). 

Here is the entity:

@Entity(name = "Person")
public class PersonEntity {
    @Id
    int id;
    String fName;
    String lName;
    int age;

    public String getFName() {
        return fName;
    }

    public void setFName(String fName) {
        this.fName = fName;
    }

    public String getLName() {
        return lName;
    }

    public void setLName(String lName) {
        this.lName = lName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

1. Modify the *context.xml file to include the following hibernate jdbc parameter:

<prop key="hibernate.jdbc.batch_size">20</prop>

2. Write the code with session clearing every BATCH_SIZE. For convinience sake I am extending the HibernateDaoSupport class but you can use the sessionFactory directly. Here is what I have:

@Transactional
public class HibernateInsertTest extends HibernateDaoSupport implements HibernateInsertInterface {
    private final int BATCH_SIZE = 20;
    private final int NUMBER_OF_ENTITIES = 100000;
    @Transactional(propagation = Propagation.REQUIRED)
    public void init() {
        Session session = getSession();
        StopWatch stopWatch = new StopWatch("Insert test");

        stopWatch.start("Saving entities");
        for (int x = 0; x<NUMBER_OF_ENTITIES; x++) {

            PersonEntity ent = new PersonEntity();
            double rand = Math.random();
            ent.setId(new Long(System.currentTimeMillis() + x).hashCode());
            ent.setFName(""+ rand);
            ent.setLName("" + rand);
            ent.setAge((int) (rand * 100));
            session.persist(ent);
            if (x%BATCH_SIZE == 0) {
                session.flush();
                session.clear(); //<-- IMPORTANT TO CLEAR THE SESSION
            }
        }
        stopWatch.stop();
        System.out.println(stopWatch.prettyPrint());
    }

}

There is something subtle about this code that I want to talk about. Firstly notice that the inserts are in one transaction - although I can use the hibernate session to being and commit a transaction I am instead using the @Transaction spring annotation. The corresponding configuration (since I am using Jboss I can tie this in with the Jta transaction manager) is the following: 

    <!-- configure JTA transaction manager -->
    <tx:annotation-driven transaction-manager="transactionManager" />
    <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
    </bean>

Since spring works by generating a proxy for this class I am also implementing an interface (its good practice anyway). The other thing are the periodic calls to session.clear() and session.flush() - they are to prevent an OutOfMemroyException which can occurr if you are inserting a large amount of objects.  

Here is the output of the program:

StopWatch 'Insert test': running time (millis) = 4687

-----------------------------------------
ms     %     Task name
-----------------------------------------
04687  100%  Saving entities

The performance is no different from vanilla JDBC batching because underneath the hood its using the same mechanism. There is however a drawback you cannot use oracle natvie bindings which speeds up batch inserts by 1.5x.