"; */ ?>

hibernate


26
Sep 10

Money Making Project

For the past several years most of my projects were based on Spring and Hibernate. And what I keep seeing is how people struggle to understand how the two are working together.

So going from project to project, I have to repeat the same spiel over and over again to get people up to speed.

Since I believe that the most effective way to LEARN is to DO, I create simple examples for people to play with / to complete / to analyze, etc..

So “Money Making Project” is one of such examples. It comfortably lives at github’s luxury apartments, so anybody can “git clone” / “fork” and play with it.

Besides making money, this project also demos things such as:

A way to structure a project

Maven based structure ( hence can be easily used by gradle ). Configuration and property files organized under “META-INF/conf”, “META-INF/props”, etc..

A way to separate Spring configs

“tx-spring-config.xml”, “persistence-spring-config.xml”, “service-spring-config.xml”, “repository-spring-config.xml” and “application-context.xml” that includes them all

Properties via PropertyPlaceholderConfigurer

<context:property-placeholder location="classpath:META-INF/props/env.properties"/>

Hibernate overall configuration file

That is injected into AnnotationSessionFactoryBean

Hibernate Named Queries

That are linked to the Hibernate overall config

<mapping resource="META-INF/conf/hibernate/mapping/startup-bank-named-queries.xml"/>

Spring’s DAO / Hibernate Exception Translation

Via @Repository and “PersistenceExceptionTranslationPostProcessor”

Simple CRUD Repository

public interface MoneyRepository {
 
    public void make( MoneyRoll moneyRoll );        // C
    public MoneyRoll find( Long id );                       // R
    public void update( MoneyRoll moneyRoll );     // U
    public void takeOut( MoneyRoll moneyRoll );    // D
}

with a Hibernate based implementation

Transaction Management with Spring AOP

Declarative, on a Service Level, using “aop:config”, “tx:advice” namespaces

Spring Testing

With SpringJUnit4ClassRunner, ContextConfiguration, etc..

Using Embeded in-memory H2 Database for Testing

<jdbc:embedded-database id="dataSource" type="H2"/>

Hibernate Logging

Most useful hibernate “log4j.logger” properties

Demoing how important Transaction is for Hibernate Sessions

“HibernateSessionNotBoundToThreadIntegrationTest” for the second (after LazyInitializationException ) most common Hibernate exception: “No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here”


19
Feb 09

Make Hibernate Update/Create Changed Objects

Make Hibernate Update/Create Changed Objects

While Hibernate is a mature framework it still has a a lot of room for improvement. Starting from polishing documentation: e.g. “MappingException JavaDoc: An exception that usually occurs at configuration time, rather than runtime, as a result of something screwy in the O-R mappings.“, and going towards more powerful default optimization.

In fact, Rod Johnson (leader of Spring Framework’s parenthood gang :)), and other SpringSource consultants, constantly mention that most of their time, on projects that use Hibernate, is spent fixing Hibernate optimization bugs.

A lot of people form their opinions on what characteristics of “a good software framework” are. There are books, articles about it, but people are different, so opinions vary. One of the characteristics that makes a good framework, in my opinion is “while allowing clients to hook into the internal framework code, do not encourage it”. Meaning the flexibility is there, but the framework should not encourage its clients to get inside framework’s stereotypes. This way framework’s code (internal stereotypes) can alter, and still have “older version clients” running without (significant) change.

One of such hooks that Hibernate actually encourages to use is evicting the object from the session.

Here is an example:

While reviewing one of a Spring Batch jobs, I found that under the same “transaction management roof” (HibernateTransactionManager) jdbcTemplate and hibernateTemplate behaved differently: jdbcTemplate updated records in DB, but hibernateTemplate was not even trying.

So I enabled Hibernate logging in “log4j.properties”:

log4j.rootLogger = ERROR, errorsLog
 
# Hibernate logs
log4j.logger.org.hibernate = DEBUG, hibernateLog
log4j.additivity.org.hibernate = false
 
# HIBERNATE APPENDER
log4j.appender.hibernateLog = org.apache.log4j.RollingFileAppender
log4j.appender.hibernateLog.File = ./path-to/hibernate.log
# Set the maximum log file size (use KB, MB or GB)
log4j.appender.hibernateLog.MaxFileSize = 4096KB
# Set the number of log files (0 means no backup files at all)
log4j.appender.hibernateLog.MaxBackupIndex = 10
# Append to the end of the file or overwrites the file at start.
log4j.appender.hibernateLog.Append = false
log4j.appender.hibernateLog.layout = org.apache.log4j.PatternLayout
log4j.appender.hibernateLog.layout.ConversionPattern = [%p] [%d{yyyy-MM-dd @ HH:mm:ss}] [%t|%c{1}] %m%n
 
