View Javadoc

1   /**
2    * Copyright 2005-2006 the original author or authors.
3    *
4    * Licensed under the Gnu General Pubic License, Version 2.0 (the
5    * "License"); you may not use this file except in compliance with
6    * the License. You may obtain a copy of the License at
7    *
8    *      http://www.opensource.org/licenses/gpl-license.php
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13   * See the Gnu General Public License for more details.
14   */
15  package org.figure8.join.core.persistence;
16  
17  import org.figure8.join.core.InfrastructureException;
18  import org.figure8.join.util.LogUtil;
19  
20  import org.apache.commons.logging.Log;
21  import org.springframework.orm.hibernate.HibernateCallback;
22  import net.sf.hibernate.Session;
23  import net.sf.hibernate.Hibernate;
24  import net.sf.hibernate.SessionFactory;
25  import net.sf.hibernate.HibernateException;
26  import net.sf.hibernate.collection.PersistentCollection;
27  import net.sf.hibernate.engine.SessionFactoryImplementor;
28  import net.sf.hibernate.metadata.ClassMetadata;
29  import net.sf.hibernate.persister.Joinable;
30  import net.sf.hibernate.type.Type;
31  import net.sf.hibernate.type.EntityType;
32  import net.sf.hibernate.type.AssociationType;
33  import net.sf.hibernate.type.PersistentCollectionType;
34  
35  import java.util.Map;
36  import java.util.Set;
37  import java.util.List;
38  import java.util.Arrays;
39  import java.util.HashSet;
40  import java.util.HashMap;
41  import java.util.Iterator;
42  import java.util.ArrayList;
43  import java.util.Collection;
44  /**
45   * This is a generic implementation of <code>HibernateObjectDao</code>
46   * that uses <code>java.lang.Object</code> as its persistent class.
47   * @author  <a href="mailto:laurent.broudoux@free.fr">Laurent Broudoux</a>
48   * @version $Revision: 1.2 $
49   */
50  public class GenericHibernateObjectDao extends HibernateObjectDao{
51  
52     // Static -------------------------------------------------------------------
53  
54     /** Get a commons logger */
55     private static final Log log = LogUtil.getLog(GenericHibernateObjectDao.class);
56  
57  
58     // Constructors -------------------------------------------------------------
59     
60     /** Creates a new instance of GenericHibernateObjectDao */
61     public GenericHibernateObjectDao(){
62     }
63  
64  
65     // Override of HibernateObjectDao -------------------------------------------
66  
67     /**
68      * Find all entity objects associated with this Dao. Because this dao
69      * implementation is generic : this means all the persitent objects !
70      * @return A List of EntityObjects
71      */
72     public List findAll(){
73        try{
74           // Find all using PersistenObjectsLoader.
75           return (List)getHibernateTemplate().execute(new HibernateCallback(){
76              public Object doInHibernate(Session session) throws HibernateException{
77                 return PersistentObjectsLoader.loadPersistentObjects(session);
78              }
79           });
80        }
81        catch(Exception e){
82           log.error("Exception in findAll()!", e);
83           throw new InfrastructureException(e);
84        }
85     }
86  
87  
88     // Implementation of HibernateObjectDao -------------------------------------
89     
90     /**
91      * Get the persistent class associated to this Dao.
92      * @return The class of the persistent object
93      */
94     public Class getPersistentClass(){
95        return (java.lang.Object.class);
96     }
97  
98  
99     // Private ------------------------------------------------------------------
100 
101    /** @return The collection implementation corresponding to Hiberante collection type */
102    private static Collection getCollectionImplementation(Object collection){
103       Collection associatedRows = null;
104       if (collection instanceof Set)
105          associatedRows = new HashSet();
106       else if (collection instanceof List)
107          associatedRows = new ArrayList();
108       return associatedRows;
109    }
110 
111    /** @return Find and initialize other side of bidirectionnal association */
112    private static Object findCollectionInAssociatedObject(SessionFactoryImplementor sessionFactoryImplementor,
113       String referencedColumns[], ClassMetadata referencedClassMetaData, Object referencedObject, Class clazz) throws HibernateException{
114       // Get properties and types in referenced object.
115       Type types[] = referencedClassMetaData.getPropertyTypes();
116       String propertyNames[] = referencedClassMetaData.getPropertyNames();
117 
118       // Browse the types of asscoiated object that are association too.
119       for (int i=0; i<types.length; i++){
120          Type type = types[i];
121          if (!type.isAssociationType())
122             continue;
123          // Retrieve type of property from associated object and its columns.
124          AssociationType associationType = (AssociationType)type;
125          Class associatedClass = associationType.getAssociatedClass(sessionFactoryImplementor);
126          Class associationTypeClass = associationType.getReturnedClass();
127          String referencedColumnsOfAssociation[] = associationType.getReferencedColumns(sessionFactoryImplementor);
128          // Check if type of associated object references the same column that current object.
129          boolean sameColumns = Arrays.equals(referencedColumns, referencedColumnsOfAssociation);
130          if (!sameColumns || !associatedClass.isAssignableFrom(clazz))
131             continue;
132          // Initialize the property corresponding to type in associated object.
133          Object associatedObject = referencedClassMetaData.getPropertyValue(referencedObject, propertyNames[i]);
134          if (associatedObject instanceof PersistentCollection)
135             if (Set.class.isAssignableFrom(associationTypeClass))
136                associatedObject = new HashSet();
137             else if (List.class.isAssignableFrom(associationTypeClass))
138                associatedObject = new ArrayList();
139          // Set the property value.
140          referencedClassMetaData.setPropertyValue(referencedObject, propertyNames[i], associatedObject);
141          return associatedObject;
142       }
143       // This should not happen...
144       return null;
145    }
146 
147 
148    // Inner classes ------------------------------------------------------------
149 
150    /** Class representing a composite key */
151    private static final class Key{
152       Object key1 = null;
153       Object key2 = null;
154       /** Creates a new 2 member keys */
155       public Key(Object key1, Object key2){
156          this.key1 = key1;
157          this.key2 = key2;
158       }
159       /** @return Hashcode of this composite key */
160       public int hashCode(){
161          return (29 * key1.hashCode() + key2.hashCode());
162       }
163       /** @return Whether objects is equal to key */
164       public boolean equals(Object obj){
165          Key theOtherObj = (Key)obj;
166          return key1.equals(theOtherObj.key1) && key2.equals(theOtherObj.key2);
167       }
168    }
169 
170    /** Inner class for loading all persistent objects */
171    public static class PersistentObjectsLoader{
172       /**
173        * Load the persistent objects from given <code>Session</code>
174        * @throws HibernateException if session or session factory access fails
175        */
176       public static List loadPersistentObjects(Session session) throws HibernateException{
177          // Prepare all we need to extract persistent objects.
178          List objects = new ArrayList();
179          Map objectsMap = new HashMap();
180          SessionFactory sessionFactory = session.getSessionFactory();
181          SessionFactoryImplementor sessionFactoryImplementor = (SessionFactoryImplementor)sessionFactory;
182 
183          // Get all metadata entries present in session factory.
184          Map metadata = sessionFactory.getAllClassMetadata();
185          Set metadataEntries = metadata.entrySet();
186          for (Iterator entries = metadataEntries.iterator(); entries.hasNext();){
187             Map.Entry entry = (Map.Entry)entries.next();
188             Class clazz = (Class)entry.getKey();
189             try{
190                // Load this class and all objects from it.
191                clazz.newInstance();
192                log.debug("Loading all entities from class: " + clazz.getName());
193                List list = session.find("from " + clazz.getName());
194                objects.addAll(list);
195                // For each object, retrieve its id and store it into map.
196                for (int i=0; i<list.size(); i++){
197                   Object obj = list.get(i);
198                   ClassMetadata metaData = sessionFactory.getClassMetadata(clazz);
199                   Object objClazz = metaData.getMappedClass();
200                   Object objId = metaData.getIdentifier(obj);
201                   objectsMap.put(new Key(objClazz, objId), obj);
202                }
203             }
204             catch (InstantiationException e){
205                // Just warn administrator user...
206                log.warn("InstanciationException while loading all persistent objects: " + e.getMessage());
207             }
208             catch (IllegalAccessException e){
209                // Just warn administrator user...
210                log.warn("IllegalAccessException while loading all persistent objects: " + e.getMessage());
211             }
212          }
213 
214          // Load each object properties, associations and collections.
215          for (int i=0; i<objects.size(); i++){
216             Object obj = objects.get(i);
217             Class clazz = Hibernate.getClass(obj);
218             ClassMetadata metaData = sessionFactory.getClassMetadata(clazz);
219             Type types[] = metaData.getPropertyTypes();
220             String propertyNames[] = metaData.getPropertyNames();
221             // Process this object properties.
222             for (int j=0; j<types.length; j++){
223                Type type = types[j];
224                String propertyName = propertyNames[j];
225                // Check if type or property is a collection...
226                if (type.isPersistentCollectionType()){
227                   PersistentCollectionType collectionType = (PersistentCollectionType)type;
228                   Joinable joinable = collectionType.getJoinable(sessionFactoryImplementor);
229                   // Do nothing if its's a N-2-M.
230                   if (joinable.isManyToMany())
231                      continue;
232                   // Update metadata of property value.
233                   Object col = metaData.getPropertyValue(obj, propertyName);
234                   if (col instanceof PersistentCollection){
235                      Collection collection = getCollectionImplementation(col);
236                      metaData.setPropertyValue(obj, propertyName, collection);
237                   }
238                   continue;
239                }
240                // Do nothing if property is not an association or is a persistent collection.
241                if (!type.isAssociationType() || type.isPersistentCollectionType())
242                   continue;
243 
244                // Because it's an association, it's also an entity.
245                EntityType entityType = (EntityType)type;
246                Class associatedClass = entityType.getAssociatedClass();
247                ClassMetadata associatedClassMetaData = (ClassMetadata)metadata.get(associatedClass);
248                Object associatedObject = metaData.getPropertyValue(obj, propertyName);
249                if (associatedObject == null)
250                   continue;
251                // Load associated ojbect.
252                Class associatedObjectClazz = associatedClassMetaData.getMappedClass();
253                Object associatedObjectId = associatedClassMetaData.getIdentifier(associatedObject);
254                // Check if there's an already loaded object for associated object.
255                Object fullyLoadedAssociatedObject = objectsMap.get(new Key(associatedObjectClazz, associatedObjectId));
256                if (fullyLoadedAssociatedObject != null){
257                   associatedObject = fullyLoadedAssociatedObject;
258                   metaData.setPropertyValue(obj, propertyName, associatedObject);
259                }
260                // Load other side of relation if it's bidirectional.
261                String referencedColumns[] = entityType.getReferencedColumns(sessionFactoryImplementor);
262                Object associatedObjectInAssociatedObject = findCollectionInAssociatedObject(sessionFactoryImplementor, referencedColumns, associatedClassMetaData, associatedObject, clazz);
263                if (associatedObjectInAssociatedObject != null && (associatedObjectInAssociatedObject instanceof Collection))
264                   ((Collection)associatedObjectInAssociatedObject).add(obj);
265             }
266          }
267          return objects;
268       }
269    }
270 }