Testing DAO classes

There is a presentation by Rod Johnson of Spring on testing with Spring that explains you what unit testing and what integration testing is, what mock objects are and some of the best practices. The presentation itself is from 2007 and while the general ideas haven't changed, the implementation techniques have.

Therefore in this article I will show you how to integration test your DAO classes. I am going to use a JPA project for this, but the same test code will work for Hibernate as well.

So, you should have a database set up, in a well known state. This should not be the production database! But it should be identical (or as close as possible).

On this database you perform the tests. This is better than mock objects, because although you can simulate certain behaviors with mock objects, it's nothing compared to using a real database, with its triggers, views, stored procedures etc.

How does it, uhm, how does it work?

Integration testing that involves a data source, whether you use Spring or, say, DBUnit, often works according to the same schema.

You mess around with the database, add, update, remove. Then all the changes are reverted.

With Spring, they are rolled back.

Integration testing with Spring 3

Prior to Spring 3 the recommended way of testing JPA was to use AbstractJpaTests - but not anymore; now it's deprecated. The official documentation suggests you use (extend) AbstractJUnit38SpringContextTests. You would rather use AbstractJUnit4SpringContextTests if you use JUnit 4. But both strategies require extending and Java allows for inheritance only (which is good), so I will show you an alternative way of doing it.

@RunWith(SpringJUnit4ClassRunner.class) - this means that JUnit will enable the functionality of the Spring TextContext Framework. That makes it possible for you to use Spring goodies (annotations like @Autowired etc.) in the tests

@ContextConfiguration - tells Spring where the beans files are. Note the format. This way it works within Eclipse, within Ant, within Maven, from Hudson etc.

@TransactionConfiguration - here we configure the transaction. We specify the manager to use and if rollback should be the default thing to do when a transaction ends (a bit explicit - the default is true anyhow)

@Transactional - all methods must run in transactions so that their effects can be rolled back

Summary

We removed an additional thin layer of JpaTemplate.

Many sites (e.g. the official Hibernate site) will tell you you cannot use @PersistenceContext in Java SE applications - well we just did, thanks to Spring (thanks to PersistenceAnnotationBeanPostProcessor).

Programmatic vs. Declarative

Spring offers two ways of handling transactions: programmatic and declarative. If you are familiar with EJB transaction handling, this corresponds to bean-managed and container-managed transaction management.

Programmatic means you have transaction management code surrounding your business code. That gives you extreme flexibility, but is difficult to maintain and, well, boilerplate.

Declarative means you separate transaction management from the business code. You only use annotations or XML based configuration.

We say that:

programmatic management is more flexible during development time but less flexible during application life

declarative management is less flexible during development time but more flexible during application life

If you apply the annotation on the bean (i.e. on the class level), every public method will be transactional.

Note: Remember that it affects only Spring managed data sources (in our example whatever comes from the entity manager factory). If you get your data source outside of Spring, the @Transactional annotation will have no effect.

Where to put @Transactional

