/* * Copyright 2008-2011 Jose Luis Martin Garcia * * 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.jdal.hibernate; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.Criteria; import org.hibernate.EntityMode; import org.hibernate.Hibernate; import org.hibernate.LockOptions; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.collection.AbstractPersistentCollection; import org.hibernate.criterion.Example; import org.hibernate.engine.PersistenceContext; import org.hibernate.impl.CriteriaImpl; import org.hibernate.impl.CriteriaImpl.Subcriteria; import org.hibernate.impl.SessionImpl; import org.hibernate.metadata.ClassMetadata; import org.hibernate.persister.collection.CollectionPersister; import org.jdal.beans.PropertyUtils; import org.jdal.util.BeanUtils; import org.springframework.util.StringUtils; /** * Hibernate Utility library * * @author Jose Luis Martin - (jlm@joseluismartin.info) */ @SuppressWarnings("rawtypes") public abstract class HibernateUtils { public static final int DEFAULT_DEPTH = 2; private static final Log log = LogFactory.getLog(HibernateUtils.class); private static final String EXISTS_QUERY = "SELECT 1 from %s x WHERE %s = ?"; /** * Initialize a Object for use whith closed session. * Will recurse on properties at maximum of n depth. * * @param sessionFactory the Hibernate SessionFactory to use * @param obj Object to initialize * @param depth max depth in recursion */ public static void initialize(SessionFactory sessionFactory, Object obj, int depth) { initialize(sessionFactory, obj, new ArrayList<Object>(), depth); } /** * Initialize a Object for use with closed sessions, * Use with care, will recurse on all properties. * * @param sessionFactory the hibernate SessionFactory * @param obj persistent object to initialize */ public static void initialize(SessionFactory sessionFactory, Object obj) { initialize(sessionFactory, obj, new ArrayList<Object>(), DEFAULT_DEPTH); } /** * Initialize Object for use with closed Session. * * @param sessionFactory max depth in recursion * @param obj Object to initialize * @param initializedObjects list with already initialized Objects * @param depth max depth in recursion */ private static void initialize(SessionFactory sessionFactory, Object obj, List<Object> initializedObjects, int depth) { // return on nulls, depth = 0 or already initialized objects if (obj == null || depth == 0) { return; } if (!Hibernate.isInitialized(obj)) { // if collection, initialize objects in collection too. Hibernate don't do it. if (obj instanceof Collection) { initializeCollection(sessionFactory, obj, initializedObjects, depth); return; } sessionFactory.getCurrentSession().buildLockRequest(LockOptions.NONE).lock(obj); Hibernate.initialize(obj); } // now we can call equals safely. If object are already initializated, return if (initializedObjects.contains(obj)) return; initializedObjects.add(obj); // initialize all persistent associaciations. ClassMetadata classMetadata = getClassMetadata(sessionFactory, obj); if (classMetadata == null) { return; // Not persistent object } Object[] pvs = classMetadata.getPropertyValues(obj, EntityMode.POJO); for (Object pv : pvs) { initialize(sessionFactory, pv, initializedObjects, depth - 1); } } /** * Initalize Collection and recurse on elements * * @param sessionFactory the hibernate SessionFactory * @param obj the obj to initilize * @param initializedObjects list with already initialized objects * @param depth max depth in recursion */ private static void initializeCollection(SessionFactory sessionFactory, Object obj, List<Object> initializedObjects, int depth) { Collection<?> collection = (Collection<?>) obj; initializeCollection(collection, sessionFactory.getCurrentSession()); // Initialize elements for (Object o : collection) { initialize(sessionFactory, o, initializedObjects, depth - 1); } } /** * Initialize Collection (detached or not) * @param collection collection to initialize * @param session Session to use for initialization */ public static void initializeCollection(Collection collection, Session session) { if (collection instanceof AbstractPersistentCollection) { AbstractPersistentCollection ps = (AbstractPersistentCollection) collection; log.debug("Initalizing PersistentCollection of role: " + ps.getRole()); if (!ps.wasInitialized()) { SessionImpl source = (SessionImpl) session; PersistenceContext context = source.getPersistenceContext(); CollectionPersister cp = source.getFactory().getCollectionPersister(ps.getRole()); if (context.getCollectionEntry(ps) == null) { // detached context.addUninitializedDetachedCollection(cp, ps); } ps.setCurrentSession(context.getSession()); Hibernate.initialize(collection); } } } /** * Get ClassMetadata for persistent object * * @param sessionFactory the hibernate SessionFactory * @param obj Object to initilize * @return ClassMetadata the class metadata */ private static ClassMetadata getClassMetadata( SessionFactory sessionFactory, Object obj) { return sessionFactory.getClassMetadata(Hibernate.getClass(obj)); } /** * Gets the identifier property name of persistent object * * @param sessionFactory the hibernate SessionFactory * @param obj the persistent object * @return the identifier property name */ public static String getIdentifierPropertyName( SessionFactory sessionFactory, Object obj) { ClassMetadata cm = getClassMetadata(sessionFactory, obj); return cm == null ? null : cm.getIdentifierPropertyName(); } /** * Get all name attributes from a object that are of the given class * @param obj Object to get fields * @param type type of the class to find. * @return name attributes with the class type specified */ public static Set<String> getFieldNamesByType(Object obj, Class <?> type) { Set<String> fieldNames = new HashSet<String> (0); Field [] fields = obj.getClass().getDeclaredFields(); for (Field field : fields) { if (type.equals(field.getType())) { fieldNames.add(field.getName()); } } return fieldNames; } /** * Get a hibernate Example object that excludes zeroes values and excludes * all boolean -primitive and wrapper class- attributes of a given object. * @param instance given object for a build a Example object. * @return a hibernate Example object. */ public static Example excludeBooleanFields (Object instance) { Example result = Example.create(instance).excludeZeroes(); Set<String> fieldNames = getFieldNamesByType(instance, Boolean.class); fieldNames.addAll(getFieldNamesByType(instance, Boolean.TYPE)); for (String fieldName : fieldNames) { result.excludeProperty(fieldName); } return result; } /** * Return a existing alias for propertyPath on Criteria or null if none * @param criteria Hibernate Criteria * @param propertyPath the property path * @return alias or null if none */ public static String findAliasForPropertyPath(Criteria criteria, String propertyPath) { CriteriaImpl c = (CriteriaImpl) criteria; Iterator iter = c.iterateSubcriteria(); while (iter.hasNext()) { Subcriteria subCriteria = (Subcriteria) iter.next(); if (propertyPath.equals(subCriteria.getPath())); return subCriteria.getAlias(); } // not found return null; } /** * Create an alias for a property path * @return the alias */ public static String createAlias(Criteria criteria, String propertyPath) { String[] paths = PropertyUtils.split(propertyPath); String alias = ""; for (String name : paths) { alias += StringUtils.isEmpty(alias) ? name : "." + name; criteria.createAlias(alias, name); } return PropertyUtils.getPropertyName(alias); } /** * Test if a entity already exists. * @param entity entity to test * @param session hibernate session * @return true if exists, false otherwise */ public static boolean exists(Object entity, Session session) { String propertyId = getIdentifierPropertyName(session.getSessionFactory(), entity); if (propertyId == null) return false; Object id = BeanUtils.getProperty(entity, propertyId); if (id == null) return false; return session.createQuery(String.format(EXISTS_QUERY, entity.getClass().getSimpleName(), propertyId)) .setParameter(0, id) .list() .size() > 0; } }