# ERRORS APPENDER
log4j.appender.errorsLog = org.apache.log4j.RollingFileAppender
log4j.appender.errorsLog.File = ./path-to/hibernate-error.log
log4j.appender.errorsLog.MaxFileSize = 4096KB
log4j.appender.errorsLog.MaxBackupIndex = 1
log4j.appender.errorsLog.layout = org.apache.log4j.PatternLayout
log4j.appender.errorsLog.layout.ConversionPattern = [%p] [%d{yyyy-MM-dd @ HH:mm:ss}] [%t|%c{1}] %m%n

First, I read an object via Hibernate, update it in the code, and then call a dao’s update method to persist it – pretty simple. However while debugging it step by step, after executing the Hibernate update query, in a log, I saw:

[DEBUG] [main|DefaultSaveOrUpdateEventListener] ignoring persistent instance
[DEBUG] [main|DefaultSaveOrUpdateEventListener] object already associated with session: [EntityName#3]

So the Hibernate did not update the object due the fact that it thought that another object was already associated with this Hibernate session. Which, in fact, was the same reference to the same object, only the update was called from a different instance.

The solution to this was to evict the object from the Hibernate session right after reading it:

  ObjectDto objectDto = dao.findById( id );
  dao.getHibernateTemplate().evict( objectDto )

Once the fix was applied, after Hibernate update call, I saw:

[DEBUG] [main|DefaultEvictEventListener] evicting [EntityName]
[DEBUG] [main|DefaultSaveOrUpdateEventListener] updating detached instance
[DEBUG] [main|DefaultSaveOrUpdateEventListener] updating [EntityName#3]
[DEBUG] [main|DefaultSaveOrUpdateEventListener] updating [EntityName#3]

And the object was successfully persisted into the database.

Happy ORMing!


22
Oct 08

Use HSQLDB Functions with Hibernate Mapping Files

Let’s say you need to do a “local integration” test, where you code does not depend on external systems (queues/external servers/DB/etc..). Creating unit tests, mocking/stubbing everything out is all good, but sometimes you need to be able to run tests that are as close as possible to the “real world” deal, while you are in “local mode” – e.g. plane, subway, basically somewhere without access to the real external systems.

One of ways you can approach it with databases is to load schemas you need in memory, and work (test) against those schemas. This is relatively easy to do with HSQLDB, and there are many “googlable” guides on how to do it. However in this little howto, I want to show you how you can define your own HSQLDB functions, and use them as either “stubs” (or even “real deal”).

Here is an example on why you may need it. Consider this Hibernate mapping file that is used in your application:

 
<?xml version="1.0"?>
 
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
 
<hibernate-mapping
	package="org.project.customer.dto.search">
	<class name="CustomerCreditCardSearchResultDto" table="CR_CREDIT_CARD">
 
		<id name="id" column="CR_CREDIT_CARD_ID">
			<generator class="native">
                                 <param name="sequence">SQ_CR_CREDIT_CARD</param>
			</generator>
		</id>
 
                <property name="accountNumber" formula="some_pkg.decode( ACCOUNT_NUMBER )" />
                <property name="secretNumber" formula="some_pkg.decode( SECRET_NUMBER )" />
                <property name="expirationDate" column="EXPIRATION_DATE" type="date" />
                <property name="zipCode" column="ZIP_CODE" />
 
	</class>
</hibernate-mapping>

Let’s say you created a CR_CREDIT_CARD table in in-memory DB, populated it, started your test. Now when you try to read a “CustomerCreditCardSearchResultDto” object somewhere in your test, Hibernate will construct an SQL query from the mapping file above, and execute it on the in-memory DB.

However there is a problem – it is going to fail with a similar Exception:

Caused by: java.sql.SQLException: Unexpected token: DECODE in statement [select <query here> .... ]
	at org.hsqldb.jdbc.Util.throwError(Unknown Source)
	at org.hsqldb.jdbc.jdbcPreparedStatement.<init>(Unknown Source)
	at org.hsqldb.jdbc.jdbcConnection.prepareStatement(Unknown Source)
	at org.apache.commons.dbcp.DelegatingConnection.prepareStatement(DelegatingConnection.java:248)
	at org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.prepareStatement(PoolingDataSource.java:302)
	at org.hibernate.jdbc.AbstractBatcher.getPreparedStatement(AbstractBatcher.java:442)
	at org.hibernate.jdbc.AbstractBatcher.getPreparedStatement(AbstractBatcher.java:368)
	at org.hibernate.jdbc.AbstractBatcher.prepareQueryStatement(AbstractBatcher.java:105)
	at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1561)
	at org.hibernate.loader.Loader.doQuery(Loader.java:661)
	at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:224)
	at org.hibernate.loader.Loader.loadEntity(Loader.java:1785)
	... 60 more

If there is an Exception – there is a Reason for it :) Notice these two mappings in the Hibernate mapping file above:

