On a Spring-Hibernate project I’m working on we use a custom way of filling and clearing specific database tables in unit tests. Yes, something like DbUnit but ofcourse completely hand-made, tailored to our situation 🙂 In a TestNG testcase we do something like this:

@BeforeMethod
public void setUp() {
	DatabaseFiller.fillTable("UserAccounts", "useraccounts.txt");
}

@AfterMethod
public void tearDown() {
	DatabaseFiller.clearTable("UserAccounts");
}

It bugged a little bit, that in this testcase e.g. we’re testing operations on the User entity (e.g. User.class) – which for database specific reasons was mapped to a “UserAccounts” table – but we still have to find the actual table name for ourselves. There are many reasons to map explicitly to a certain table name: prevent database reserved keywords (“user” is often reserved), enforce a naming convention (“tbl_user”), etc. If no specifics have been set for Hibernate it will take its own naming convention, else one could indicate the desired name through the javax.persistence.Table annotation – if using JPA.

Our User entity is described like this:

@Entity
@Table(name = "UserAccounts")
public class User {
...

which results in a “UserAccounts” table being used by the underlying provider – instead of the default naming convention.

The issue is that with a lot of entities a developer can not guess from the entity itself what table is being used actually. He can guess, due to clear company development guidelines, a table name based on the entity name, but he only knows for sure if looking at the code. In our example, clearing tables based on the table name can break if – for whatever reason – an entity is renamed and no specific table name was set. Ofcourse, renaming database tables can break a lot more than just the clearing of it in a unit test – it usually doesn’t happen very often and if it does, it needs to be done with care.

Next to that, it’s just an inconvenience for the developer having to look up the table name when he already knows the entity he wants to clear from the system. Woudn’t it be great to point to the entity itself? Hence, let us introduce a better solution using Hibernate metadata!

Hibernate metadata is all about the accessing the Hibernate runtime metamodel, including collections and entities. The ClassMetadata interface allows access to an entity’s identifier, name, properties and versioning information – which is thus perfectly capable of telling us the table name. This metadata is retrieved for an entity through Hibernate’s session factory, which for instance can be exposed like this in our Spring configuration:

<bean id="sessionFactory" factory-bean="entityManagerFactory" factory-method="getSessionFactory" />

Our earlier used Database class can be wired together with this session factory – if also loaded as a Spring bean and pointing to the session factory bean. Here’s an example – which for brevity omits the getters and setters.

public class DatabaseFiller {

    @PersistenceContext
    private EntityManager entityManager;

    private SessionFactory sessionFactory;

We can now modify it with a method which returns the table name for an entity class:

/**
 * Return the table name used by Hibernate for given <code>persistentClass</code>.
 *
 * @param persistentClass
 *            The class of the entity
 * @return the table name
 * @throws NullPointerException
 *             if the <code>persistentClass</code> is null
 * @throws IllegalArgumentException
 *             if the <code>persistentClass</code> has no class metadata to get the table name from
 */
public String getTableName(Class persistentClass) {
	if (persistentClass == null) {
		throw new NullPointerException("persistent class");
	}

	ClassMetadata classMetadata = sessionFactory.getClassMetadata(persistentClass);
	if (classMetadata == null) {
		throw new IllegalArgumentException("No class metadata.");
	}

	if (classMetadata instanceof AbstractEntityPersister) {
		return ((AbstractEntityPersister) classMetadata).getTableName();
	} else {
		throw new RuntimeException("Unknown implementation.");
	}
}

Ofcourse, you can do whatever you want if things fail, return null instead of an exception for instance. Using Spring it’s only more easy to hook up the required beans, but it’s not a prerequisite.

Now we can alter our DatabaseFiller.clearTable method to accept a class,  internally lookup the table name and perform the same logic it always does. The developer isn’t bothered anymore to lookup the table name him or herself which is basically the point of this excercise.

@AfterMethod
public void tearDown() {
	DatabaseFiller.clearTable(User.class);
}

To summarize, Hibernate’s metadata API is a very powerful aid in getting the runtime information you might possibly need on your datamodel.

Advertisements