View Javadoc
1   /***
2    * License Agreement.
3    * 
4    * JSPA (POJO-SP)
5    * 
6    * Copyright (C) 2009 HRX Pty Ltd
7    * 
8    * This file is part of JSPA.
9    * 
10   * JSPA is free software: you can redistribute it and/or modify it under the
11   * terms of the GNU Lesser General Public License version 3 as published by the
12   * Free Software Foundation.
13   * 
14   * JSPA is distributed in the hope that it will be useful, but WITHOUT ANY
15   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16   * A PARTICULAR PURPOSE. See the Lesser General Public License for more details.
17   * 
18   * You should have received a copy of the GNU Lesser General Public License
19   * along with JSPA. If not, see <http://www.gnu.org/licenses/>.
20   */
21  package com.hrx.rasp.util.dao;
22  
23  import java.lang.reflect.Constructor;
24  import java.lang.reflect.Field;
25  import java.sql.ResultSet;
26  import java.sql.SQLException;
27  import java.util.ArrayList;
28  import java.util.List;
29  
30  import javax.persistence.Column;
31  import javax.persistence.EmbeddedId;
32  import javax.persistence.Entity;
33  
34  import org.apache.log4j.Logger;
35  
36  import com.hrx.rasp.util.dao.annotations.DAOConstructor;
37  import com.hrx.rasp.util.dao.exception.StoredProcedureProccessResultException;
38  import com.hrx.rasp.util.dao.metadata.MetadataHelper;
39  
40  /***
41   * The default implementation will return an ArrayList containing instances of
42   * the DAO that calls the JDBC Stored Procedure.
43   * 
44   * The default implementation is expected to be overwrite for any generic Store
45   * Procedure where the CURSOR will returns different data type than the DAO
46   * caller type.
47   * 
48   * When using this implementation the DAO implementation must implement a
49   * Constructor the has the same arguments and in the same order as the CURSOR
50   * record properties.
51   * 
52   * @author dan.stoica <dan.stoica@acslink.net.au>
53   * 
54   */
55  public class DefaultResultSetProcessor<E> implements ResultSetProcessor<E>
56  {
57  	private static Logger log = Logger.getLogger(DefaultResultSetProcessor.class);
58  
59  	/***
60  	 * The maximum number of results to retrieve from result set.
61  	 * 
62  	 */
63  	int maxResult = 0;
64  
65  	/***
66  	 * The start position of the first result, numbered from 0 for ResutlSet
67  	 * parameters
68  	 */
69  	int startPosition = 0;
70  
71  	/*
72  	 * (non-Javadoc)
73  	 * 
74  	 * @see com.hrx.rasp.util.dao.ResultSetProcessor#create(java.sql.ResultSet,
75  	 * java.lang.Class)
76  	 */
77  	public List<E> create(ResultSet rs, Class<E> resultClass, boolean isSecret) throws SQLException, StoredProcedureProccessResultException
78  	{
79  
80  		if (rs == null)
81  		{
82  			return null;
83  		}
84  		// Generate an ArrayList for results
85  		List<E> values = null;
86  
87  		values = processConstructor(rs, resultClass, isSecret);
88  		if (values == null)
89  		{
90  			try
91  			{
92  				values = processColumnSetters(rs, resultClass);
93  			}
94  			catch (InstantiationException e)
95  			{
96  				throw new StoredProcedureProccessResultException(resultClass.getCanonicalName(), e);
97  			}
98  			catch (IllegalAccessException e)
99  			{
100 				throw new StoredProcedureProccessResultException(resultClass.getCanonicalName(), e);
101 			}
102 		}
103 		return values;
104 	}
105 
106 	/***
107 	 * @param rs
108 	 * @param resultClass
109 	 * @param values
110 	 * @return
111 	 * @throws StoredProcedureProccessResultException
112 	 */
113 	private List<E> processConstructor(ResultSet rs, Class<E> resultClass, boolean isSecret) throws StoredProcedureProccessResultException
114 	{
115 		try
116 		{
117 			Constructor<E> constuctor = findConstructor(resultClass);
118 
119 			if (constuctor != null)
120 			{
121 				int endRecord = startPosition + maxResult;
122 				List<E> values = new ArrayList<E>();
123 				// transform the record into Java Return Objects
124 				while (rs.next() && (maxResult == 0 || rs.getRow() < endRecord))
125 				{
126 					if (rs.getRow() >= startPosition)
127 					{
128 						if (!isSecret && log.isTraceEnabled())
129 						{
130 							StoredProcUtility.dumpResultSetRecord(rs);
131 						}
132 						
133 						E value = MetadataHelper.createInstance(rs, constuctor);
134 						values.add(value);
135 					}
136 				}
137 				return values;
138 			}
139 		}
140 		catch (Exception e)
141 		{
142 			throw new StoredProcedureProccessResultException(e);
143 		}
144 		return null;
145 	}
146 
147 	/***
148 	 * Find the constructor to use for the Result List. It must be annotated
149 	 * with DAOConstructor annotation.
150 	 * 
151 	 * @param constrs
152 	 * @return
153 	 */
154 	protected Constructor<E> findConstructorWithAnnotation(Constructor<E>[] constrs)
155 	{
156 		/***
157 		 * Find the DAO constructor that has been annotated with DAOConstructor
158 		 * annotation. This will be used to create the new DAO instances for
159 		 * each database record returned in the database ResultSet
160 		 * 
161 		 */
162 		for (Constructor<E> cons : constrs)
163 		{
164 			if (cons.isAnnotationPresent(DAOConstructor.class))
165 			{
166 				return cons;
167 			}
168 		}
169 		return null;
170 	}
171 
172 	@SuppressWarnings("unchecked")
173 	private Constructor<E> findConstructor(Class<E> resultClass) throws SQLException
174 	{
175 		Constructor<E>[] constrs = (Constructor<E>[]) resultClass.getConstructors();
176 		Constructor<E> constuctor = findConstructorWithAnnotation(constrs);
177 		return constuctor;
178 	}
179 
180 	/***
181 	 * @param spMetaData
182 	 * @param returnType
183 	 * @throws SQLException
184 	 * @throws IllegalAccessException
185 	 * @throws InstantiationException
186 	 * @throws StoredProcedureProccessResultException
187 	 */
188 	private List<E> processColumnSetters(ResultSet rs, Class<E> resultClass) throws SQLException, InstantiationException, IllegalAccessException,
189 					StoredProcedureProccessResultException
190 	{
191 		/*
192 		 * If no annotated constructor found, just try any available
193 		 * constructor.
194 		 */
195 
196 		if (resultClass.isAnnotationPresent(Entity.class))
197 		{
198 			try
199 			{
200 				List<E> entities = new ArrayList<E>();
201 
202 				Field[] fields = resultClass.getDeclaredFields();
203 				// transform the record into Java Return Objects
204 
205 				int endRecord = startPosition + maxResult;
206 
207 				// transform the record into Java Return Objects
208 				while (rs.next() && (maxResult == 0 || rs.getRow() < endRecord))
209 				{
210 					if (rs.getRow() >= startPosition)
211 					{
212 						StoredProcUtility.dumpResultSetRecord(log, rs);
213 						E entity = resultClass.newInstance();
214 						for (Field field : fields)
215 						{
216 							if (field.isAnnotationPresent(Column.class))
217 							{
218 								Object value = getValue(rs, field);
219 								MetadataHelper.setPropertyValue(field, value, entity);
220 							}
221 							else if (field.isAnnotationPresent(EmbeddedId.class))
222 							{
223 								// set the pk fields
224 								Class<?> pkClass = field.getClass();
225 								Object pk = pkClass.newInstance();
226 								for (Field pkField : pkClass.getDeclaredFields())
227 								{
228 									if (pkField.isAnnotationPresent(Column.class))
229 									{
230 										Object value = getValue(rs, pkField);
231 										MetadataHelper.setPropertyValue(pkField, value, pk);
232 									}
233 								}
234 								MetadataHelper.setPropertyValue(field, pk, entity);
235 							}
236 						}
237 						entities.add(entity);
238 						if (log.isTraceEnabled())
239 						{
240 							log.trace("Process setter parameters succesful for entity:'" + entity.toString() + "'");
241 						}
242 					}
243 				}
244 				return entities;
245 			}
246 			catch (Exception e)
247 			{
248 				throw new StoredProcedureProccessResultException("Result Set Entity processor error.", e);
249 			}
250 		}
251 		throw new StoredProcedureProccessResultException("Expect an Entity bean. The '" + resultClass.getName() + "' class is not an Entity.");
252 	}
253 
254 	private Object getValue(ResultSet rs, Field field) throws SQLException, StoredProcedureProccessResultException
255 	{
256 		Column column = field.getAnnotation(Column.class);
257 		String columnName = column.name();
258 		if (columnName.length() == 0)
259 		{
260 			// Use the field name if the property name is not defined.
261 			columnName = field.getName();
262 		}
263 		Class<?> type = ((Field) field).getType();
264 		Object value;
265 		try
266 		{
267 			int columnIndex = rs.findColumn(columnName);
268 			value = StoredProcUtility.getResultSetObject(rs, columnIndex, type);
269 		}
270 		catch (SQLException e)
271 		{
272 			value = null;
273 		}
274 		return value;
275 	}
276 
277 	/***
278 	 * @return the maxResult
279 	 */
280 	public int getMaxResult()
281 	{
282 		return maxResult;
283 	}
284 
285 	/***
286 	 * @param maxResult
287 	 *            the maxResult to set
288 	 */
289 	public void setMaxResult(int maxResult)
290 	{
291 		this.maxResult = maxResult;
292 	}
293 
294 	/***
295 	 * @return the startPosition
296 	 */
297 	public int getStartPosition()
298 	{
299 		return startPosition;
300 	}
301 
302 	/***
303 	 * @param startPosition
304 	 *            the startPosition to set
305 	 */
306 	public void setStartPosition(int startPosition)
307 	{
308 		this.startPosition = startPosition;
309 	}
310 
311 }