Reputation: 884
I'm trying to use hibernate to build up a local cache of data that I pull from source websites. I have the the objects configured with JPA (if it makes a difference) and can read/write them fine in a test application.
Now I want to move the code into a generic "Caching" class so that I can request an object from the Cache and process it as normal. I can read the object from the database and pass it back to the calling class, but when I try and access the collections in the object I get the dreaded lazy initialization exception. I know what causes this, the class that I read the object from commits the transaction after it's read the object from the database and before it returns the object to the calling class.
I have tried various methods to work around this, and the simplest (for me) seems to be to try and access all of the collections in the object to ensure they are loaded before closing the transaction and returning the object.
The problem with that approach is that I don't know the structure of the object that I am retrieving from the database (part of the benefit of Hibernate for me) and therefore I can't call the appropriate methods to load the data. How do I overcome this? I don't really want to do eager fetching of the objects as they may be used by other applications. I don't want to use hbm files if I can avoid it.
This is the call to the Cache class:
Series series = (Series) Cache.getFromCache(id, Series.class)
In the Cache class:
public static Object getFromCache(String key, Class clazz) {
Object dbObject = HibernateUtil.loadObject(clazz, key);
if (dbObject != null) {
logger.debug("Cache (Get): Got object (" + clazz.getSimpleName() + ") for " + key);
return dbObject;
}
}
And HibernateUtil does:
public static Object loadObject(Class clazz, Serializable key) {
Session session = sessionFactory.getCurrentSession();
Object dbObject;
try {
session.beginTransaction();
dbObject = clazz.cast(session.get(clazz, key));
} finally {
session.getTransaction().commit();
}
return dbObject;
Upvotes: 0
Views: 1702
Reputation: 691635
First of all, you could avoid a type cast by making your loadObject method parameterized:
public static <T> T loadObject(Class<T> clazz, Serializable key) {
Session session = sessionFactory.getCurrentSession();
T dbObject;
try {
session.beginTransaction();
dbObject = clazz.cast(session.get(clazz, key));
}
finally {
session.getTransaction().commit();
}
return dbObject;
}
Second: why do you open and commit the transaction in this method? Letting the caller open a transaction and commit it when he has finished using the object it has loaded from the cache would solve the problem.
Third: if you really want to let this method open and close the transaction, let it take an Initializer<T>
instance as parameter. This initializer would have the responsibility to initialize all the necessary associations before returning the entity.
public static <T> T loadObject(Class<T> clazz,
Serializable key,
Initializer<T> initializer) {
Session session = sessionFactory.getCurrentSession();
T dbObject;
try {
session.beginTransaction();
dbObject = clazz.cast(session.get(clazz, key));
initializer.initialize(dbObject);
}
finally {
session.getTransaction().commit();
}
return dbObject;
}
Upvotes: 1