Filed under: hibernate

Quartz and Hibernate

I recently put Quartz into a project with Hibernate and ran into a strange problem. Every time I'd try to create a new JobDetail, I would get this exception:

java.lang.NoSuchMethodError: org.apache.commons.collections.SetUtils.orderedSet (Ljava/util/Set;)Ljava/util/Set;

I had the right version of Jarkarta Commons Collections in my classpath. I couldn't figure out what was going on for quite some time. I eventually realized that too many JAR files from Hibernate were being pulled in. Hibernate ships with a file called checkstyle-all.jar which is not used at runtime. It runs checkstyle (obviously). However, this includes all of checkstyle's dependencies, including an old version of commons collections. Once I removed it from the classpath (or at least got the real commons JAR earlier in the classpath) everything worked fine.

Inherited Embedded IDs

I recently ran into a difficult problem using Hibernate through JPA. I had two tables that were effectively the same but had to be separate for various database optimization reasons. The two tables had equivalent primary key definitions. That definition was a composite key based off of two foreign keys. Here is the general view of what I had.

@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class Entity {
    private IdClass _id;
    
    @EmbeddedId
    public IdClass getId() { return _id; }
    protected void setId(IdClass id) { _id = id; }
}

@Entity
@Table(name="table_a")
public class EntityA extends Entity {}

@Entity
@Table(name="table_b")
public class EntityB extends Entity {}

@Embeddable
public class IdClass {
// a couple foreign keys
}

This caused some problems. There is a point in normal execution where we have to actually copy the values from one table to another. It turns out that the IDs are actually bound to a table as well as long as they are attached to the context. This made things very messy. I decided that it would be better for each of them to have their own IDs anyhow. I thought it would be easy to use inheritance on this. It was somewhat easy in the end, but there were definitely issues. Here is how I finally got everything working.

@MappedSuperclass
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class Entity<T extends IdClass> {
    private T _id;
    
    @Transient
    public T getId() { return _id; }
    protected void setId(T id) { _id = id; }
}

@Entity
@Table(name="table_a")
public class EntityA extends Entity<IdClassA> {
    @EmbeddedId
    public IdClassA getId() { return super.getId(); }
}

@Entity
@Table(name="table_b")
public class EntityB extends Entity<IdClassB> {
    @EmbeddedId
    public IdClassB getId() { return super.getId(); }
}

@MappedSuperclass
@Embeddable
public abstract class IdClass {
// a couple foreign keys
}

public class IdClassA extends IdClass {}

public class IdClassB extends IdClass{}

The secret sauce here is the getId() method in the two entity child classes. I tried several things before finding this. Without this, you will likely be getting ClassCastExceptions every time you try to retrieve one of these things. It would appear that it is retreiving something that is of the parent type but not the child. I don't really understand what is going on there. The other error I got (and can't remember the exact flavor of incantation to get it) involved a message saying that IdClassA had no id property.

All in all, it isn't a terribly complex solution, but it took a while to find the right combination of annotations and the magic getId() method in the subclasses. That was the hardest part to find.

jMock and interfaces galore

The ASM pain is now over with, I think. I've tried various mocking packages and settled on jMock. There really isn't a huge difference between jMock and EasyMock. In fact, they both use CGLib. However, there are some subtle differences.

First, jMock integrates better with JUnit 4.4. This is pretty cool. If I were so inclined, I could specify exactly which mocked methods are called and the sequence in which they should be called. I can even specify several sequences. Then, at the end of each test, it would check the expectations and throw an exception. I also like the smaller overhead of jMock. EasyMock forces you to perform a lot more setup overhead for each mocked object. This can be pretty annoying. jMock simlpy makes me annotate the class, create a Mockery (I love the jMock terminology), create the mock, put it where it belongs, and create Expectations. There is no need to call a method to start the replay because I'm not calling the methods on the actual mocked object to set the expectations.

Second, jMock uses CGLib but provides a dependency-free version of the JAR. This allows me to more safely put it at the front of the classpath. I have used this a bit with hibernate being called and see no issues.

Finally, if you need to mock a class rather than an interface, you have to use an Imposteriser. That has to be the best word that I've ever typed into code. Therefore, jMock wins style points.

Of course, I have done a lot of work on this and just now realized that I'm only mocking a single class. That class is used in a package that doesn't need to call hibernate in the unit tests. That makes my life a lot easier. This is mostly due to the fact that one of the custom service dispatchers we use is a final class. That made it impossible to mock. I ended up creating a singleton that my unit tests could replace. Since I wrote it, I could make an interface for it. At this point, the only thing class I'm mocking is InitialLdapContext. I think this is a pretty good situation.

There you have it. ASM pain still exists in the world, but I've found my way around it.

ASM is not my friend

I am currently working on cleaning up our unit tests at work. Our service code calls several other services within the company. However, we don't necessarily want those called during the unit tests. Development-level services are notoriously unreliable. I have started using EasyMock 2.3 to mock the interfaces for our dependencies. However, some of the things we need to do require mocking classes. Enter EasyMock Class Extensions 2.3.

I first tried to use the class extensions and immediately got a NoSuchMethodError when running my tests. To make a long (and painful) story short, Hibernate imports a version of ASM that is newer than the one used by EasyMock Class Extensions. Luckily, the particular package I\'m using doesn't need Hibernate. It only gets included in the classpath because it is in the list of transitive dependencies. However, once I start using Hibernate, I fear it will not work. I will be trying this out today. If anyone has any ideas, though, please let me know.

web
stats