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
73
74
75
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
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
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
193
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
204
205 int endRecord = startPosition + maxResult;
206
207
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
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
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 }