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);
/**
* 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
* retrieved
@ -121,6 +123,17 @@ public interface Dao<E extends PersistableEntity> {
*/
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
* <code>PersistableEntity</code>.

View File

@ -19,6 +19,7 @@ import net.sf.acegisecurity.domain.PersistableEntity;
import org.springframework.util.Assert;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
@ -52,6 +53,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
* <code>Collection</code> using the passed <code>Object</code> (provided
* that the passed <code>Object</code> implements

View File

@ -16,6 +16,7 @@
package net.sf.acegisecurity.domain.hibernate;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Collection;
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.InitializationCapable;
import net.sf.acegisecurity.domain.dao.PaginatedList;
import net.sf.acegisecurity.domain.validation.ValidationManager;
import org.hibernate.Criteria;
import org.hibernate.EntityMode;
@ -35,9 +37,12 @@ import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.type.Type;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.util.Assert;
import org.springframework.validation.BindException;
/**
* Generics supporting {@link Dao} implementation that uses Hibernate 3 for persistence.
@ -52,6 +57,9 @@ public class DaoHibernate<E extends PersistableEntity> extends HibernateDaoSuppo
/** The class that this instance provides services for */
private Class supportsClass;
/** Enables mutator methods to validate an object prior to persistence */
private ValidationManager validationManager;
//~ Methods ================================================================
@ -63,15 +71,33 @@ public class DaoHibernate<E extends PersistableEntity> extends HibernateDaoSuppo
return supportsClass;
}
public E create(E value) {
public ValidationManager getValidationManager() {
return validationManager;
}
public void setValidationManager(ValidationManager validationManager) {
this.validationManager = validationManager;
}
public E create(E value) {
Assert.notNull(value);
validate(value);
getHibernateTemplate().save(value);
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) {
Assert.notNull(value);
validate(value);
getHibernateTemplate().saveOrUpdate(value);
return readId(value.getInternalId());
@ -79,6 +105,7 @@ public class DaoHibernate<E extends PersistableEntity> extends HibernateDaoSuppo
public void delete(E value) {
Assert.notNull(value);
validate(value);
getHibernateTemplate().delete(value);
}
@ -104,6 +131,37 @@ public class DaoHibernate<E extends PersistableEntity> extends HibernateDaoSuppo
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,
int maxElements, String orderByAsc) {
Assert.notNull(value);
@ -134,6 +192,7 @@ public class DaoHibernate<E extends PersistableEntity> extends HibernateDaoSuppo
public E update(E value) {
Assert.notNull(value);
validate(value);
getHibernateTemplate().update(value);
return readId(value.getInternalId());
@ -146,6 +205,7 @@ public class DaoHibernate<E extends PersistableEntity> extends HibernateDaoSuppo
*/
protected final void initDao() throws Exception {
Assert.notNull(supportsClass, "supportClass is required");
Assert.notNull(validationManager, "validationManager is required");
Assert.isTrue(PersistableEntity.class.isAssignableFrom(supportsClass),
"supportClass is not an implementation of PersistableEntity");
initHibernateDao();

View File

@ -19,6 +19,7 @@ import net.sf.acegisecurity.domain.validation.IntrospectionManager;
import net.sf.acegisecurity.domain.validation.ValidationRegistryManager;
import org.hibernate.EntityMode;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
@ -120,11 +121,12 @@ public class IntrospectionManagerHibernate implements IntrospectionManager,
Type propertyType = classMetadata.getPropertyType(propertyNames[i]);
// 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(
propertyType.getReturnedClass()) != null) {
Object childObject = classMetadata.getPropertyValue(parentObject, propertyNames[i], EntityMode.POJO);
if (childObject != null) {
if (childObject != null && Hibernate.isInitialized(childObject)) {
allObjects.add(childObject);
}
}

View File

@ -129,7 +129,9 @@ public interface Manager<E extends PersistableEntity> {
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
* retrieved
@ -138,6 +140,17 @@ public interface Manager<E extends PersistableEntity> {
*/
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
* <code>PersistableEntity</code>.

View File

@ -123,7 +123,12 @@ public class ManagerImpl<E extends PersistableEntity> extends ApplicationObjectS
return dao.readId(id);
}
public PaginatedList<E> scroll(E value, int firstElement,
public E readPopulatedId(Serializable id) {
Assert.notNull(id);
return dao.readPopulatedId(id);
}
public PaginatedList<E> scroll(E value, int firstElement,
int maxElements) {
Assert.notNull(value);
Assert.isInstanceOf(this.supportsClass, value, "Can only scroll with values this manager supports");

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
for validation can be identified and in turn also validated.
<p>The {@link ValidationInterceptor} and {@link ValidationAdvisor} should be
used against each of your data access object (DAO) mutator methods, such as
<code>SomeDao.create(Object)</code> and <code>SomeDao.update(Object)</code>.
The interceptor will cause the <code>Object</code> to be presented to the
<code>ValidationManager</code>, thus ensuring the domain object instance is in
a valid state before being persisted.</p>
<p>The {@link ValidationInterceptor} and {@link ValidationAdvisor} are provided,
although their use is not recommended against DAOs given many <code>Validator</code>s
require a DAO and this will cause a loop that results in the DAO not being
advised. Instead your DAO implementations should have their mutator methods
pass the object to the <code>ValidationManager</code> prior to persistence. This
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
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
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
Hibernate, it is recommended your <code>Validator</code>s subclass a common
abstract parent that provides an <code>evict(Object)</code> and
<code>evict(Collection)</code> method. That way your <code>Validator</code>s
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.
may be checking no other user has this username, or comparing the object's old
state to detect modifications that violate business rules. If using an ORM tool
such as Hibernate, it is recommended you use the <code>EvictionUtils</code>
static methods to remove objects from the session.
</p>
</body>
</html>