View Javadoc

1   /**
2    * Copyright 2005-2008 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.util.LogUtil;
18  import org.figure8.join.core.EntityObject;
19  import org.figure8.join.core.InfrastructureException;
20  
21  import org.apache.commons.logging.Log;
22  import org.springframework.orm.hibernate.HibernateCallback;
23  import org.springframework.orm.hibernate.SessionFactoryUtils;
24  import org.springframework.orm.hibernate.support.HibernateDaoSupport;
25  import net.sf.hibernate.Query;
26  import net.sf.hibernate.Session;
27  import net.sf.hibernate.LockMode;
28  import net.sf.hibernate.Hibernate;
29  import net.sf.hibernate.ReplicationMode;
30  import net.sf.hibernate.HibernateException;
31  
32  import java.util.List;
33  import java.util.Collections;
34  import java.util.HashSet;
35  /**
36   * This is a base class for Dao implementations using Hibernate as their
37   * ORM solution. This base class also extends Spring container Hibernate
38   * support class in order to use Spring's features such as declarative transaction
39   * demarcation ...
40   * @author  <a href="mailto:laurent.broudoux@free.fr">Laurent Broudoux</a>
41   * @version $Revision: 1.3 $
42   */
43  public abstract class HibernateObjectDao extends HibernateDaoSupport
44     implements ObjectDao{
45     
46     // Attributes ---------------------------------------------------------------
47  
48     /** Logger */
49     protected Log log;
50        
51     
52     // Constructors -------------------------------------------------------------
53     
54     /** Default constructor. */
55     public HibernateObjectDao(){
56        log = LogUtil.getLog(this.getPersistentClass());
57     }
58  
59     
60     // Implementation of ObjectDao ----------------------------------------------
61     
62     /**
63      * Save the given entity into underlying datastore.
64      * @param entityobject EntityObject to save
65      */
66     public void save(EntityObject entityobject){
67        try{
68           // Execute saveOrUpdateCopy, so that it does not thrown a net.sf.hibernate.NonUniqueObjectException
69           // if another object associated with the session has the same identifier. This is useful when managers
70           // try loading objects having same properties for checks before doing an update on another instance
71           // coming from another Hibernate session.
72           // TODO: study impact of copy on application performance. Eventually, refactor copy as a separate method.
73           getHibernateTemplate().saveOrUpdateCopy(entityobject);
74        }
75        catch (Exception e){
76           log.error("Exception in saveOrUpdate()!", e);
77           throw new InfrastructureException(e);
78        }
79     }
80     
81     /**
82      * Remove the given entity object from underlying datastore.
83      * @param entityobject EntityObject to remove
84      */
85     public void remove(EntityObject entityobject){
86        try{
87           // Execute delete.
88           getHibernateTemplate().delete(entityobject);
89        }
90        catch (Exception e){
91           log.error("Exception in remove()!", e);
92           throw new InfrastructureException(e);
93        }
94     }
95     
96     /**
97      * Re-read the content of the given entity from underlying datastore.
98      * @param entityobject EntityObject to refresh content
99      */
100    public void refresh(EntityObject entityobject){
101       try{
102          // Execute refresh.
103          getHibernateTemplate().refresh(entityobject);
104       }
105       catch (Exception e){
106          log.error("Exception in refresh()!", e);
107          throw new InfrastructureException(e);
108       }
109    }
110 
111    /**
112     * Force initialization of the given entity from underlying datastore
113     * (this may involves initialization of lazily loaded relations fields)
114     * @param entityobject EntityObject to initialize
115     */
116    public void initialize(EntityObject entityobject){
117       try{
118          // Execute initialization after having reattaching object.
119          getHibernateTemplate().lock(entityobject, LockMode.READ);
120          getHibernateTemplate().initialize(entityobject);
121       }
122       catch (Exception e){
123          log.error("Exception in initialize()!", e);
124          throw new InfrastructureException(e);
125       }
126    }
127 
128    /**
129     * Force initialization of the given entity association from underlying datastore
130     * @param entityobject EntityObject whose association shoud be initialized
131     * @param proxy A proxy object representing entoty association (this may be a collection)
132     */
133    public void initializeAssociation(EntityObject entityobject, Object proxy){
134       try{
135          // Execute proxy initialization after having reattached entity object.
136          getHibernateTemplate().lock(entityobject, LockMode.READ);
137          getHibernateTemplate().initialize(proxy);
138       }
139       catch (Exception e){
140          log.error("Exception in initializeAssociation()!", e);
141          throw new InfrastructureException(e);
142       }
143    }
144 
145    /**
146     * Check if an object (EntityObject or association proxy) is initialized from datastore
147     * @param object The object to check initialization for
148     * @return true if object has been loaded from datastore, false otherwise
149     */
150    public boolean isInitialized(Object object){
151       try{
152          return Hibernate.isInitialized(object);
153       }
154       catch (Exception e){
155          log.error("Exception in isInitialized()!", e);
156          throw new InfrastructureException(e);
157       }
158    }
159 
160    /**
161     * Persist the object state throughout the cluster.
162     * @param obj The object to replicate
163     */
164    public void replicate(final Object obj){
165       try{
166          // Execute replicate.
167          getHibernateTemplate().execute(new HibernateCallback(){
168             public Object doInHibernate(Session session) throws HibernateException{
169                session.replicate(obj, ReplicationMode.OVERWRITE);
170                return null;
171             }
172          });
173       }
174       catch(Exception e){
175          log.error("Exception in replicate()!", e);
176          throw new InfrastructureException(e);
177       }
178    }
179    
180    /**
181     * Find all entity objects associated with this Dao.
182     * @return A List of EntityObjects
183     */
184    public List findAll(){
185       return findAllSorted(null);
186    }
187 
188    /**
189     * Find all entity objects asscoiated with this Dao.
190     * The result list is sorted by <b>sortField</b> criteria.
191     * @param sortField Field for sorting criteria
192     * @return A List of EntityObjects
193     */
194    public List findAllSorted(String sortField){
195       // Create new query adding field sort if provided.
196       String query = "FROM " + getPersistentClass().getName() + " result";
197       if (sortField != null)
198          query = query + " ORDER BY LOWER(result." + sortField + ")";
199       try{
200          // Execute query.
201          List result = getHibernateTemplate().find(query);
202          if (result == null)
203             return Collections.EMPTY_LIST;
204          return result;
205       }
206       catch (Exception e){
207          log.error("Exception in findAllSorted()!", e);
208          throw new InfrastructureException(e);
209       }
210    }
211    
212    
213    // Protected ----------------------------------------------------------------
214 
215    /**
216     * Retrieve an object of the persistent class using its unique identifier (primary key)
217     * @param id The identifier of the persistent object to retrieve
218     * @return The EntityObject corresponding to <b>id</b>
219     */
220    protected EntityObject getById(final long id){
221       try{
222          // Execute session.get() through hibernate template.
223          EntityObject obj = (EntityObject)getHibernateTemplate().execute(new HibernateCallback(){
224             public Object doInHibernate(Session session) throws HibernateException{
225                return session.get(getPersistentClass(), new Long(id));
226             }
227          });
228          return obj;
229       }
230       catch (Exception e){
231          log.error("Exception in getById()!", e);
232          throw new InfrastructureException(e);
233       }
234    }
235 
236    /**
237     * Execute a named query defined in a Hibernate mapping file.
238     * Query will not be cached.
239     * @param queryName Name of the query to execute
240     * @return List of entity objects
241     */
242    protected List findNamedQuery(String queryName){
243       return findNamedQuery(queryName, false);
244    }
245 
246    /**
247     * Execute a named query defined in a Hibernate mapping file.
248     * @param queryName Name of the query to execute
249     * @param cacheable wether the query is cached or not
250     * @return List of entity objects
251     */
252    protected List findNamedQuery(String queryName, boolean cacheable){
253       return findNamedQueryStringParam(queryName, null, null, cacheable);
254    }
255 
256    /**
257     * Execute a named query defined in a Hibernate mapping file.
258     * @param queryName Name of the query to execute
259     * @param cacheable wether the query is cached or not
260     * @param maxResultCount Max number of results
261     * @return List of entity objects
262     */
263    protected List findNamedQuery(String queryName, boolean cacheable, int maxResultCount){
264       return findNamedQueryStringParam(queryName, null, null, cacheable, maxResultCount);
265    }
266 
267    /**
268     * Execute a named query with one param defined in a Hibernate mapping file.
269     * Query will not be cached.
270     * @param queryName Name of the query to execute
271     * @param paramName Name of the query parameter
272     * @param paramValue Value of the query parameter
273     * @return List of entity objects
274     */
275    protected List findNamedQueryStringParam(String queryName, String paramName, Object paramValue){
276       return findNamedQueryStringParam(queryName, paramName, paramValue, false);
277    }
278 
279    /**
280     * Execute a named query with one param defined in a Hibernate mapping file.
281     * @param queryName Name of the query to execute
282     * @param paramName Name of the query parameter
283     * @param paramValue Value of the query parameter
284     * @param cacheable wether the query is cached or not
285     * @return List of entity objects
286     */
287    protected List findNamedQueryStringParam(String queryName, String paramName, Object paramValue, boolean cacheable){
288       return findNamedQueryStringParams(queryName, paramName, paramValue, null, null, cacheable);
289    }
290 
291    /**
292     * Execute a named query with one param defined in a Hibernate mapping file.
293     * @param queryName Name of the query to execute
294     * @param paramName Name of the query parameter
295     * @param paramValue Value of the query parameter
296     * @param cacheable wether the query is cached or not
297     * @param maxResultCount Max number of results
298     * @return List of entity objects
299     */
300    protected List findNamedQueryStringParam(String queryName, String paramName, Object paramValue, boolean cacheable, int maxResultCount){
301       return findNamedQueryStringParams(queryName, paramName, paramValue, null, null, null, null, cacheable, maxResultCount);
302    }
303 
304    /**
305     * Execute a named query with two params defined in a Hibernate mapping file.
306     * Query will not be cached.
307     * @param queryName Name of the query to execute
308     * @param paramName Name of the query parameter
309     * @param paramValue Value of the query parameter
310     * @param param2Name Name of the 2nd query parameter
311     * @param param2Value Value of the 2nd query parameter
312     * @return List of entity objects
313     */
314    protected List findNamedQueryStringParams(String queryName, String paramName, Object paramValue, String param2Name, Object param2Value){
315       return findNamedQueryStringParams(queryName, paramName, paramValue, param2Name, param2Value, null, null, false);
316    }
317 
318    /**
319     * Execute a named query with two params defined in a Hibernate mapping file.
320     * @param queryName Name of the query to execute
321     * @param paramName Name of the query parameter
322     * @param paramValue Value of the query parameter
323     * @param param2Name Name of the 2nd query parameter
324     * @param param2Value Value of the 2nd query parameter
325     * @param cacheable wether the query is cached or not
326     * @return List of entity objects
327     */
328    protected List findNamedQueryStringParams(String queryName, String paramName, Object paramValue, String param2Name, Object param2Value, boolean cacheable){
329       return findNamedQueryStringParams(queryName, paramName, paramValue, param2Name, param2Value, null, null, cacheable, -1);
330    }
331 
332    /**
333     * Execute a named query with three params defined in a Hibernate mapping file.
334     * Query will not be cached.
335     * @param queryName Name of the query to execute
336     * @param paramName Name of the query parameter
337     * @param paramValue Value of the query parameter
338     * @param param2Name Name of the 2nd query parameter
339     * @param param2Value Value of the 2nd query parameter
340     * @param param3Name Name of the 3rd query parameter
341     * @param param3Value Value of the 3rd query parameter
342     * @return List of entity objects
343     */
344    protected List findNamedQueryStringParams(String queryName, String paramName, Object paramValue,
345              String param2Name, Object param2Value, String param3Name, Object param3Value){
346       return findNamedQueryStringParams(queryName, paramName, paramValue, param2Name, param2Value, param3Name, param3Value, false);
347    }
348 
349    /**
350     * Execute a named query with two params defined in a Hibernate mapping file.
351     * @param queryName Name of the query to execute
352     * @param paramName Name of the query parameter
353     * @param paramValue Value of the query parameter
354     * @param param2Name Name of the 2nd query parameter
355     * @param param2Value Value of the 2nd query parameter
356     * @param param3Name Name of the 3rd query parameter
357     * @param param3Value Value of the 3rd query parameter
358     * @param cacheable wether the query is cached or not
359     * @return List of entity objects
360     */
361    protected List findNamedQueryStringParams(String queryName, String paramName, Object paramValue,
362                String param2Name, Object param2Value, String param3Name, Object param3Value, boolean cacheable){
363       return findNamedQueryStringParams(queryName, paramName, paramValue, param2Name, param2Value, param3Name, param3Value, cacheable, -1);
364    }
365 
366 
367    /**
368     * Execute a named query with two params defined in a Hibernate mapping file.
369     * @param queryName Name of the query to execute
370     * @param paramName Name of the query parameter
371     * @param paramValue Value of the query parameter
372     * @param param2Name Name of the 2nd query parameter
373     * @param param2Value Value of the 2nd query parameter
374     * @param param3Name Name of the 3rd query parameter
375     * @param param3Value Value of the 3rd query parameter
376     * @param cacheable wether the query is cached or not
377     * @param maxResultCount Max number of results
378     * @return List of entity objects
379     */
380    protected List findNamedQueryStringParams(final String queryName, final String paramName, final Object paramValue,
381          final String param2Name, final Object param2Value, final String param3Name, final Object param3Value, final boolean cacheable, final int maxResultCount){
382             
383       return getHibernateTemplate().executeFind(new HibernateCallback(){
384          public Object doInHibernate(Session session) throws HibernateException{
385             Query queryObject = session.getNamedQuery(queryName);
386             if (cacheable)
387                queryObject.setCacheable(true);
388             SessionFactoryUtils.applyTransactionTimeout(queryObject, getSessionFactory());
389             if (paramName != null)
390                queryObject.setParameter(paramName, paramValue);
391             if (param2Name != null)
392                queryObject.setParameter(param2Name, param2Value);
393             if (param3Name != null)
394                queryObject.setParameter(param3Name, param3Value);
395             if (maxResultCount != -1)
396                queryObject.setMaxResults(maxResultCount);
397             return queryObject.list();
398          }
399       });
400    }
401 
402    /**
403     * Filter a list of results that should only contain one element
404     * @param results The list of results to filter
405     * @return The element if it is the only one, otherwise log an error and return null 
406     */
407    protected Object findSingleObject(List results){
408       if (results != null && results.size() == 1)
409          return results.get(0);
410       if (results != null && results.size() > 1){
411          // Hibernate doesn't return a distinct result list when fetching a collection ...
412          // Try making the result distinct ourselves, using a Set.
413          HashSet distinctResults = new HashSet(results);
414          if (distinctResults.size() == 1)
415             return distinctResults.iterator().next();
416          // Log an error ...
417          log.error("Uh oh, problem... - found more than one object when single object requested: " + results);
418       }
419       return null;
420    }
421 }