@ -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,181 +45,66 @@ 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 ( ) {
doG etHibernateTemplate( ) . 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 ) {
Assert . notNull ( id ) ;
return ( E ) doGetHibernateTemplate ( ) . execute ( new HibernateCallback ( ) {
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" )
public E readId ( Serializable id ) {
Assert . notNull ( id ) ;
Assert . notNull ( id ) ;
return readId ( id , false ) ;
return ( E ) getHibernateTemplate ( ) . load ( supportsClass , id ) ;
}
}
public E readPopulatedId ( Serializable id ) {
@SuppressWarnings ( "unchecked" )
Assert . notNull ( id ) ;
public PaginatedList < E > scroll ( E value , int firstElement ,
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 ) doG etHibernateTemplate( ) . 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" )
int maxElements , String orderByAsc ) {
public PaginatedList < E > scrollWithSubclasses ( E value , int firstElement ,
validateScrollMethod ( value , firstElement , maxElements , orderByAsc ) ;
return ( PaginatedList ) doGetHibernateTemplate ( ) . execute ( getFindByValueCallback (
this . supportsClass , false , value , firstElement , maxElements , Order . asc ( orderByAsc ) ) ) ;
}
public PaginatedList < E > scrollPopulated ( E value , int firstElement ,
int maxElements , String orderByAsc ) {
int maxElements , String orderByAsc ) {
validateScrollMethod ( value , firstElement , maxElements , orderByAsc ) ;
validateScrollMethod ( value , firstElement , maxElements , orderByAsc ) ;
return ( PaginatedList ) doG etHibernateTemplate( ) . execute ( getFindByValueCallback (
return ( PaginatedList ) super . getHibernateTemplate ( ) . execute ( getFindByValueCallback (
value . getClass ( ) , true , value , firstElement , maxElements , Order . asc ( orderByAsc ) ) ) ;
this . supportsClass , 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 ) ;
Assert . hasText ( orderByAsc ,
Assert . hasText ( orderByAsc ,
@ -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 ) ;
}
}
} ;
} ;
}
}