View Javadoc

1   //----------------------------------------------------------------------
2   // 
3   // PerfectJPattern: "Design patterns are good but components are better!" 
4   // SpringGenericDao.java Copyright (c) 2009 Giovanni Azua Garcia
5   // bravegag@hotmail.com
6   //  
7   // This program is free software; you can redistribute it and/or
8   // modify it under the terms of the GNU General Public License
9   // as published by the Free Software Foundation; either version 3
10  // of the License, or (at your option) any later version.
11  //
12  // This program is distributed in the hope that it will be useful,
13  // but WITHOUT ANY WARRANTY; without even the implied warranty of
14  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  // GNU General Public License for more details.
16  //
17  // You should have received a copy of the GNU General Public License
18  // along with this program; if not, see <http://www.gnu.org/licenses/>.
19  //
20  //----------------------------------------------------------------------
21  package org.perfectjpattern.jee.integration.dao;
22  
23  import java.io.*;
24  import java.lang.annotation.*;
25  import java.lang.reflect.*;
26  import java.util.*;
27  
28  import org.hibernate.*;
29  import org.hibernate.type.Type;
30  import org.perfectjpattern.jee.api.integration.dao.*;
31  
32  /**
33   * Spring-based implementation of {@link IGenericDao}
34   * 
35   * @param <Id> Identification type
36   * @param <Element> Element type
37   * 
38   * @author <a href="mailto:bravegag@hotmail.com">Giovanni Azua</a>
39   * @version $ $Date: Nov 5, 2008 11:40:00 AM $
40   */
41  public 
42  class SpringGenericDao<Id extends Serializable, Element>
43  extends HibernateGenericDao<Id, Element>
44  implements IFinderExecutor<Element>
45  {
46      //------------------------------------------------------------------------
47      // public
48      //------------------------------------------------------------------------
49      /** 
50       * {@inheritDoc}
51       */
52      @SuppressWarnings("unchecked")
53      public List<Element> 
54      execute(Method aMethod, Object... anArguments)
55      {
56          final Query myQuery = prepareQuery(aMethod, anArguments);
57          
58          return myQuery.list();
59      }
60      
61      //------------------------------------------------------------------------
62      /**
63       * Returns the namingStrategy
64       * 
65       * @return the namingStrategy
66       */
67      public final IFinderNamingStrategy 
68      getNamingStrategy()
69      {
70          return theNamingStrategy;
71      }
72  
73      //------------------------------------------------------------------------
74      /**
75       * Sets the namingStrategy
76       * 
77       * @param aNamingStrategy the namingStrategy to set
78       */
79      public final void 
80      setNamingStrategy(IFinderNamingStrategy aNamingStrategy)
81      {
82          theNamingStrategy = aNamingStrategy;
83      }
84  
85      //------------------------------------------------------------------------
86      /**
87       * Returns the argumentTypeResolver
88       * 
89       * @return the argumentTypeResolver
90       */
91      public final IArgumentTypeResolver 
92      getArgumentTypeResolver()
93      {
94          return theArgumentTypeResolver;
95      }
96  
97      //------------------------------------------------------------------------
98      /**
99       * Sets the argumentTypeResolver
100      * 
101      * @param anArgumentTypeResolver the argumentTypeResolver to set
102      */
103     public final void 
104     setArgumentTypeResolver(IArgumentTypeResolver anArgumentTypeResolver)
105     {
106         theArgumentTypeResolver = anArgumentTypeResolver;
107     }        
108     
109     //------------------------------------------------------------------------
110     // protected
111     //------------------------------------------------------------------------
112     /**
113      * Constructs a {@link SpringGenericReadOnlyDao} instance from the 
114      * persistent class type and the {@link ISessionStrategy} that creates 
115      * {@link Session} instances and the {@link ITransactionStrategy} that 
116      * creates {@link Transaction} instances 
117      * 
118      * @param aPersistentClass The persistent Java Bean class
119      * @param aSessionStrategy Strategy that creates Sessions
120      * @param aTransactionStrategy Strategy that creates Transaction
121      * @throws IllegalArgumentException 'aPersistentClass' must not be null
122      * @throws IllegalArgumentException 'aPersistentClass' must be a class type
123      * @throws IllegalArgumentException 'aDaoSessionStrategy' must not be null
124      * @throws IllegalArgumentException 'aDaoTransactionStrategy' must not be 
125      *         null
126      */
127     protected
128     SpringGenericDao(Class<Element> aPersistentClass, 
129         ISessionStrategy aSessionStrategy, 
130             ITransactionStrategy aTransactionStrategy)
131     {
132         super(aPersistentClass, aSessionStrategy, aTransactionStrategy);
133     }    
134     
135     //------------------------------------------------------------------------
136     // private
137     //------------------------------------------------------------------------
138     private Query 
139     prepareQuery(Method aMethod, Object... anArguments) 
140     {
141         assert aMethod != null : "'aMethod' must not be null";
142         assert anArguments != null : "'anArguments' must not be null";
143         
144         final String myQueryName = getNamingStrategy().getQueryName(
145             getPersistentClass(), aMethod);
146         
147         final Query myQuery = getActualSession().getNamedQuery(myQueryName);
148         
149         String[] myQueryParameters = myQuery.getNamedParameters();
150         if (myQueryParameters.length == 0) 
151         {
152             setPositionalParams(myQuery, anArguments);
153         } 
154         else 
155         {
156             Annotation myAnnotation = null;
157             final int myNumberOfAnnotations = aMethod.getAnnotations().length;
158             for (int i = 0; i < myNumberOfAnnotations; ++i)
159             {
160                 if (aMethod.getAnnotations()[i] instanceof QueryParameters)
161                 {
162                     myAnnotation = aMethod.getAnnotations()[i];
163                     
164                     break;
165                 }
166             }
167             
168             assert myAnnotation != null : "'" + aMethod.getName() + 
169                 "' does not include the required QueryParameters annotation";
170             
171             String[] myAnnotatedParameters = ((QueryParameters) 
172                 myAnnotation).names();
173             
174             final int myNumberOfAnnotated = myAnnotatedParameters.length;
175             final int myNumberOfParameters = myQueryParameters.length;
176 
177             assert myNumberOfAnnotated == myNumberOfParameters : "Arguments " +
178                 "mismatch, all query parameters must be annotated"; 
179             
180             
181             setNamedParams(myQuery, anArguments, myQueryParameters, 
182                 myAnnotatedParameters);
183         }
184         
185         return myQuery;
186     }    
187     
188     //------------------------------------------------------------------------
189     /**
190      * Assigns the parameters according to their order in the finder method 
191      * signature. This method is called when the query parameters are defined
192      * as ? and not named.
193      * 
194      * @param aNamedQuery The Hibernate named query defined in the mapping
195      * @param aMethodArguments The finder method argument values
196      */
197     private void 
198     setPositionalParams(Query aNamedQuery, Object[] aMethodArguments) 
199     {
200         assert aNamedQuery != null : "'aNamedQuery' must not be null";
201         assert aMethodArguments != null : "'anArguments' must not be null";
202         
203         // set parameter
204         for (int i = 0; i < aMethodArguments.length; i++) 
205         {
206             Object myArgument = aMethodArguments[i];
207             
208             Type myArgumentType = getArgumentTypeResolver().getArgumentType(
209                 myArgument);
210             
211             if (myArgumentType != null) 
212             {
213                 aNamedQuery.setParameter(i, myArgument, myArgumentType);
214             } 
215             else 
216             {                    
217                 aNamedQuery.setParameter(i, myArgument);
218             }
219         }
220     }
221     
222     //------------------------------------------------------------------------
223     /**
224      * Assigns the parameters of the interface finder method to the named 
225      * query. The parameters are assigned based on the name of the finder 
226      * method arguments to match those of the named query. The arguments 
227      * are not assumed to be in any specific order but will be matched 
228      * according to the name. In case that there is a mismatch i.e. missing 
229      * or outstanding parameter in either side a {@link IllegalAccessError} 
230      * will be thrown. 
231      * 
232      * @param aNamedQuery The Hibernate named query defined in the mapping
233      * @param aMethodArguments The finder method argument values
234      * @param aQueryParameters The named query parameter place holders
235      * @param aMethodParameters The ordered parameter names as defined in 
236      *        the finder method signature
237      * @throws IllegalAccessError "Mismatching number of arguments" 
238      */
239     private void 
240     setNamedParams(Query aNamedQuery, Object[] aMethodArguments, 
241         String[] aQueryParameters, String[] aMethodParameters) 
242     {        
243         assert aNamedQuery != null : "'aNamedQuery' must not be null";
244         assert aMethodArguments != null : "'anArguments' must not be null";
245         assert aQueryParameters != null : "'aNamedParameters' must not be null";
246         assert aMethodParameters != null : 
247             "'anOrderedArgumentNames' must not be null";
248 
249         if (aMethodArguments.length != aQueryParameters.length || 
250             aQueryParameters.length != aMethodParameters.length)
251         {
252             throw new IllegalAccessError("Mismatching number of arguments");
253         }
254 
255         // populate a Map of Method parameter values keyed by parameter name
256         Map<String, Object> myArgumentsMap = new HashMap<String, Object>();
257         final int myNumberOfParameters = aMethodArguments.length;
258         for (int i = 0; i < myNumberOfParameters; i++)
259         {
260             myArgumentsMap.put(aMethodParameters[i], aMethodArguments[i]);
261         }
262                 
263         // finally assign the parameters
264         for (String myQueryParameter : aQueryParameters)
265         {
266             // do the parameter matching taking into account naming conventions
267             Object myArgument = null;
268             if (myArgumentsMap.containsKey(myQueryParameter))
269             {
270                 myArgument = myArgumentsMap.get(myQueryParameter); 
271             }
272             
273             assert myArgument != null : "Query parameter '" + 
274                 myQueryParameter + "' was not found, check for misspelling";
275             
276             Type myArgumentType = getArgumentTypeResolver().getArgumentType(
277                 myArgument);
278             
279             if (myArgumentType != null) 
280             {
281                 aNamedQuery.setParameter(myQueryParameter, myArgument, 
282                     myArgumentType);
283             } 
284             else 
285             {
286                 if (myArgument instanceof Collection) 
287                 {
288                     aNamedQuery.setParameterList(myQueryParameter, 
289                         (Collection<?>) myArgument);
290                 } 
291                 else 
292                 {
293                     aNamedQuery.setParameter(myQueryParameter, myArgument);
294                 }
295             }            
296         }        
297     }
298     
299     //------------------------------------------------------------------------
300     // members
301     //------------------------------------------------------------------------
302     /**
303      * Defines an strategy for discovering the finder methods
304      */
305     private IFinderNamingStrategy theNamingStrategy = 
306         new SimpleFinderNamingStrategy();
307     
308     /**
309      * Provides ability to map instances to their corresponding type
310      */
311     private IArgumentTypeResolver theArgumentTypeResolver = 
312         new SimpleArgumentTypeResolver();
313 }