/* * Copyright 2004 Chris Nelson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the License. */ package org.trails.hibernate; import java.io.Serializable; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.apache.hivemind.util.PropertyUtils; import org.hibernate.Criteria; import org.hibernate.EntityMode; import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.Session; import org.hibernate.TransientObjectException; import org.hibernate.criterion.CriteriaSpecification; import org.hibernate.criterion.DetachedCriteria; import org.hibernate.criterion.Expression; import org.hibernate.criterion.MatchMode; import org.hibernate.criterion.Projections; import org.hibernate.criterion.Restrictions; import org.hibernate.metadata.ClassMetadata; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.dao.DataAccessException; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import org.trails.descriptor.CollectionDescriptor; import org.trails.descriptor.DescriptorService; import org.trails.descriptor.IClassDescriptor; import org.trails.descriptor.IPropertyDescriptor; import org.trails.exception.TrailsRuntimeException; import org.trails.persistence.HibernatePersistenceService; import org.trails.persistence.PersistenceException; import org.trails.util.Utils; import org.trails.validation.ValidationException; public class HibernatePersistenceServiceImpl extends HibernateDaoSupport implements HibernatePersistenceService, ApplicationContextAware { ApplicationContext appContext = null; private DescriptorService _descriptorService = null; /** * We need this because cylcic reference between HibernatePersistenceServiceImpl and TrailsDescriptorService */ public DescriptorService getDescriptorService() { if (_descriptorService == null) { _descriptorService = (DescriptorService) appContext.getBean("descriptorService"); } return _descriptorService; } /** * https://trails.dev.java.net/servlets/ReadMsg?listName=users&msgNo=1226 *
* Very often I find myself writing: Object example = new Object(); example.setProperty(uniqueValue); List
* objects = ((TrailsPage)getPage()).getPersistenceService().getInstances(example); (MyObject)objects.get(0);
* when, in fact, I know that the single property I populated my example object with should be unique, and thus only
* one object should be returned
*
* @param type The type to use to check for security restrictions.
* @param detachedCriteria
* @return
*/
@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public This method is a thin wrapper around {@link org.hibernate.Session#load(Class,java.io.Serializable)} for
* convenience. For an explanation of the exact semantics of this method, please do refer to the Hibernate API
* documentation in the first instance.
*
* @param type a persistent class
* @param id the identifier of the persistent instance
* @return the persistent instance
* @see org.springframework.orm.hibernate3.HibernateTemplate#load(Class,java.io.Serializable)
* @see org.hibernate.Session#load(Class,java.io.Serializable)
*/
@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public
*
* @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
* private Serializable getIdentifier(final Object data)
* {
* return (Serializable) getHibernateTemplate().execute(new HibernateCallback()
* {
* public Object doInHibernate(Session session) throws HibernateException, SQLException
* {
* return session.getIdentifier(data);
* }
* });
* }
*
*
*
* but it didn't work.
* "session.getIdentifier(data)" thows TransientObjectException when the Entity is not loaded by the current session,
* which is pretty usual in Trails.
*
*
* @param data
* @param classDescriptor
* @return
*/
public Serializable getIdentifier(final Object data, final IClassDescriptor classDescriptor)
{
return (Serializable) PropertyUtils.read(data, classDescriptor.getIdentifierDescriptor().getName());
}
public boolean isTransient(Object data, IClassDescriptor classDescriptor)
{
try
{
return getIdentifier(data, classDescriptor) == null;
} catch (TransientObjectException e)
{
return true;
}
}
@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public List getInstances(final Object example, final IClassDescriptor classDescriptor)
{
return (List) getHibernateTemplate().execute(new HibernateCallback()
{
public Object doInHibernate(Session session) throws HibernateException, SQLException
{
//create Criteria instance
DetachedCriteria searchCriteria = DetachedCriteria.forClass(Utils.checkForCGLIB(example.getClass()));
searchCriteria = alterCriteria(example.getClass(), searchCriteria);
//loop over the example object's PropertyDescriptors
for (IPropertyDescriptor propertyDescriptor : classDescriptor.getPropertyDescriptors())
{
//only add a Criterion to the Criteria instance if this property is searchable
if (propertyDescriptor.isSearchable())
{
String propertyName = propertyDescriptor.getName();
Class propertyClass = propertyDescriptor.getPropertyType();
Object value = PropertyUtils.read(example, propertyName);
//only add a Criterion to the Criteria instance if the value for this property is non-null
if (value != null)
{
if (String.class.isAssignableFrom(propertyClass) && ((String) value).length() > 0)
{
searchCriteria
.add(Restrictions.like(propertyName, value.toString(), MatchMode.ANYWHERE));
}
/**
* 'one'-end of many-to-one, one-to-one
*
* Just match the identifier
*/
else if (propertyDescriptor.isObjectReference())
{
Serializable identifierValue = getIdentifier(value,
getDescriptorService().getClassDescriptor(propertyDescriptor.getBeanType()));
searchCriteria.createCriteria(propertyName).add(Restrictions.idEq(identifierValue));
} else if (propertyClass.isPrimitive())
{
//primitive types: ignore zeroes in case of numeric types, ignore booleans anyway (TODO come up with something...)
if (!propertyClass.equals(boolean.class) && ((Number) value).longValue() != 0)
{
searchCriteria.add(Restrictions.eq(propertyName, value));
}
} else if (propertyDescriptor.isCollection())
{
//one-to-many or many-to-many
CollectionDescriptor collectionDescriptor =
(CollectionDescriptor) propertyDescriptor;
IClassDescriptor classDescriptor = getDescriptorService().getClassDescriptor(collectionDescriptor.getElementType());
if (classDescriptor != null)
{
String identifierName = classDescriptor.getIdentifierDescriptor().getName();
Collection