You should put this annotation on your business logic methods (service methods), not on DAO methods (unless you know what you're doing) - that's a rule of thumb. Normally a business method will call many DAO methods and those calls make only sense when made together, every single one or none (atomicity).

Transaction Propagation

Whenever a transactional method is called, a decision is made - what to do with the transaction. Create a new one? Use an existing one if it exists, otherwise create a new one? Use an existing one only if it exists, otherwise fail?

To make it possible for you to specify this, propagation behaviors exist.

REQUIRED - uses the existing transaction. If it doesn't exist, creates a new one

REQUIRES_NEW - must start a new transaction. If there's an existing one, it should be suspended

SUPPORTS - if there is a transaction, runs in it. If there isn't, it just runs outside transaction context

NOT_SUPPORTED - must not run in a transaction. If there's an existing one, it should be suspended

MANDATORY - must run in a transaction. If there isn't one, it throws an exception

NEVER - must not run in a transaction. If there's an existing one, an exception will be thrown

NESTED - if there is a transaction, it runs withing the nested transaction of it. Otherwise it runs in its own

It is up to you to decide which behavior is best for each method. REQUIRED is the default one.

Note: If you are using the XML setup for transactions, you can also configure these behaviors there instead of Java code.

Transaction Isolation

Concurrent transactions cause problems that might be difficult to investigate.

Lost update - two transactions both update a row, the second transaction aborts, both changes are lost

Dirty read - reading changes that are not yet committed

Unrepeatable read - a transactions read twice the same row, getting different data each time

Phantom read - similar to the previous one, except that the number of rows changed

Now, the perfect solution to these problems is maximum isolation, but in reality this would cost too much resources and could lead to deadlocks. So, instead, you will set one of five isolation levels (where the fifth one is actually the maximum isolation level):

Think about it for a second! Are you sure you understand what it does?

If the doSomething method terminates its execution naturally, with no exceptions, the transaction is committed

If the doSomething method or any other method it calls throws any kind of exception that is caught within doSomething or this other method and not rethrown, the transaction is committed

If the doSomething method or any other method it calls throws any kind of checked exception that is not caught or is caught and rethrown, the transaction is committed (so everything up to the moment of the exception being thrown is persisted)

If the doSomething method or any other method it calls throws any kind of unchecked exception that is not caught, the transaction is rolled back (so nothing saved in this transaction is persisted)

That's the default behavior. However, you may want to change it for certain cases:

JPA is only an API that requires a concrete implementation - we are using Hibernate here; you can use anything you want though as long as it is JPA 2.0 compliant. There are going to be no references to Hibernate in Java code

Persistence unit name is Dogs

hibernate.hbm2ddl.auto is set to create so that tables are only dropped when the application starts (and not where it shuts down) - you can investigate the content after shutdown (see how)

If you are looking for more information on HSQLDB, I wrote an article on HSQLDB and Eclipse where you can find some basic information (and this project has it set up exactly the same way).

Spring beans file - applicationContext.xml

This file is located directly in the classpath, I put it in src/main/resources (a Maven convention).

Entity Manager Factory

This is the JPA way of doing things in Spring. You do not declare a data source in your Spring beans file (it is declared in persistence.xml), but an entity manager factory instead. You give it the persistence unit name ("Dogs").

JPA template

We are going to use JPA template from our DAO classes. It makes DAO operations a bit easier (and sometimes it doesn't). Note that Hibernate Template also exists and has methods that are easier to use than those of JPA template, but I promised no Hibernate in the code.

It should first print false, then true - and lots of useful (or not) logs.

I hope this gives you an idea how to build such an application.

Issues and Solutions

Missing artifact org.hibernate:hibernate:jar:3.5.3-Final:compile

It happened to me that Maven was unable to fetch this lib - hibernate-3.5.3-Final.jar. If it happens to you, just download the correct (3.5.3-Final) Hibernate distribution ZIP/TAR file and put the missing file (it might be called hibernate3.jar) into your local repository manually (or your company corporate repository).

Caused by: java.io.FileNotFoundException: class path resource [applicationContext.xml] cannot be opened because it does not exist

Might happen when you run your application from Eclipse before you packaged it with Maven. That's a classpath issue. Just do Maven -> Clean and Maven -> Package, and then launch the application again.

Exception in thread "main"
javax.persistence.PersistenceException:
No Persistence provider for
EntityManager named helloworld

[Line breaks added for readability]

There can be at least three different causes, one not so obvious.

Your persistence.xml file cannot be found - it does not reside in a folder called META-INF that is located in classpath root

Your persistence.xml file does not contain a persistence-unit with the given name (helloworld in this case)

The tricky one - you have a problem with dependencies

I had problem #3. The error message suggested something different, but actually my dependencies were screwed up. Here's an example of correct dependencies (hibernate-entitymanager was the one causing problems).

You have a no-argument constructor (that's a JPA requirement). You have an ID annotated with @Id, the class is annotated with @Entity and you have the @Table annotation so that an alternative table name can be specified.

Note that in the peristence.xml file content was created automatically. It should look more or less like this.

The hibernate.hbm2ddl.auto paramater is set to create because it is more convenient for us in this situation. It means that the database is dropped and recreated on application start-up, but is not dropped after it shuts down, so that you can investigate the content of it after the application has run.

Alright! Something got saved in the database and was successfully retrieved. Note that each persisting operation and retrieval occur in separate transactions. If you don't like that, there are several solutions available:

Create business classes instead of DAO classes that handle persistence AND business logic

Use sf.getCurrentSession().beginTransaction() where sf is session factory - but that makes you use Hibernate directly, not just under the hood

And, if you refresh/clean the project now, you should see that the previous error (Table "DOGS" cannot be resolved) is now gone. You may have to do more Eclipse magic, like closing the database connection and opening it again etc.

Creating an entity from an existing database table

You can create entities from existing database tables, but it doesn't work so well, as I will show.

Imagine you already have an existing table in the database. Or, better, let's create one.

In the next window of the wizard it is possible to specify the associations. Please look at the following screenshot:

It is smart enough to suggest the right columns:

Now it's time to specify the kind of association. In our case, each dog has many toys.

If you now click Finish, there's a surprise waiting for you. The list of associations remains empty.

I don't understand why. The class it generates doesn't even compile, and has no track of the relationship. Even when I created the relationship in JPA myself:

@ManyToOne
public Dog getDog() {
return this.dog;
}

... and let it create the table in the database, and then from this Hibernate-made table I repeated the procedure - I got the same disappointing result. What am I missing here?

Hand-written associations

I gave up generating entities using Eclipse JPA, I wrote them myself. Look at the previous code sample. This gives a one way association from a toy to a dog. A dog entity is unaware of its toys. You would need a separate DAO (ToysDAO) to even save toys.

We can change this by making the association bidirectional. In the Dog class:

2010-06-22

In this tutorial I will show you how to create an Eclipse JPA project for a desktop application, avoiding some common pitfalls. You're going to need Eclipse Galileo 3.5.2 for this (the JEE edition) but other versions should work too (the UI may look different). I am also going to use HSQLDB, Java-based database. Note that my application doesn't use Maven.

Step #1: Open the Eclipse JPA project wizard

The first step is to open the Eclipse JPA project wizard. If it doesn't show up in the menu (as shown in the screenshot above), simply go to File -> New -> Other... and then under JPA there should be a "JPA Project" to select. If it isn't, you probably aren't using the correct version of Eclipse.

Step #2: Project name and runtime configuration

Give the project a name (I called mine "Dogs"). I did not select a target runtime because the application is supposed to be a desktop one. I also selected the Default Configuration.

Step #3: Source folders

We're going to leave this with the default value.

Step #4: Platform, JPA implementation

This part gave me a bit of a headache. As a long time Maven user I could not agree to referencing project libraries from my local disk ("C:\Michal\Development\Libs\...") - it would never work for anyone else and not even for me on a different computer. And that's exactly what the wizard suggests you do!

So just select the "Hibernate" platform and "Disable Library Configuration" for JPA implementation. Eclipse displays a neat warning as if it suspected we didn't know what we are doing.

Now it's time to set up the connection.

Step #5: Adding the connection

Click "Add connection...". Give it a name and click next.

Fill in the data. We are using HSQLDB in the Server mode, so the DB location is hsql://localhost/dogsdb and it makes the URL become jdbc:hsqldb:hsql://localhost/dogsdb.

Note: HSQLDB in Server mode means that you need to explicitly start the server, it won't work if you just run the application (unlike the non-server file based and memory modes).

The server isn't up yet, so you will get an error when trying to connect:

I would not check "Add driver library to build path".

Click "Finish".

Step #6: The HSQLDB

If the connection error annoyed you, no worries. We will take care of that. Create a folder called "lib" in your project and drop hsqldb-1.8.0.7.jar in there. You can get it from HSQLDB Download site (a newer version like 1.8.1.12 is fine, an older one should be OK too).

Now, let's add two batch files (or shell files; I am Windows based here at the moment) to the project root. Let's call them startDb.bat (startDb.sh for Unix based systems) and startManager.bat (startManager.sh). Here's the content.

[Line breaks added for readability] See how the database name corresponds with what you provided earlier?

If you run this, a folder called db will be created and database files put there. The java command must be in system PATH.

Note: You should run it from your system explorer (Windows Explorer, Finder etc.) and not from Eclipse. If you run it from Eclipse it is bound to fail because Eclipse runs it from its own root folder (where it is installed) and not from the project root.

[Line breaks added for readability] Now, this is for the Database Manager from HSQLDB. If you run this, you should set connection parameters as follows and it will let you in.

Step #7: Project classpath

You must add some libs to the project - namely Hibernate libs with certain dependencies. You can get them from the official distribution. Just drop them in the lib folder where hsqldb already is. Select them all (except for hsqldb) and right click them. Then Build path -> Add to build path.

You need the following jar files in the classpath:

antlr-2.7.6.jar

commons-collections-3.1.jar

dom4j-1.6.1.jar

hibernate3.jar

hibernate-jpa-2.0-api-1.0.0.Final.jar

javassist-3.9.0.jar

jta-1.1.jar

slf4j-api-1.5.8.jar

Now, you might get the following warning:

Classpath entry /Dogs/lib/dom4j-1.6.1.jar
will not be exported or published.
Runtime ClassNotFoundExceptions may result

[Line breaks added for readability]

The only thing I could do about it was to click each of those issues (one per jar in classpath) and go for the quick fix (right click it and select "Quick Fix"; or select it and hit Ctrl+1). Then I would select "Exclude the associated raw classpath entry from the set of potential publish/export dependencies." Selecting the other option ("Mark the associated raw classpath entry as a publish/export dependency") would give me another warning that I couldn't fix.

If you plan to export your application with the Eclipse Export tool you might want to add hsqldb-1.8.0.7.jar to the build path too and it will not be exported.

Summary

In this tutorial we created a simple Eclipse JPA project thanks to which we can benefit from Eclipse JPA support. What is important, the project itself has all the dependencies it needs, so they are not referenced from an external location. In the next part we will create some simple JPA code.