In my previous post: ClassCastException is a Proxy Problem I talked about how Hibernate tries to optimize the loading of objects by lazily fetching the parent class object, and returning a proxy for the subclass instead of the actual subclass, thus causing a ClassCastException when the proxy is cast to the actual subclass. The proxied object doesn’t contain member variables or a representation of the subclass’ methods, only the parent’s.
My proposed solution to the ClassCastException was to add an enum for each subclass that would be stored in the parent class, and used to figure out the subclass of the object and retrieve the actual instance from the database. I created a method that returned a generic in either the parent class or DAO to keep the code clean. However, after implementing this solution I discovered a bug that had been introduced a long time ago; the wrong enum value had been stored for the subclass. The enum value was for SpanishCourse, so when I retrieved the object from the database, I cast it to SpanishCourse, when it was actually a MathCourse, causing a ClassCastException. While I could just fix the data in the database I wasn’t satisfied with this pattern, because it broke the OO principle of encapsulation; the parent class had to have knowledge of its subclasses’ enum value.
I searched for awhile and found this article: Hibernate Proxies and Polymorphism. I do think this is a good solution to the proxy problem, but, it pertains to lazily initializing the member variables of a subclass, and is a bit overkill for what I needed. So then I went back to thinking about a solution for my specific problem. How was I going to load the subclass instance without it being proxied?
Here are a few things to note about Hibernate:
- Hibernate proxies an object inside a database transaction, the object is lazily loaded and it contains no data. The data can be lazily fetched once a superclass method is called, as long as the database transaction is still open, otherwise you will get a LazyLoadingException.
- When calling a superclass’ abstract method, Hibernate will lookup the implementation of the method corresponding to the appropriate subclass.
- Hibernate cannot resolve subclass methods that are not part of the abstract parent class.
Here is my proposed solution:
public abstract class Course {
public abstract class void load();
….
}
Each subclass’ implementation of the load method will handle lazily fetching the member variables of the class that are needed. This avoids the need for a cast altogether. If you need to return the object you can continue to use generics. If you need the actual instance of the subclass then you will need to implement the visitor pattern described in the Hibernate articles. Here is a simple example:
public interface CourseVisitor {
public MathCourse getMathCourse();
public SpanishCourse getSpanishCourse();
…
}
public abstract class Course {
public abstract void load(CourseVisitor visitor);
….
}
public class MathCourse {
public void load(CourseVisitor visitor) {
return visitor.setMathCourse(this);
}
…
}
public class CourseVisitorImpl implements CourseVisitor {
private MathCourse mc;
private SpanishCourse s;
public MathCourse getMathCourse() { return mc; }
public SpanishCourse getSpanishCourse(){ return s; }
public void setMathCourse(MathCourse mc) { this.mc = mc; }
public void setSpanishCourse(SpanishCourse s) { this.s = s; }
…
}
The Course object will be a proxy, and when the load method is called on it, passing in a new instance of CourseVisitor the subclass will be instantiated, and can be retrieved using the CourseVisitor’s getter method.
This solution introduces more code, and requires that the CourseVisitor be updated with new subclasses of Course. However, the visitor pattern is the correct solution to use, because it makes use of language concepts instead of relying on data such as an enum value, which can be error prone.