mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-07-12 21:33:30 +00:00
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);
|
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>.
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>.
|
||||||
|
@ -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);
|
||||||
|
@ -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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user