The problem
After years and years working with hibernate there are a few things I hate about it.The logging and the error messages are not clear and sometimes misleading (I will not expound now) and then the most common problem of all, the lazy load exception.
There are solutions like SessionInView pattern etc, but I and thousands of others wonder why it is so complicated or bad to make the Collection reconnect itself when needed.
I mean eclipse link does it also with a readonly connection.
The solution
So here is my solution and it finally seems to work. I actually implemented Collection class that does the reconnecting for me.For this to work i have to do several steps
In the hibernate mapping define field access.
You can do this with annotations or if there is hibernate mapping file it looks like this.
At the top do:
..
In the entity in the getter of a relationship I return a wrapper class
public class MyEntity{
//..
Collection
public Collection
return new LazyCollectionWithReconnect
(customers, this, new MyProjectRelationshipInitializer());
}
}
The implementation of the wrapper class is in the end of this post
The RelationShipInitializer
The interesting stuff is in MyProjectRelationshipInitializerWe have a generic interface:
public interface RelationshipInitializer {
void initializeRelationship(Object entity, Object propertyValue);
}
That must be implemented by the project
Here I lookup a Stateless sessionbean that has access to the PersistenceContext and does the loading of the relationship
public class MyProjectRelationshipInitializer implements RelationshipInitializer{
@Override
public void initializeRelationship(Object entity, Object propertyValue) {
MyGenericServiceLocator.inst().getGenericPersistenceService()
.initializeRelationship(entity, propertyValue);
}
}
The GenericPersistenceServiceBean that actually initializes the hibernate proxy
looks like this:@Stateless
@Local(GenericPersistenceService.class)
public class GenericPersistenceServiceBean implements GenericPersistenceService
{
@PersistenceContext
protected EntityManager em;
@Override
public void initializeRelationship(Object entity, Object relationShipObject) {
org.hibernate.Session session = getHibernateSession();
// the lock method adds the entity back into the Entitymanager makes it
//managed.
try {
session.lock(entity, LockMode.NONE);
if(!Hibernate.isInitialized(relationShipObject))
{
Hibernate.initialize(relationShipObject);
}
} catch (TransientObjectException e) {
//ignore object that are not saved yet
}
}
private Session getHibernateSession() {
//on web as
if (em.getDelegate() instanceof Session){
return (Session) em.getDelegate();
}
//in openejb
if (em.getDelegate() instanceof HibernateEntityManager){
HibernateEntityManager e = (HibernateEntityManager) em.getDelegate();
return (Session) e.getDelegate();
}
throw new IllegalStateException(String.format("Invalid type of entity manager %s (%s). Expected it to be org.hibernate.Session or org.hibernate.ejb.HibernateEntityManager", em, em.getClass()));
}
}
Note that the getHibernateSession() is a little bit J2EE Server dependent and might be different for your vendor.
The LazyColletionWithReconnect implementation
is rather boring:public class LazyCollectionWithReconnect
private Collection
private Object surroundingEntity;
private RelationShipInitializer relationShipInitializer;
public LazyCollectionWithReconnect(Collection
super();
this.srcCollection = srcCollection;
this.surroundingEntity = surroundingEntity;
this.relationShipInitializer = relationShipInitializer;
}
protected void reconnectIfNecessary() {
ensureRelationIsLoaded(
this.surroundingEntity,
this.srcCollection,
this.relationShipInitializer
);
}
public static boolean ensureRelationIsLoaded(
Object entity,
Object propertyValue,
RelationShipInitializer initializer) {
boolean wasLoaded = false;
// first check
if(!Hibernate.isInitialized(propertyValue)){
initializer
.initializeRelationship(entity, propertyValue);
wasLoaded = true;
}
return wasLoaded;
}
public boolean add(E e) {
reconnectIfNecessary();
return srcCollection.add(e);
}
public boolean addAll(Collection c) {
reconnectIfNecessary();
return srcCollection.addAll(c);
}
public void clear() {
reconnectIfNecessary();
srcCollection.clear();
}
public boolean contains(Object o) {
reconnectIfNecessary();
return srcCollection.contains(o);
}
public boolean containsAll(Collection c) {
reconnectIfNecessary();
return srcCollection.containsAll(c);
}
public boolean equals(Object o) {
return srcCollection.equals(o);
}
public int hashCode() {
return srcCollection.hashCode();
}
public boolean isEmpty() {
reconnectIfNecessary();
return srcCollection.isEmpty();
}
public Iterator
reconnectIfNecessary();
return srcCollection.iterator();
}
public boolean remove(Object o) {
reconnectIfNecessary();
return srcCollection.remove(o);
}
reconnectIfNecessary();
return srcCollection.removeAll(c);
}
reconnectIfNecessary();
return srcCollection.retainAll(c);
}
reconnectIfNecessary();
return srcCollection.size();
}
reconnectIfNecessary();
return srcCollection.toArray();
}
public
reconnectIfNecessary();
return srcCollection.toArray(a);
}
}
No comments:
Post a Comment