More eager/lazy fetching optimisations.

This commit is contained in:
Ben Alex 2005-05-13 07:14:32 +00:00
parent c9fc2684ad
commit 32646b5581
7 changed files with 143 additions and 20 deletions

View File

@ -112,7 +112,9 @@ public interface Dao<E extends PersistableEntity> {
public List<E> findId(Collection<Serializable> ids); public List<E> findId(Collection<Serializable> ids);
/** /**
* Load a persistent instance by its identifier. * Load a persistent instance by its identifier, although some properties
* may be lazy loaded depending on the underlying DAO implementation and/or
* persistence engine mapping document.
* *
* @param id the identifier of the persistent instance desired to be * @param id the identifier of the persistent instance desired to be
* retrieved * retrieved
@ -121,6 +123,17 @@ public interface Dao<E extends PersistableEntity> {
*/ */
public E readId(Serializable id); public E readId(Serializable id);
/**
* Loads a persistent instance by its identifier, along with any
* lazy loaded properties associated with that instance.
*
* @param id the identifier of the persistent instance desired to be
* retrieved
*
* @return the request item, or <code>null</code> if not found
*/
public E readPopulatedId(Serializable id);
/** /**
* Find persistent instances with properties matching those of the passed * Find persistent instances with properties matching those of the passed
* <code>PersistableEntity</code>. * <code>PersistableEntity</code>.

View File

@ -19,6 +19,7 @@ import net.sf.acegisecurity.domain.PersistableEntity;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import java.lang.reflect.Method;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
@ -51,6 +52,39 @@ public class EvictionUtils {
} }
} }
/**
* Evicts the <code>PersistableEntity</code> using the passed
* <code>Object</code> (provided that the passed <code>Object</code>
* implements <code>EvictionCapable</code>), along with expressly
* evicting every <code>PersistableEntity</code> returned by the
* <code>PersistableEntity</code>'s getters.
*
* @param daoOrServices the potential source for
* <code>EvictionCapable</code> services (never <code>null</code>)
* @param entity to evict includnig its getter results (can be <code>null</code>)
*/
public static void evictPopulatedIfRequired(Object daoOrServices,
PersistableEntity entity) {
EvictionCapable evictor = getEvictionCapable(daoOrServices);
if (evictor != null && entity != null) {
evictor.evict(entity);
Method[] methods = entity.getClass().getMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().startsWith("get") && methods[i].getParameterTypes().length == 0) {
try {
Object result = methods[i].invoke(entity, new Object[] {});
if (result instanceof PersistableEntity) {
evictor.evict((PersistableEntity) result);
}
} catch (Exception ignored) {}
}
}
}
}
/** /**
* Evicts each <code>PersistableEntity</code> element of the passed * Evicts each <code>PersistableEntity</code> element of the passed
* <code>Collection</code> using the passed <code>Object</code> (provided * <code>Collection</code> using the passed <code>Object</code> (provided

View File

@ -16,6 +16,7 @@
package net.sf.acegisecurity.domain.hibernate; package net.sf.acegisecurity.domain.hibernate;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -24,6 +25,7 @@ import net.sf.acegisecurity.domain.dao.Dao;
import net.sf.acegisecurity.domain.dao.EvictionCapable; import net.sf.acegisecurity.domain.dao.EvictionCapable;
import net.sf.acegisecurity.domain.dao.InitializationCapable; import net.sf.acegisecurity.domain.dao.InitializationCapable;
import net.sf.acegisecurity.domain.dao.PaginatedList; import net.sf.acegisecurity.domain.dao.PaginatedList;
import net.sf.acegisecurity.domain.validation.ValidationManager;
import org.hibernate.Criteria; import org.hibernate.Criteria;
import org.hibernate.EntityMode; import org.hibernate.EntityMode;
@ -35,9 +37,12 @@ import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order; import org.hibernate.criterion.Order;
import org.hibernate.metadata.ClassMetadata; import org.hibernate.metadata.ClassMetadata;
import org.hibernate.type.Type; import org.hibernate.type.Type;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.validation.BindException;
/** /**
* Generics supporting {@link Dao} implementation that uses Hibernate 3 for persistence. * Generics supporting {@link Dao} implementation that uses Hibernate 3 for persistence.
@ -53,6 +58,9 @@ public class DaoHibernate<E extends PersistableEntity> extends HibernateDaoSuppo
/** The class that this instance provides services for */ /** The class that this instance provides services for */
private Class supportsClass; private Class supportsClass;
/** Enables mutator methods to validate an object prior to persistence */
private ValidationManager validationManager;
//~ Methods ================================================================ //~ Methods ================================================================
public void setSupportsClass(Class supportClass) { public void setSupportsClass(Class supportClass) {
@ -63,15 +71,33 @@ public class DaoHibernate<E extends PersistableEntity> extends HibernateDaoSuppo
return supportsClass; return supportsClass;
} }
public ValidationManager getValidationManager() {
return validationManager;
}
public void setValidationManager(ValidationManager validationManager) {
this.validationManager = validationManager;
}
public E create(E value) { public E create(E value) {
Assert.notNull(value); Assert.notNull(value);
validate(value);
getHibernateTemplate().save(value); getHibernateTemplate().save(value);
return readId(value.getInternalId()); return readId(value.getInternalId());
} }
protected void validate(E value) throws DataIntegrityViolationException {
try {
validationManager.validate(value);
} catch (BindException bindException) {
throw new DataIntegrityViolationException("Entity state is invalid", bindException);
}
}
public E createOrUpdate(E value) { public E createOrUpdate(E value) {
Assert.notNull(value); Assert.notNull(value);
validate(value);
getHibernateTemplate().saveOrUpdate(value); getHibernateTemplate().saveOrUpdate(value);
return readId(value.getInternalId()); return readId(value.getInternalId());
@ -79,6 +105,7 @@ public class DaoHibernate<E extends PersistableEntity> extends HibernateDaoSuppo
public void delete(E value) { public void delete(E value) {
Assert.notNull(value); Assert.notNull(value);
validate(value);
getHibernateTemplate().delete(value); getHibernateTemplate().delete(value);
} }
@ -104,6 +131,37 @@ public class DaoHibernate<E extends PersistableEntity> extends HibernateDaoSuppo
return (E) getHibernateTemplate().get(supportsClass, id); return (E) getHibernateTemplate().get(supportsClass, id);
} }
public E readPopulatedId(Serializable id) {
Assert.notNull(id);
E result = readId(id);
initializeAllZeroArgumentGetters(result);
return result;
}
/**
* Locates every <code>get*()</code> method against the passed entity
* and calls it. This method does not nest its initialization beyond
* the immediately passed entity.
*
* <p>For example, a Foo object might provide a getBar() method.
* Passing the Foo instance to this method will guarantee getBar() is
* available to the services layer. However, it getBar() returned a Bar
* which in turn provided a getCar() method, there is NO GUARANTEE
* the getCar() method will be initialized.
*
* @param entity for which its immediate getters should be initialized
*/
protected void initializeAllZeroArgumentGetters(E entity) {
Method[] methods = entity.getClass().getMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().startsWith("get") && methods[i].getParameterTypes().length == 0) {
try {
Hibernate.initialize(methods[i].invoke(entity, new Object[] {}));
} catch (Exception ignored) {}
}
}
}
public PaginatedList<E> scroll(E value, int firstElement, public PaginatedList<E> scroll(E value, int firstElement,
int maxElements, String orderByAsc) { int maxElements, String orderByAsc) {
Assert.notNull(value); Assert.notNull(value);
@ -134,6 +192,7 @@ public class DaoHibernate<E extends PersistableEntity> extends HibernateDaoSuppo
public E update(E value) { public E update(E value) {
Assert.notNull(value); Assert.notNull(value);
validate(value);
getHibernateTemplate().update(value); getHibernateTemplate().update(value);
return readId(value.getInternalId()); return readId(value.getInternalId());
@ -146,6 +205,7 @@ public class DaoHibernate<E extends PersistableEntity> extends HibernateDaoSuppo
*/ */
protected final void initDao() throws Exception { protected final void initDao() throws Exception {
Assert.notNull(supportsClass, "supportClass is required"); Assert.notNull(supportsClass, "supportClass is required");
Assert.notNull(validationManager, "validationManager is required");
Assert.isTrue(PersistableEntity.class.isAssignableFrom(supportsClass), Assert.isTrue(PersistableEntity.class.isAssignableFrom(supportsClass),
"supportClass is not an implementation of PersistableEntity"); "supportClass is not an implementation of PersistableEntity");
initHibernateDao(); initHibernateDao();

View File

@ -19,6 +19,7 @@ import net.sf.acegisecurity.domain.validation.IntrospectionManager;
import net.sf.acegisecurity.domain.validation.ValidationRegistryManager; import net.sf.acegisecurity.domain.validation.ValidationRegistryManager;
import org.hibernate.EntityMode; import org.hibernate.EntityMode;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.SessionFactory; import org.hibernate.SessionFactory;
@ -120,11 +121,12 @@ public class IntrospectionManagerHibernate implements IntrospectionManager,
Type propertyType = classMetadata.getPropertyType(propertyNames[i]); Type propertyType = classMetadata.getPropertyType(propertyNames[i]);
// Add this property to the List of Objects to validate // Add this property to the List of Objects to validate
// only if a Validator is registered for that Object // only if a Validator is registered for that Object AND
// the object is initialized (ie not lazy loaded)
if (this.validationRegistryManager.findValidator( if (this.validationRegistryManager.findValidator(
propertyType.getReturnedClass()) != null) { propertyType.getReturnedClass()) != null) {
Object childObject = classMetadata.getPropertyValue(parentObject, propertyNames[i], EntityMode.POJO); Object childObject = classMetadata.getPropertyValue(parentObject, propertyNames[i], EntityMode.POJO);
if (childObject != null) { if (childObject != null && Hibernate.isInitialized(childObject)) {
allObjects.add(childObject); allObjects.add(childObject);
} }
} }

View File

@ -129,7 +129,9 @@ public interface Manager<E extends PersistableEntity> {
public List<E> findId(Collection<Serializable> ids); public List<E> findId(Collection<Serializable> ids);
/** /**
* Load a persistent instance by its identifier. * Load a persistent instance by its identifier, although some properties
* may be lazy loaded depending on the underlying DAO implementation and/or
* persistence engine mapping document.
* *
* @param id the identifier of the persistent instance desired to be * @param id the identifier of the persistent instance desired to be
* retrieved * retrieved
@ -138,6 +140,17 @@ public interface Manager<E extends PersistableEntity> {
*/ */
public E readId(Serializable id); public E readId(Serializable id);
/**
* Loads a persistent instance by its identifier, along with any
* lazy loaded properties associated with that instance.
*
* @param id the identifier of the persistent instance desired to be
* retrieved
*
* @return the request item, or <code>null</code> if not found
*/
public E readPopulatedId(Serializable id);
/** /**
* Find persistent instances with properties matching those of the passed * Find persistent instances with properties matching those of the passed
* <code>PersistableEntity</code>. * <code>PersistableEntity</code>.

View File

@ -123,6 +123,11 @@ public class ManagerImpl<E extends PersistableEntity> extends ApplicationObjectS
return dao.readId(id); return dao.readId(id);
} }
public E readPopulatedId(Serializable id) {
Assert.notNull(id);
return dao.readPopulatedId(id);
}
public PaginatedList<E> scroll(E value, int firstElement, public PaginatedList<E> scroll(E value, int firstElement,
int maxElements) { int maxElements) {
Assert.notNull(value); Assert.notNull(value);

View File

@ -8,12 +8,12 @@ will need to wire a suitable {@link IntrospectionManager} against the
<code>ValidationManager</code> so that children of a domain object presented <code>ValidationManager</code> so that children of a domain object presented
for validation can be identified and in turn also validated. for validation can be identified and in turn also validated.
<p>The {@link ValidationInterceptor} and {@link ValidationAdvisor} should be <p>The {@link ValidationInterceptor} and {@link ValidationAdvisor} are provided,
used against each of your data access object (DAO) mutator methods, such as although their use is not recommended against DAOs given many <code>Validator</code>s
<code>SomeDao.create(Object)</code> and <code>SomeDao.update(Object)</code>. require a DAO and this will cause a loop that results in the DAO not being
The interceptor will cause the <code>Object</code> to be presented to the advised. Instead your DAO implementations should have their mutator methods
<code>ValidationManager</code>, thus ensuring the domain object instance is in pass the object to the <code>ValidationManager</code> prior to persistence. This
a valid state before being persisted.</p> is a non-AOP approach, but represetns a practical solution.</p>
<p>If you domain objects themselves wish to ensure they are in a valid state <p>If you domain objects themselves wish to ensure they are in a valid state
prior to internal business methods being invoked, it is suggested they provide prior to internal business methods being invoked, it is suggested they provide
@ -32,14 +32,10 @@ directly, as it ignores classes that do not implement
<p>Finally, sometimes <code>Validator</code>s might need to perform queries <p>Finally, sometimes <code>Validator</code>s might need to perform queries
against a persistence or services layer. For example, the <code>Validator</code> against a persistence or services layer. For example, the <code>Validator</code>
may be checking no other user has this username. If using an ORM tool such as may be checking no other user has this username, or comparing the object's old
Hibernate, it is recommended your <code>Validator</code>s subclass a common state to detect modifications that violate business rules. If using an ORM tool
abstract parent that provides an <code>evict(Object)</code> and such as Hibernate, it is recommended you use the <code>EvictionUtils</code>
<code>evict(Collection)</code> method. That way your <code>Validator</code>s static methods to remove objects from the session.
can utilise standard services layer or DAO methods as required to retrieve
other domain objects, and flush them from the session cache afterwards. Whilst
this is more a <code>Validator</code> design choice than something mandated by
this package, we have found it a worthwhile pattern in real-world applications.
</p> </p>
</body> </body>
</html> </html>