More eager/lazy fetching optimisations.
This commit is contained in:
parent
c9fc2684ad
commit
32646b5581
|
@ -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>.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>.
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue