Refactored. NB: This code is unsupported and illustrates non-anemic domain objects that do not leverage transparent persistence capabilities (I recommend you use transparent persistence capabilities where possible - I hope to check in different packages with new patterns in due course).

This commit is contained in:
Ben Alex 2006-03-25 00:18:45 +00:00
parent 0581fc0759
commit 12c78791de
11 changed files with 132 additions and 530 deletions

View File

@ -52,5 +52,5 @@ public interface PersistableEntity {
* *
* @return the persistence identity of this instance * @return the persistence identity of this instance
*/ */
abstract Serializable getInternalId(); public abstract Serializable getInternalId();
} }

View File

@ -15,13 +15,13 @@
package org.acegisecurity.domain.dao; package org.acegisecurity.domain.dao;
import org.acegisecurity.domain.PersistableEntity;
import java.io.Serializable; import java.io.Serializable;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import org.acegisecurity.domain.PersistableEntity;
import org.springframework.dao.DataAccessException;
/** /**
* Provides fundamental DAO capabilities for a single concrete {@link * Provides fundamental DAO capabilities for a single concrete {@link
* PersistableEntity}, using JDK 1.5 generics. * PersistableEntity}, using JDK 1.5 generics.
@ -44,9 +44,7 @@ import java.util.List;
* be the sole entry point into the persistance layer of an application. The * be the sole entry point into the persistance layer of an application. The
* persistence layer should only respond to requests from the services layer. * persistence layer should only respond to requests from the services layer.
* The services layer is where all transaction demarcation, security * The services layer is where all transaction demarcation, security
* authorization, casting to and from concrete {@link * authorization, workflow and business logic should take place.
* org.acegisecurity.domain.PersistableEntity}s, workflow and business
* logic should take place.
* </p> * </p>
* *
* <p> * <p>
@ -67,17 +65,15 @@ public interface Dao<E extends PersistableEntity> {
* PersistableEntity#getInternalId()} value being ignored. * PersistableEntity#getInternalId()} value being ignored.
* *
* @param value (without the identity property initialized) * @param value (without the identity property initialized)
*
* @return the value created (with the identity property initialised)
*/ */
public E create(E value); public void create(E value) throws DataAccessException;
/** /**
* Delete an object. * Delete an object.
* *
* @param value the value to delete * @param value the value to delete
*/ */
public void delete(E value); public void delete(E value) throws DataAccessException;
/** /**
* Return all persistent instances, including subclasses. * Return all persistent instances, including subclasses.
@ -85,7 +81,7 @@ public interface Dao<E extends PersistableEntity> {
* @return all persistence instances (an empty <code>List</code> will be * @return all persistence instances (an empty <code>List</code> will be
* returned if no matches are found) * returned if no matches are found)
*/ */
public List<E> findAll(); public List<E> findAll() throws DataAccessException;
/** /**
* Find a <code>List</code> of <code>PersistableEntity</code>s, searched by * Find a <code>List</code> of <code>PersistableEntity</code>s, searched by
@ -96,7 +92,7 @@ public interface Dao<E extends PersistableEntity> {
* @return the values with those identifiers (an empty <code>List</code> * @return the values with those identifiers (an empty <code>List</code>
* will be returned if no matches are found) * will be returned if no matches are found)
*/ */
public List<E> findId(Collection<Serializable> ids); public List<E> findId(Collection<Serializable> ids) throws DataAccessException;
/** /**
* Load a persistent instance by its identifier, although some properties * Load a persistent instance by its identifier, although some properties
@ -108,19 +104,8 @@ public interface Dao<E extends PersistableEntity> {
* *
* @return the request item, or <code>null</code> if not found * @return the request item, or <code>null</code> if not found
*/ */
public E readId(Serializable id); public E readId(Serializable id) throws DataAccessException;
/**
* 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>.
@ -146,36 +131,7 @@ public interface Dao<E extends PersistableEntity> {
* <code>PaginatedList</code> is returned if no results match) * <code>PaginatedList</code> is returned if no results match)
*/ */
public PaginatedList<E> scroll(E value, int firstElement, public PaginatedList<E> scroll(E value, int firstElement,
int maxElements, String orderByAsc); int maxElements, String orderByAsc) throws DataAccessException;
/**
* Find persistent instances with properties matching those of the passed
* <code>PersistableEntity</code>, with a guarantee the returned results
* will have each of the <code>value</code> class' immediate properties
* initialized.
*
* <P>
* Persistent instances are matched on the basis of query by example.
* Properties whose value is <code>null</code>, empty
* <code>String</code>s, and any <code>Collection</code>s are ignored in
* the query by example evaluation.
* </p>
*
* @param value parameters to filter on (the class of this object will
* be added to the filter)
* @param firstElement the first result (start at zero to obtain all
* results)
* @param maxElements the maximum number of results desired for this page
* of the result set
* @param orderByAsc the property name of the
* <code>PersistableEntity</code> that should be used to order the
* results
*
* @return the requested page of the result list (a properly formed
* <code>PaginatedList</code> is returned if no results match)
*/
public PaginatedList<E> scrollPopulated(E value, int firstElement,
int maxElements, String orderByAsc);
/** /**
* Find persistent instances with properties matching those of the passed * Find persistent instances with properties matching those of the passed
@ -197,31 +153,7 @@ public interface Dao<E extends PersistableEntity> {
* <code>PaginatedList</code> is returned if no results match) * <code>PaginatedList</code> is returned if no results match)
*/ */
public PaginatedList<E> scrollWithSubclasses(E value, int firstElement, public PaginatedList<E> scrollWithSubclasses(E value, int firstElement,
int maxElements, String orderByAsc); int maxElements, String orderByAsc) throws DataAccessException;
/**
* Find persistent instances with properties matching those of the passed
* <code>PersistableEntity</code>, ignoring the class of the passed
* <code>PersistableEntity</code> (useful if you pass a superclass, as you
* want to find all subclass instances which match). Guarantees the returned
* results will have each of the DAO's <code>supports</code> class' immediate
* properties initialized.
*
* @param value parameters to filter on (the class of this object will
* NOT be added to the filter)
* @param firstElement the first result (start at zero to obtain all
* results)
* @param maxElements the maximum number of results desired for this page
* of the result set
* @param orderByAsc the property name of the
* <code>PersistableEntity</code> that should be used to order the
* results
*
* @return the requested page of the result list (a properly formed
* <code>PaginatedList</code> is returned if no results match)
*/
public PaginatedList<E> scrollPopulatedWithSubclasses(E value, int firstElement,
int maxElements, String orderByAsc);
/** /**
* Indicates whether the DAO instance provides persistence services for the * Indicates whether the DAO instance provides persistence services for the
@ -240,8 +172,6 @@ public interface Dao<E extends PersistableEntity> {
* *
* @param value to update, with the <code>PersistableEntity</code> having a * @param value to update, with the <code>PersistableEntity</code> having a
* non-<code>null</code> identifier * non-<code>null</code> identifier
*
* @return the updated value
*/ */
public E update(E value); public void update(E value) throws DataAccessException;
} }

View File

@ -16,36 +16,26 @@
package org.acegisecurity.domain.hibernate; package org.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;
import org.acegisecurity.domain.PersistableEntity; import org.acegisecurity.domain.PersistableEntity;
import org.acegisecurity.domain.dao.Dao; import org.acegisecurity.domain.dao.Dao;
import org.acegisecurity.domain.dao.DetachmentContextHolder;
import org.acegisecurity.domain.dao.EvictionCapable;
import org.acegisecurity.domain.dao.InitializationCapable;
import org.acegisecurity.domain.dao.PaginatedList; import org.acegisecurity.domain.dao.PaginatedList;
import org.acegisecurity.domain.util.GenericsUtils; import org.acegisecurity.domain.util.GenericsUtils;
import org.acegisecurity.domain.validation.ValidationManager;
import org.hibernate.Criteria; import org.hibernate.Criteria;
import org.hibernate.EntityMode; import org.hibernate.EntityMode;
import org.hibernate.FetchMode;
import org.hibernate.Hibernate; import org.hibernate.Hibernate;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Expression; import org.hibernate.criterion.Expression;
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.HibernateTemplate;
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;
/** /**
@ -55,180 +45,65 @@ import org.springframework.validation.BindException;
* @author Matthew Porter * @author Matthew Porter
* @version $Id$ * @version $Id$
*/ */
public class DaoHibernate<E extends PersistableEntity> extends HibernateDaoSupport implements Dao<E>, public class DaoHibernate<E extends PersistableEntity> extends HibernateDaoSupport implements Dao<E> {
EvictionCapable, InitializationCapable {
//~ Instance fields ======================================================== //~ Instance fields ========================================================
/** 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;
public DaoHibernate() { public DaoHibernate(SessionFactory sessionFactory) {
Assert.notNull(sessionFactory, "Non-null Hibernate SessionFactory must be expressed as a constructor argument");
super.setSessionFactory(sessionFactory);
this.supportsClass = GenericsUtils.getGeneric(getClass()); this.supportsClass = GenericsUtils.getGeneric(getClass());
if (this.supportsClass == null) { Assert.notNull(this.supportsClass, "Could not determine the generics type");
if (logger.isWarnEnabled()) {
logger.warn("Could not determine the generics type - you will need to set manually");
}
}
} }
//~ Methods ================================================================ //~ Methods ================================================================
/** public void create(E value) {
* Obtains a <code>HibernateTemplate</code> that uses the appropriate <code>Session</code>
* based on the value of {@link DetachmentContextHolder}.
*
* <p>Specifically, if <code>DetachmentContextHolder</code> requires detached instances,
* the method will build a new <code>Session</code> (ignore the current thread-bound
* <code>Session</code>) and use that new <code>Session</code> in the <code>HibernateTemplate</code>.
* If <code>DetachmentContextHolder</code> is at its fault <code>false</code> value, the
* returned <code>HibernateTemplate</code> will simply use the <code>Session</code> obtained
* from the superclass, which is generally the same <code>Session</code> as used for the
* transaction.
*
* @return the template, containing the correct <code>Session</code> based on the
* <code>DetachmentContactHolder</code> request
*/
protected HibernateTemplate doGetHibernateTemplate() {
if (DetachmentContextHolder.isForceReturnOfDetachedInstances()) {
HibernateTemplate hibernateTemplate = new HibernateTemplate(getSessionFactory());
hibernateTemplate.setAlwaysUseNewSession(true);
return hibernateTemplate;
} else {
return super.getHibernateTemplate();
}
}
public void setSupportsClass(Class supportClass) {
this.supportsClass = supportClass;
}
public Class getSupportsClass() {
return supportsClass;
}
public ValidationManager getValidationManager() {
return validationManager;
}
public void setValidationManager(ValidationManager validationManager) {
this.validationManager = validationManager;
}
public E create(E value) {
Assert.notNull(value); Assert.notNull(value);
validate(value); super.getHibernateTemplate().save(value);
doGetHibernateTemplate().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 void delete(E value) { public void delete(E value) {
Assert.notNull(value); Assert.notNull(value);
validate(value); super.getHibernateTemplate().delete(value);
doGetHibernateTemplate().delete(value);
} }
public void evict(PersistableEntity entity) { @SuppressWarnings("unchecked")
Assert.notNull(entity); public List<E> findAll() {
doGetHibernateTemplate().evict(entity); return super.getHibernateTemplate().loadAll(supportsClass);
} }
public List<E> findAll() { @SuppressWarnings("unchecked")
return doGetHibernateTemplate().loadAll(supportsClass); public List<E> findId(Collection<Serializable> ids) {
}
public List<E> findId(Collection<Serializable> ids) {
Assert.notNull(ids, "Collection of IDs cannot be null"); Assert.notNull(ids, "Collection of IDs cannot be null");
Assert.notEmpty(ids, "There must be some values in the Collection list"); Assert.notEmpty(ids, "There must be some values in the Collection list");
return (List) doGetHibernateTemplate().execute(getFindByIdCallback(ids)); return (List) super.getHibernateTemplate().execute(getFindByIdCallback(ids));
} }
private E readId(final Serializable id, final boolean populate) { @SuppressWarnings("unchecked")
public E readId(Serializable id) {
Assert.notNull(id); Assert.notNull(id);
return (E) doGetHibernateTemplate().execute(new HibernateCallback() { return (E) getHibernateTemplate().load(supportsClass, id);
public Object doInHibernate(Session session) throws HibernateException {
E obj = (E) session.get(supportsClass, id);
if (populate) {
initializeAllZeroArgumentGetters(obj);
}
return obj;
}
}, true);
} }
public E readId(Serializable id) { @SuppressWarnings("unchecked")
Assert.notNull(id); public PaginatedList<E> scroll(E value, int firstElement,
return readId(id, false);
}
public E readPopulatedId(Serializable id) {
Assert.notNull(id);
return readId(id, true);
}
/**
* 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, if 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) { int maxElements, String orderByAsc) {
validateScrollMethod(value, firstElement, maxElements, orderByAsc); validateScrollMethod(value, firstElement, maxElements, orderByAsc);
return (PaginatedList) doGetHibernateTemplate().execute(getFindByValueCallback( return (PaginatedList) super.getHibernateTemplate().execute(getFindByValueCallback(
value.getClass(), false, value, firstElement, maxElements, Order.asc(orderByAsc))); value.getClass(), value, firstElement, maxElements, Order.asc(orderByAsc)));
} }
public PaginatedList<E> scrollWithSubclasses(E value, int firstElement, @SuppressWarnings("unchecked")
public PaginatedList<E> scrollWithSubclasses(E value, int firstElement,
int maxElements, String orderByAsc) { int maxElements, String orderByAsc) {
validateScrollMethod(value, firstElement, maxElements, orderByAsc); validateScrollMethod(value, firstElement, maxElements, orderByAsc);
return (PaginatedList) doGetHibernateTemplate().execute(getFindByValueCallback( return (PaginatedList) super.getHibernateTemplate().execute(getFindByValueCallback(
this.supportsClass, false, value, firstElement, maxElements, Order.asc(orderByAsc))); this.supportsClass, value, firstElement, maxElements, Order.asc(orderByAsc)));
} }
public PaginatedList<E> scrollPopulated(E value, int firstElement,
int maxElements, String orderByAsc) {
validateScrollMethod(value, firstElement, maxElements, orderByAsc);
return (PaginatedList) doGetHibernateTemplate().execute(getFindByValueCallback(
value.getClass(), true, value, firstElement, maxElements, Order.asc(orderByAsc)));
}
public PaginatedList<E> scrollPopulatedWithSubclasses(E value, int firstElement,
int maxElements, String orderByAsc) {
validateScrollMethod(value, firstElement, maxElements, orderByAsc);
return (PaginatedList) doGetHibernateTemplate().execute(getFindByValueCallback(
this.supportsClass, true, value, firstElement, maxElements, Order.asc(orderByAsc)));
}
private void validateScrollMethod(E value, int firstElement, int MaxElements, String orderByAsc) { private void validateScrollMethod(E value, int firstElement, int MaxElements, String orderByAsc) {
Assert.notNull(value); Assert.notNull(value);
@ -239,46 +114,14 @@ public class DaoHibernate<E extends PersistableEntity> extends HibernateDaoSuppo
public boolean supports(Class clazz) { public boolean supports(Class clazz) {
Assert.notNull(clazz); Assert.notNull(clazz);
return this.supportsClass.equals(clazz); return this.supportsClass.equals(clazz);
} }
public E update(E value) { public void update(E value) {
Assert.notNull(value); Assert.notNull(value);
validate(value); super.getHibernateTemplate().update(value);
doGetHibernateTemplate().update(value);
return readId(value.getInternalId());
} }
/**
* Custom initialization behavior. Called by superclass.
*
* @throws Exception if initialization fails
*/
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();
}
/**
* Allows subclasses to provide custom initialization behaviour. Called
* during {@link #initDao()}.
*
* @throws Exception
*/
protected void initHibernateDao() throws Exception {}
public void initialize(Object entity) {
Hibernate.initialize(entity);
}
public boolean isInitialized(Object entity) {
return Hibernate.isInitialized(entity);
}
/** /**
* Provides a <code>HibernateCallback</code> that will load a list of * Provides a <code>HibernateCallback</code> that will load a list of
* objects by a <code>Collection</code> of identities. * objects by a <code>Collection</code> of identities.
@ -313,8 +156,6 @@ public class DaoHibernate<E extends PersistableEntity> extends HibernateDaoSuppo
* match, otherwise find by exact match. * match, otherwise find by exact match.
* *
* @param whichClass the class (and subclasses) which results will be limited to including * @param whichClass the class (and subclasses) which results will be limited to including
* @param initializeAllProperties indicates whether lazy initialized properties
* should be initialized in the returned results
* @param bean bean with the values of the parameters * @param bean bean with the values of the parameters
* @param firstElement the first result, numbered from 0 * @param firstElement the first result, numbered from 0
* @param count the maximum number of results * @param count the maximum number of results
@ -322,15 +163,16 @@ public class DaoHibernate<E extends PersistableEntity> extends HibernateDaoSuppo
* *
* @return a PaginatedList containing the requested objects * @return a PaginatedList containing the requested objects
*/ */
private HibernateCallback getFindByValueCallback(final Class whichClass, final boolean initializeAllProperties, final Object bean, private HibernateCallback getFindByValueCallback(final Class whichClass, final Object bean, final int firstElement, final int count, final Order order) {
final int firstElement, final int count, final Order order) {
return new HibernateCallback() { return new HibernateCallback() {
public Object doInHibernate(Session session) @SuppressWarnings("unchecked")
public Object doInHibernate(Session session)
throws HibernateException { throws HibernateException {
Criteria criteria = session.createCriteria(whichClass); int paramCount = 0;
criteria.addOrder(order); StringBuffer queryString = new StringBuffer("from ").append(bean.getClass().getName()).append(" as queryTarget");
ClassMetadata classMetadata = getSessionFactory() ClassMetadata classMetadata = getSessionFactory()
.getClassMetadata(bean .getClassMetadata(bean
.getClass()); .getClass());
@ -344,12 +186,6 @@ public class DaoHibernate<E extends PersistableEntity> extends HibernateDaoSuppo
/* for each persistent property of the bean */ /* for each persistent property of the bean */
for (int i = 0; i < propertyNames.length; i++) { for (int i = 0; i < propertyNames.length; i++) {
String name = propertyNames[i]; String name = propertyNames[i];
// Indicate preferred fetching here
if (initializeAllProperties) {
criteria.setFetchMode(name, FetchMode.JOIN);
}
// TODO: Check if EntityMode.POJO appropriate // TODO: Check if EntityMode.POJO appropriate
Object value = classMetadata.getPropertyValue(bean, name, EntityMode.POJO); Object value = classMetadata.getPropertyValue(bean, name, EntityMode.POJO);
@ -371,35 +207,47 @@ public class DaoHibernate<E extends PersistableEntity> extends HibernateDaoSuppo
continue; continue;
} }
Type type = classMetadata.getPropertyType(name);
if (name.equals("version")) { if (name.equals("version")) {
continue; continue;
} }
if (name.equals("lastUpdatedUsername") || name.equals("lastUpdatedDate")) {
continue;
}
if (type.equals(Hibernate.STRING)) { Type type = classMetadata.getPropertyType(name);
if (type.equals(Hibernate.STRING)) {
// if the property is mapped as String, find partial match // if the property is mapped as String, find partial match
criteria.add(Expression.ilike(name, if (paramCount == 0) {
value.toString(), MatchMode.ANYWHERE)); queryString.append(" where ");
} else {
queryString.append(" and ");
}
paramCount++;
queryString.append("lower(queryTarget.").append(name).append(") like '%" + value.toString().toLowerCase() + "%'");
} else { } else {
// find exact match // find exact match
criteria.add(Expression.eq(name, value)); if (paramCount == 0) {
queryString.append(" where ");
} else {
queryString.append(" and ");
}
paramCount++;
queryString.append("queryTarget.").append(name).append(" = " + value);
} }
} }
/* if (logger.isDebugEnabled()) {
* TODO Use Criteria.count() when available in next Hibernate logger.debug(queryString.toString());
* versions }
*/
int size = criteria.list().size(); // Determine number of rows
org.hibernate.Query countQuery = session.createQuery("select count(*) " + queryString.toString());
List<E> list = criteria.setFirstResult(firstElement).setMaxResults(count).list(); int size = ((Integer) countQuery.iterate().next()).intValue();
return new PaginatedList<E>(list, firstElement, count, size); // Obtain requested page of query
org.hibernate.Query query = session.createQuery(queryString.toString());
query.setMaxResults(count);
query.setFirstResult(firstElement);
return new PaginatedList(query.list(), firstElement, count, size);
} }
}; };
} }

View File

@ -22,6 +22,7 @@ import java.sql.SQLException;
import java.sql.Types; import java.sql.Types;
import org.acegisecurity.domain.util.GenericsUtils;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType; import org.hibernate.usertype.UserType;
@ -32,9 +33,11 @@ import org.hibernate.usertype.UserType;
* @version $Id$ * @version $Id$
*/ */
public class EnumUserType<E extends Enum<E>> implements UserType { public class EnumUserType<E extends Enum<E>> implements UserType {
private Class<E> clazz = null; private Class<E> clazz = null;
protected EnumUserType(Class<E> c) {
this.clazz = c; @SuppressWarnings("unchecked")
protected EnumUserType() {
this.clazz = GenericsUtils.getGeneric(getClass());
} }
private static final int[] SQL_TYPES = {Types.VARCHAR}; private static final int[] SQL_TYPES = {Types.VARCHAR};

View File

@ -64,13 +64,4 @@ public abstract class AbstractPersistableEntity extends BusinessObject
public int getVersion() { public int getVersion() {
return version; return version;
} }
/**
* Sets the version numbers.
*
* @param version the new version number to use
*/
public void setVersion(int version) {
this.version = version;
}
} }

View File

@ -51,7 +51,11 @@ public abstract class BusinessObject implements Serializable, Cloneable {
* Swallow cloning. * Swallow cloning.
* *
* <p> * <p>
* This method delegates to BeanUtils.cloneBean(). * This method delegates to BeanUtils.cloneBean(). Please note that
* this class uses serialization to achieve a clone, so this may
* represent a performance issue in certain applications. In
* such circumstances you should override this method and provide
* alternative cloning logic.
* </p> * </p>
* *
* @return a clone of the current instance * @return a clone of the current instance

View File

@ -17,6 +17,7 @@ package org.acegisecurity.domain.impl;
import java.io.Serializable; import java.io.Serializable;
/** /**
* A persistable entity that uses a <code>Integer</code> based identity. * A persistable entity that uses a <code>Integer</code> based identity.
* *
@ -26,49 +27,22 @@ import java.io.Serializable;
public abstract class PersistableEntityInteger extends AbstractPersistableEntity { public abstract class PersistableEntityInteger extends AbstractPersistableEntity {
//~ Instance fields ======================================================== //~ Instance fields ========================================================
protected Integer id; private Integer id;
//~ Methods ================================================================ //~ Methods ================================================================
/**
* DO NOT USE DIRECTLY.
*
* <p>
* Typically only used by the persistence layer, but provided with public
* visibility to not limit flexibility.
* </p>
*
* @param id the new instance identity
*/
public void setId(Integer id) {
this.id = id;
}
/** /**
* Obtains the persistence identity of this instance. * Obtains the persistence identity of this instance.
*
* <p>Marked as abstract to remind users to implement. They'll need to implement
* so their annotations reflect the correct sequence name.
*/ */
public abstract Integer getId(); public Integer getId() {
return this.id;
}
/** /**
* DO NOT USE DIRECTLY. * Required solely because Hibernate
*
* <p>
* Use {@link #getId()} instead, as it provides the correct return type.
* This method is only provided for use by the persistence layer and to
* satisfy the {@link org.acegisecurity.domain.PersistableEntity}
* interface contract.
* </p>
*
* <p>
* Internally delegates to {@link #getId()}.
* </p>
*
* @return the instance's identity
*/ */
public Serializable getInternalId() { public Serializable getInternalId() {
return this.getId(); return this.id;
} }
} }

View File

@ -17,6 +17,7 @@ package org.acegisecurity.domain.impl;
import java.io.Serializable; import java.io.Serializable;
/** /**
* A persistable entity that uses a <code>Long</code> based identity. * A persistable entity that uses a <code>Long</code> based identity.
* *
@ -26,49 +27,23 @@ import java.io.Serializable;
public abstract class PersistableEntityLong extends AbstractPersistableEntity { public abstract class PersistableEntityLong extends AbstractPersistableEntity {
//~ Instance fields ======================================================== //~ Instance fields ========================================================
protected Long id; private Long id;
//~ Methods ================================================================ //~ Methods ================================================================
/**
* DO NOT USE DIRECTLY.
*
* <p>
* Typically only used by the persistence layer, but provided with public
* visibility to not limit flexibility.
* </p>
*
* @param id the new instance identity
*/
public void setId(Long id) {
this.id = id;
}
/** /**
* Obtains the persistence identity of this instance. * Obtains the persistence identity of this instance.
*/ */
public Long getId() { public Long getId() {
return this.id; return this.id;
} }
/** /**
* DO NOT USE DIRECTLY. * Required solely because Hibernate
*
* <p>
* Use {@link #getId()} instead, as it provides the correct return type.
* This method is only provided for use by the persistence layer and to
* satisfy the {@link org.acegisecurity.domain.PersistableEntity}
* interface contract.
* </p>
*
* <p>
* Internally delegates to {@link #getId()}.
* </p>
*
* @return the instance's identity
*/ */
public Serializable getInternalId() { public Serializable getInternalId() {
return this.getId(); return this.id;
} }
} }

View File

@ -17,6 +17,7 @@ package org.acegisecurity.domain.service;
import org.acegisecurity.domain.PersistableEntity; import org.acegisecurity.domain.PersistableEntity;
import org.acegisecurity.domain.dao.PaginatedList; import org.acegisecurity.domain.dao.PaginatedList;
import org.springframework.dao.DataAccessException;
import java.io.Serializable; import java.io.Serializable;
@ -28,18 +29,16 @@ import java.util.List;
* PersistableEntity}, using JDK 1.5 generics. * PersistableEntity}, using JDK 1.5 generics.
* *
* <P> * <P>
* This interface provides a remoting protocol compliant approach to accessing * A design decision was to rely on by-reference calling semantics typical of
* services layer logic for a given application. A generics-based services * recommended same-JVM (colocated) deployment environments. If you are using
* layer interface decreases development time because the basic CRUD and finder * remoting you may need to provide a remoting facade that returns the updated
* operations can be specified in a typesafe fashion that reuses superclass * object to the client.
* code.
* </p> * </p>
* *
* <p> * <p>
* It is not envisioned that this interface will provide <b>all</b> services layer * It is not envisioned that this interface will provide <b>all</b> services layer
* functions. The significant value of a services layer is the value-add beyond * functions. The significant value of a services layer is the value-add beyond
* simply fronting the DAO or applying validation/binding logic that is better * simply fronting the DAO. The type of value-adds
* situated in the domain object or its validator. The type of value-adds
* expected to be provided by a services layer include incrementing business * expected to be provided by a services layer include incrementing business
* identifiers (eg an invoice number); generating messages for logging/audit * identifiers (eg an invoice number); generating messages for logging/audit
* purposes (thus such messages are at a business transaction level of granularity, * purposes (thus such messages are at a business transaction level of granularity,
@ -47,17 +46,9 @@ import java.util.List;
* the message becomes unclear); updating related domain objects via * the message becomes unclear); updating related domain objects via
* their respective services layer beans (eg an invoice services layer bean * their respective services layer beans (eg an invoice services layer bean
* would call the general journal services layer bean to create the accrual * would call the general journal services layer bean to create the accrual
* accounting entries); making changes to a domain object that requires * accounting entries); producing messages (eg notify another system the invoice
* logic that is unsuitable to put into a validator because it extends * was created or email the customer via SMTP); provide a layer to locate transaction
* beyond a single domain object instance or requires access to other persistent * and security configuration etc.
* entities (eg computing taxation appliable to an invoice based on a break-down
* of each item on the order, its delivery destination, and the customer);
* producing messages (eg notify another system the invoice was created or
* email the customer via SMTP); provide a layer to locate transaction and
* security configuration; expose a reasonably protocol-independent interface
* to the application that can be used by a variety of web services and
* client types; ensure any returned objects are eagerly loaded to a well-defined
* interface contract etc.
* </p> * </p>
* *
* <P> * <P>
@ -85,7 +76,7 @@ public interface ImmutableManager<E extends PersistableEntity> {
* @return all persistence instances (an empty <code>List</code> will be * @return all persistence instances (an empty <code>List</code> will be
* returned if no matches are found) * returned if no matches are found)
*/ */
public List<E> findAll(); public List<E> findAll() throws DataAccessException;
/** /**
* Find a <code>List</code> of <code>PersistableEntity</code>s, searched by * Find a <code>List</code> of <code>PersistableEntity</code>s, searched by
@ -96,7 +87,7 @@ public interface ImmutableManager<E extends PersistableEntity> {
* @return the values with those identifiers (an empty <code>List</code> * @return the values with those identifiers (an empty <code>List</code>
* will be returned if no matches are found) * will be returned if no matches are found)
*/ */
public List<E> findId(Collection<Serializable> ids); public List<E> findId(Collection<Serializable> ids) throws DataAccessException;
/** /**
* Load a persistent instance by its identifier, although some properties * Load a persistent instance by its identifier, although some properties
@ -108,19 +99,8 @@ public interface ImmutableManager<E extends PersistableEntity> {
* *
* @return the request item, or <code>null</code> if not found * @return the request item, or <code>null</code> if not found
*/ */
public E readId(Serializable id); public E readId(Serializable id) throws DataAccessException;
/**
* 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>.
@ -143,33 +123,7 @@ public interface ImmutableManager<E extends PersistableEntity> {
* <code>PaginatedList</code> is returned if no results match) * <code>PaginatedList</code> is returned if no results match)
*/ */
public PaginatedList<E> scroll(E value, int firstElement, public PaginatedList<E> scroll(E value, int firstElement,
int maxElements); int maxElements) throws DataAccessException;
/**
* Find persistent instances with properties matching those of the passed
* <code>PersistableEntity</code>, with a guarantee the returned results
* will have each of the <code>value</code> class' immediate properties
* initialized.
*
* <P>
* Persistent instances are matched on the basis of query by example.
* Properties whose value is <code>null</code>, empty
* <code>String</code>s, and any <code>Collection</code>s are ignored in
* the query by example evaluation.
* </p>
*
* @param value parameters to filter on (the class of this object will
* be added to the filter)
* @param firstElement the first result (start at zero to obtain all
* results)
* @param maxElements the maximum number of results desired for this page
* of the result set
*
* @return the requested page of the result list (a properly formed
* <code>PaginatedList</code> is returned if no results match)
*/
public PaginatedList<E> scrollPopulated(E value, int firstElement,
int maxElements);
/** /**
* Find persistent instances with properties matching those of the passed * Find persistent instances with properties matching those of the passed
@ -188,29 +142,8 @@ public interface ImmutableManager<E extends PersistableEntity> {
* <code>PaginatedList</code> is returned if no results match) * <code>PaginatedList</code> is returned if no results match)
*/ */
public PaginatedList<E> scrollWithSubclasses(E value, int firstElement, public PaginatedList<E> scrollWithSubclasses(E value, int firstElement,
int maxElements); int maxElements) throws DataAccessException;
/**
* Find persistent instances with properties matching those of the passed
* <code>PersistableEntity</code>, ignoring the class of the passed
* <code>PersistableEntity</code> (useful if you pass a superclass, as you
* want to find all subclass instances which match). Guarantees the returned
* results will have each of the DAO's <code>supports</code> class' immediate
* properties initialized.
*
* @param value parameters to filter on (the class of this object will
* NOT be added to the filter)
* @param firstElement the first result (start at zero to obtain all
* results)
* @param maxElements the maximum number of results desired for this page
* of the result set
*
* @return the requested page of the result list (a properly formed
* <code>PaginatedList</code> is returned if no results match)
*/
public PaginatedList<E> scrollPopulatedWithSubclasses(E value, int firstElement,
int maxElements);
/** /**
* Indicates whether the DAO instance provides persistence services for the * Indicates whether the DAO instance provides persistence services for the
* specified class. * specified class.

View File

@ -23,8 +23,6 @@ import org.acegisecurity.domain.PersistableEntity;
import org.acegisecurity.domain.dao.Dao; import org.acegisecurity.domain.dao.Dao;
import org.acegisecurity.domain.dao.PaginatedList; import org.acegisecurity.domain.dao.PaginatedList;
import org.acegisecurity.domain.util.GenericsUtils; import org.acegisecurity.domain.util.GenericsUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.support.ApplicationObjectSupport; import org.springframework.context.support.ApplicationObjectSupport;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -34,39 +32,25 @@ import org.springframework.util.Assert;
* @author Ben Alex * @author Ben Alex
* @version $Id$ * @version $Id$
*/ */
public class ImmutableManagerImpl<E extends PersistableEntity> extends ApplicationObjectSupport implements ImmutableManager<E>, InitializingBean { public class ImmutableManagerImpl<E extends PersistableEntity> extends ApplicationObjectSupport implements ImmutableManager<E> {
//~ Instance fields ======================================================== //~ Instance fields ========================================================
/** The class that this instance provides services for */ /** The class that this instance provides services for */
private Class supportsClass; private Class supportsClass;
private String beanName;
protected Dao<E> dao; protected Dao<E> dao;
//~ Methods ================================================================ //~ Methods ================================================================
public ImmutableManagerImpl() { public ImmutableManagerImpl(Dao<E> dao) {
// work out what domain object we support
this.supportsClass = GenericsUtils.getGeneric(getClass()); this.supportsClass = GenericsUtils.getGeneric(getClass());
if (supportsClass == null) { Assert.notNull(this.supportsClass, "Could not determine the generics type");
if (logger.isWarnEnabled()) { Assert.isTrue(PersistableEntity.class.isAssignableFrom(supportsClass), "supportClass is not an implementation of PersistableEntity");
logger.warn("Could not determine the generics type - you will need to set manually");
} // store the DAO and check it also supports our domain object type
} Assert.notNull(dao, "Non-null DAO (that supports the same domain object class as this services layer) is required as a constructor argument");
} Assert.isTrue(dao.supports(supportsClass), "Dao '" + dao + "' does not support '" + supportsClass + "'");
public void setSupportsClass(Class supportClass) {
this.supportsClass = supportClass;
}
public Class getSupportsClass() {
return supportsClass;
}
public Dao<E> getDao() {
return dao;
}
public void setDao(Dao<E> dao) {
this.dao = dao; this.dao = dao;
} }
@ -76,22 +60,6 @@ public class ImmutableManagerImpl<E extends PersistableEntity> extends Applicati
protected String getDefaultSortOrder() { protected String getDefaultSortOrder() {
return "id"; return "id";
} }
/**
* Provides hook for custom subclasses to provide initialization behaviour
*
* @throws Exception
*/
protected void doInitManager() throws Exception {}
public final void afterPropertiesSet() throws Exception {
Assert.notNull(supportsClass, "supportClass is required");
Assert.isTrue(PersistableEntity.class.isAssignableFrom(supportsClass),
"supportClass is not an implementation of PersistableEntity");
Assert.notNull(dao, "Dao is null");
Assert.isTrue(dao.supports(supportsClass), "Dao '" + dao + "' does not support '" + supportsClass + "'");
doInitManager();
}
public List<E> findAll() { public List<E> findAll() {
return dao.findAll(); return dao.findAll();
@ -108,11 +76,6 @@ public class ImmutableManagerImpl<E extends PersistableEntity> extends Applicati
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);
@ -121,13 +84,6 @@ public class ImmutableManagerImpl<E extends PersistableEntity> extends Applicati
return dao.scroll(value, firstElement, maxElements, getDefaultSortOrder()); return dao.scroll(value, firstElement, maxElements, getDefaultSortOrder());
} }
public PaginatedList<E> scrollPopulated(E value, int firstElement, int maxElements) {
Assert.notNull(value);
Assert.isInstanceOf(this.supportsClass, value, "Can only scroll with values this manager supports");
return dao.scrollPopulated(value, firstElement, maxElements, getDefaultSortOrder());
}
public PaginatedList<E> scrollWithSubclasses(E value, int firstElement, public PaginatedList<E> scrollWithSubclasses(E value, int firstElement,
int maxElements) { int maxElements) {
Assert.notNull(value); Assert.notNull(value);
@ -136,20 +92,8 @@ public class ImmutableManagerImpl<E extends PersistableEntity> extends Applicati
return dao.scrollWithSubclasses(value, firstElement, maxElements, getDefaultSortOrder()); return dao.scrollWithSubclasses(value, firstElement, maxElements, getDefaultSortOrder());
} }
public PaginatedList<E> scrollPopulatedWithSubclasses(E value, int firstElement, int maxElements) {
Assert.notNull(value);
Assert.isInstanceOf(this.supportsClass, value, "Can only scroll with values this manager supports");
return dao.scrollPopulatedWithSubclasses(value, firstElement, maxElements, getDefaultSortOrder());
}
public boolean supports(Class clazz) { public boolean supports(Class clazz) {
Assert.notNull(clazz); Assert.notNull(clazz);
return this.supportsClass.equals(clazz); return this.supportsClass.equals(clazz);
} }
public void setBeanName(String beanName) {
this.beanName = beanName;
}
} }

View File

@ -17,7 +17,7 @@ public class GenericsUtils {
* @param clazz The class to introspect * @param clazz The class to introspect
* @return the first generic declaration, or <code>null</code> if cannot be determined * @return the first generic declaration, or <code>null</code> if cannot be determined
*/ */
public static Class getGeneric(Class clazz) { public static Class getGeneric(Class clazz) {
Type genType = clazz.getGenericSuperclass(); Type genType = clazz.getGenericSuperclass();
if (genType instanceof ParameterizedType) { if (genType instanceof ParameterizedType) {