<property name="accountNumber" formula="some_pkg.decode( ACCOUNT_NUMBER )" />
<property name="secretNumber" formula="some_pkg.decode( SECRET_NUMBER )" />

That says Hibernate to construct SQL that uses this “some_pkg.decode()” custom function/store procedure after (in case of SELECT) reading these values from a database. However, in you local testing environment, you do not have this function defined. It is probably defined in the DB (like Oracle/DB2/etc) itself.

But for most, if not for all, technical problems there is a solution, so don’t worry – you can define this function(s) yourself. Since it is a test, the most logical thing, since you want to abstract the code out from the external systems (and their functions) would be to define stubs to these functions.

Let’s create a utility “HsqlFunctions” Java class with static methods to be used by HSQLDB (it can only use static Java methods in its SQL, btw):

package org.project.test.util;
 
/**
 * HSQL functions to be aliased.
 *
 *    DDL example:
 *
 *    CREATE ALIAS DECODE FOR "org.project.test.util.HsqlFunctions.decodeString"
 *
 *
 * @author blog.dotkam.com
 *
 */
 
public final class HsqlFunctions
{
   private HsqlFunctions()
   {
     // static utility class - does not need to be constructed.
   }
 
    /**
     * Stub for the decode function.
     * Usually used to "please" Hibernate Mapping Files.
     *
     * @param value - String value for the column
     * @return - returns the same String value
     */
    public static String decodeString( String value )
    {
        return value;
    }
}

Now, when creating a test schema, you can ALIAS this static method as HSQLDB function like this:

     CREATE ALIAS DECODE FOR "org.project.test.util.HsqlFunctions.decodeString"

This will tell HSQLDB to call “decodeString” static Java method on the column value, every time it sees “decode( COLUMN )” in SQL.

One thing to notice, though – make sure the type that the Java methods take are exactly the same as defined by DDL (Database Schema). For example, if you pass in column value as an Object:

    public static String decodeString( Object value )
    {
        return String.valueOf (value );
    }

And the column is defined as VARCHAR in schema, HSQLDB will try to pack that String into an Object, and it will fail with a similar exception:

Caused by: java.sql.SQLException: Wrong data type: hexadecimal string with odd number of characters in statement [select ... <query here>]
	at org.hsqldb.jdbc.Util.throwError(Unknown Source)
	at org.hsqldb.jdbc.jdbcPreparedStatement.executeQuery(Unknown Source)
	at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:93)
	at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:139)
	at org.hibernate.loader.Loader.getResultSet(Loader.java:1669)
	at org.hibernate.loader.Loader.doQuery(Loader.java:662)
	at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:224)
	at org.hibernate.loader.Loader.loadEntity(Loader.java:1785)
	... 60 more

Below is just an excerpt on how you would set up the schema to use it with HSQLDB in your test:

    ....
 
    private static final String CREATE_ALIASES =
        "CREATE ALIAS DECODE " +
        "FOR \"org.project.test.util.HsqlFunctions.decodeString\"";
 
    private static final String CREATE_SEQUENCES =
        "DROP SEQUENCE SQ_CR_CREDIT_CARD IF EXISTS;" +
 
        "CREATE SEQUENCE SQ_CR_CREDIT_CARD " +
        "START WITH 1 INCREMENT BY 1";
 
    private static final String CREATE_TABLES =
        "DROP TABLE cr_credit_card IF EXISTS;" +
 
        "CREATE TABLE cr_credit_card (" +
            "cr_credit_id NUMERIC(15), " +
            "account_number VARCHAR(48)," +
             ....
             ....
   ....
 
        //  Creating the schema
 
        m_jdbcTemplate.execute( CREATE_ALIASES );
        m_jdbcTemplate.execute( CREATE_SEQUENCES );
        m_jdbcTemplate.execute( CREATE_TABLES );
 
        //  HSQLDB is ready to be populated with data at this point.
   ....

You can use DBUnit to create the schema above and populate it with the data. The above is just a straight forward hardcoded example.
Notice how it also creates a sequence “SQ_CR_CREDIT_CARD” to please the Hibernate, and others who might use it in the application.

Happy “local integration” testing!

Feel free to post questions/comments/suggestions, I’ll try to respond when have a free second or two :)