There is one solution that works at least with little testing.
This solution uses new persister subclass, that enables setting lazy-value at runtime. It may sound little complicated but it is not, and there is no any special tricks used. No other functionality is either added to persister than ability to set lazy value, and it is direct subclass from BasicCollectionPersister, that is default implementation to persists collections.
I use, at this moment, at this project, Hibernate version 2.1, and if there is ready-made functionality to set lazy value at runtime in Hibernate version 3.x, I honestly recommend to use it instead.
Any comments?
How-to-use instruction is included in code-comments.
Code:
/* Copyright © 2005 Ohjelmistotalo Arkki
*
* Luotu 2.5.2005
*
*/
package test;
import java.util.HashMap;
import java.util.Map;
import net.sf.hibernate.MappingException;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.cache.CacheException;
import net.sf.hibernate.cfg.Configuration;
import net.sf.hibernate.collection.BasicCollectionPersister;
import net.sf.hibernate.engine.SessionFactoryImplementor;
import net.sf.hibernate.mapping.Collection;
/**
* Custom persister with you can set value of lazy property at runtime.
*
* Using this util class to chage lazy value at runtime should affect same way as
* changing lazy value from configuration file. But I made only quick testing, and I'm
* quite newbie with Hibernate, so I can't guarantee anything.
*
* To use:
*
* Add to Hibernate's entity mapping file(user.hbm.xml for example),
* to collection association configure element, property
* <code>persister="this class"</code>
*
* for example, (association beetween users and roles)
* < set name="roles" table="user_role" lazy="true" persister="test.MyBasicCollectionPersister" >
*
* As you can see, lazy value can be setted, and it is handled as default value.
*
*
* In runtime you then use this persister as follows:
*
* Let's say that we have entity class <code>entity.User</code>, which contains set
* of roles, property named <code>roles</code>. So role name for this association in
* Hibernate is, by default at least, <code>entity.User.roles</code>, that is name that
* persister is binded with. So here is how you can change lazy property of that association
* in runtime.
*
* <code>
* Session session = ...open session as normally...
* ...open transaction as normally ...
*
* MyBasicCollectionPersister.getPersister( session.getSessionFactory(), "entity.User.roles").setLazy( false );
*
* ...do query as normally...
*
* ...commit transaction...
* ...close session...
* </code>
*
* And that's it.
*
* You can set default value by calling setLazyDefault(), instead of setLazy(boolean).
*
* @author Kari Surakka
*/
public class MyBasicCollectionPersister extends BasicCollectionPersister {
// Map of maps
public static Map persiterMaps = new HashMap();
// Thread specific lazy values(java.lang.Boolean)
public final ThreadLocal lazyValues = new ThreadLocal();
/**
* Creates new persister, and binds created persister to role name, so it can
* be accessed by static method getPersister( SessionFactory, String ).
*
* @param collection
* @param cfg
* @param factory
* @throws MappingException
* @throws CacheException
*/
public MyBasicCollectionPersister(Collection collection, Configuration cfg, SessionFactoryImplementor factory)
throws MappingException, CacheException {
super(collection, cfg, factory);
System.out.println("Factory "+factory );
System.out.println("My persister "+this.getRole() );
this.addPersister( factory, this.getRole() );
}
/**
* Binds this persister to session factory and to role. It has to be binded to both,
* because there is one persister object(I think) per one session factory per role.
*
* @param sessionFactory binding key
* @param role binding key
*/
private void addPersister( SessionFactory sessionFactory, String role ) {
Map persiters = (Map)persiterMaps.get( sessionFactory );
if (persiters==null) {
persiters = new HashMap();
persiterMaps.put( sessionFactory, persiters);
}
// Exposin self reference, but should not be critical here
persiters.put( role, this);
}
/**
* Returns persister binded to session factory and to role. Returns null if there
* is no persiter binded.
*
* @param factory
* @param role
* @return binded persister
*/
public static MyBasicCollectionPersister getPersister( SessionFactory factory, String role ) {
Map persisters = (Map)persiterMaps.get( factory );
if (persisters!=null) {
return (MyBasicCollectionPersister)persisters.get( role );
} else {
return null;
}
}
/**
* Sets lazy-value for this persister for current thread
* @param lazy value to set
*/
public void setLazy( boolean lazy ) {
lazyValues.set( new Boolean(lazy) );
}
/**
* Sets default(configuration) value of lazy property for this persister.
* Functionality is same as <code>perister.setLazy( persister.isLazyDefault() )</code>
*/
public void setLazyDefaulft() {
this.setLazy( this.isLazyDefault() );
}
/**
* Returns default(configuration) value of lazy of this persister.
* @return default value of lazy
*/
public boolean isLazyDefault() {
return super.isLazy();
}
/**
* Returns lazy-value of this persister for current thread.
* If lazy value is not setted with setLazy(boolean) method, returns
* default(configuration) value of lazy property for this persister.
*
* @return runtime setted value of lazy property
*/
public boolean isLazy() {
Boolean boolObject = (Boolean)lazyValues.get();
boolean lazy = boolObject!=null ? boolObject.booleanValue() : this.isLazyDefault();
System.out.println("lazy value :"+lazy);
return lazy;
}
}