KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ibatis > sqlmap > engine > mapping > result > BasicResultMap


1 /*
2  * Copyright 2004 Clinton Begin
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package com.ibatis.sqlmap.engine.mapping.result;
17
18 import com.ibatis.common.beans.Probe;
19 import com.ibatis.common.beans.ProbeFactory;
20 import com.ibatis.common.jdbc.exception.NestedSQLException;
21 import com.ibatis.common.resources.Resources;
22 import com.ibatis.common.exception.NestedRuntimeException;
23 import com.ibatis.sqlmap.client.SqlMapException;
24 import com.ibatis.sqlmap.engine.exchange.DataExchange;
25 import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient;
26 import com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate;
27 import com.ibatis.sqlmap.engine.mapping.result.loader.ResultLoader;
28 import com.ibatis.sqlmap.engine.mapping.sql.Sql;
29 import com.ibatis.sqlmap.engine.mapping.statement.MappedStatement;
30 import com.ibatis.sqlmap.engine.scope.ErrorContext;
31 import com.ibatis.sqlmap.engine.scope.RequestScope;
32 import com.ibatis.sqlmap.engine.type.DomCollectionTypeMarker;
33 import com.ibatis.sqlmap.engine.type.DomTypeMarker;
34 import com.ibatis.sqlmap.engine.type.TypeHandler;
35 import com.ibatis.sqlmap.engine.type.TypeHandlerFactory;
36 import org.w3c.dom.Document JavaDoc;
37
38 import javax.xml.parsers.DocumentBuilderFactory JavaDoc;
39 import javax.xml.parsers.ParserConfigurationException JavaDoc;
40 import java.sql.ResultSet JavaDoc;
41 import java.sql.SQLException JavaDoc;
42 import java.util.*;
43
44 /**
45  * Basic implementation of ResultMap interface
46  */

47 public class BasicResultMap implements ResultMap {
48
49   private static final Probe PROBE = ProbeFactory.getProbe();
50
51   private String JavaDoc id;
52   private Class JavaDoc resultClass;
53
54   // DO NOT ACCESS EITHER OF THESE OUTSIDE OF THEIR BEAN GETTER/SETTER
55
private ResultMapping[] resultMappings;
56   private ThreadLocal JavaDoc remappableResultMappings = new ThreadLocal JavaDoc();
57
58   private DataExchange dataExchange;
59
60   private List nestedResultMappings;
61
62   private Discriminator discriminator;
63
64   private Set JavaDoc groupByProps;
65
66   private String JavaDoc xmlName;
67
68   private String JavaDoc resource;
69
70   private SqlMapExecutorDelegate delegate;
71
72   protected boolean allowRemapping = false;
73
74   /**
75    * Constructor to pass a SqlMapExecutorDelegate in
76    *
77    * @param delegate - the SqlMapExecutorDelegate
78    */

79   public BasicResultMap(SqlMapExecutorDelegate delegate) {
80     this.delegate = delegate;
81   }
82
83   /**
84    * Getter for the SqlMapExecutorDelegate
85    *
86    * @return - the delegate
87    */

88   public SqlMapExecutorDelegate getDelegate() {
89     return delegate;
90   }
91
92   public String JavaDoc getId() {
93     return id;
94   }
95
96   /**
97    * Setter for the ID
98    *
99    * @param id - the new ID
100    */

101   public void setId(String JavaDoc id) {
102     this.id = id;
103   }
104
105   public Class JavaDoc getResultClass() {
106     return resultClass;
107   }
108
109   public Object JavaDoc getUniqueKey(Object JavaDoc[] values) {
110     if (groupByProps != null) {
111       StringBuffer JavaDoc keyBuffer = new StringBuffer JavaDoc();
112       for (int i = 0; i < getResultMappings().length; i++) {
113         String JavaDoc propertyName = getResultMappings()[i].getPropertyName();
114         if (groupByProps.contains(propertyName)) {
115           keyBuffer.append(values[i]);
116           keyBuffer.append('-');
117         }
118       }
119       if (keyBuffer.length() < 1) {
120         return null;
121       } else {
122         return keyBuffer.toString();
123       }
124     } else {
125       return null;
126     }
127   }
128
129   /**
130    * Setter for the result class (what the results will be mapped into)
131    *
132    * @param resultClass - the result class
133    */

134   public void setResultClass(Class JavaDoc resultClass) {
135     this.resultClass = resultClass;
136   }
137
138   /**
139    * Getter for the DataExchange object to be used
140    *
141    * @return - the DataExchange object
142    */

143   public DataExchange getDataExchange() {
144     return dataExchange;
145   }
146
147   /**
148    * Setter for the DataExchange object to be used
149    *
150    * @param dataExchange - the new DataExchange object
151    */

152   public void setDataExchange(DataExchange dataExchange) {
153     this.dataExchange = dataExchange;
154   }
155
156   /**
157    * Getter (used by DomDataExchange) for the xml name of the results
158    *
159    * @return - the name
160    */

161   public String JavaDoc getXmlName() {
162     return xmlName;
163   }
164
165   /**
166    * Setter (used by the SqlMapBuilder) for the xml name of the results
167    *
168    * @param xmlName - the name
169    */

170   public void setXmlName(String JavaDoc xmlName) {
171     this.xmlName = xmlName;
172   }
173
174   /**
175    * Getter for the resource (used to report errors)
176    *
177    * @return - the resource
178    */

179   public String JavaDoc getResource() {
180     return resource;
181   }
182
183   /**
184    * Setter for the resource (used by the SqlMapBuilder)
185    *
186    * @param resource - the resource name
187    */

188   public void setResource(String JavaDoc resource) {
189     this.resource = resource;
190   }
191
192   public void addGroupByProperty(String JavaDoc name) {
193     if (groupByProps == null) {
194       groupByProps = new HashSet();
195     }
196     groupByProps.add(name);
197   }
198
199   public boolean hasGroupBy() {
200     return groupByProps != null && groupByProps.size() > 0;
201   }
202
203   public Iterator groupByProps() {
204     return groupByProps.iterator();
205   }
206
207   public void addNestedResultMappings(ResultMapping mapping) {
208     if (nestedResultMappings == null) {
209       nestedResultMappings = new ArrayList();
210     }
211     nestedResultMappings.add(mapping);
212   }
213
214   public ResultMapping[] getResultMappings() {
215     if (allowRemapping) {
216       return (ResultMapping[]) remappableResultMappings.get();
217     } else {
218       return resultMappings;
219     }
220   }
221
222   public void setDiscriminator (Discriminator discriminator) {
223     if (this.discriminator != null) {
224       throw new SqlMapException ("A discriminator may only be set once per result map.");
225     }
226     this.discriminator = discriminator;
227   }
228
229   public Discriminator getDiscriminator() {
230     return discriminator;
231   }
232
233   public ResultMap resolveSubMap (RequestScope request, ResultSet JavaDoc rs) throws SQLException {
234     ResultMap subMap = this;
235     if (discriminator != null) {
236       BasicResultMapping mapping = (BasicResultMapping)discriminator.getResultMapping();
237       Object JavaDoc value = getPrimitiveResultMappingValue(rs, mapping);
238       subMap = discriminator.getSubMap(String.valueOf(value));
239       if (subMap == null) {
240         subMap = this;
241       } else if (subMap != this) {
242         subMap = subMap.resolveSubMap(request, rs);
243       }
244     }
245     return subMap;
246   }
247
248   /**
249    * Setter for a list of the individual ResultMapping objects
250    *
251    * @param resultMappingList - the list
252    */

253   public void setResultMappingList(List resultMappingList) {
254     if (allowRemapping) {
255       this.remappableResultMappings.set((BasicResultMapping[]) resultMappingList.toArray(new BasicResultMapping[resultMappingList.size()]));
256     } else {
257       this.resultMappings = (BasicResultMapping[]) resultMappingList.toArray(new BasicResultMapping[resultMappingList.size()]);
258     }
259
260
261     Map props = new HashMap();
262     props.put("map", this);
263     dataExchange = getDelegate().getDataExchangeFactory().getDataExchangeForClass(resultClass);
264     dataExchange.initialize(props);
265   }
266
267   /**
268    * Getter for the number of ResultMapping objects
269    *
270    * @return - the count
271    */

272   public int getResultCount() {
273     return this.getResultMappings().length;
274   }
275
276   /**
277    * @param rs
278    * @return
279    * @throws java.sql.SQLException
280    */

281   public Object JavaDoc[] getResults(RequestScope request, ResultSet JavaDoc rs)
282       throws SQLException {
283     ErrorContext errorContext = request.getErrorContext();
284     errorContext.setActivity("applying a result map");
285     errorContext.setObjectId(this.getId());
286     errorContext.setResource(this.getResource());
287     errorContext.setMoreInfo("Check the result map.");
288
289     boolean foundData = false;
290     Object JavaDoc[] columnValues = new Object JavaDoc[getResultMappings().length];
291     for (int i = 0; i < getResultMappings().length; i++) {
292       BasicResultMapping mapping = (BasicResultMapping) getResultMappings()[i];
293       errorContext.setMoreInfo(mapping.getErrorString());
294       if (mapping.getStatementName() != null) {
295         if (resultClass == null) {
296           throw new SqlMapException("The result class was null when trying to get results for ResultMap named " + getId() + ".");
297         } else if (Map.class.isAssignableFrom(resultClass)) {
298           columnValues[i] = getNestedSelectMappingValue(request, rs, mapping, Object JavaDoc.class);
299         } else if (DomTypeMarker.class.isAssignableFrom(resultClass)) {
300           Class JavaDoc javaType = mapping.getJavaType();
301           if (javaType == null) {
302             javaType = DomTypeMarker.class;
303           }
304           columnValues[i] = getNestedSelectMappingValue(request, rs, mapping, javaType);
305         } else {
306           Probe p = ProbeFactory.getProbe(resultClass);
307           Class JavaDoc type = p.getPropertyTypeForSetter(resultClass, mapping.getPropertyName());
308           columnValues[i] = getNestedSelectMappingValue(request, rs, mapping, type);
309         }
310       } else if (mapping.getNestedResultMapName() == null) {
311         columnValues[i] = getPrimitiveResultMappingValue(rs, mapping);
312       }
313       foundData = foundData || columnValues[i] != null;
314     }
315
316     request.setRowDataFound(foundData);
317
318     return columnValues;
319   }
320
321   public Object JavaDoc setResultObjectValues(RequestScope request, Object JavaDoc resultObject, Object JavaDoc[] values) {
322     Object JavaDoc ukey = getUniqueKey(values);
323
324     Map uniqueKeys = request.getUniqueKeys(this);
325
326     if (uniqueKeys != null && uniqueKeys.containsKey(ukey)) {
327       // Unique key is already known, so get the existing result object and process additional results.
328
resultObject = uniqueKeys.get(ukey);
329       applyNestedResultMap(request, resultObject, values);
330       resultObject = NO_VALUE;
331     } else if (ukey == null || uniqueKeys == null || !uniqueKeys.containsKey(ukey)) {
332       // Unique key is NOT known, so create a new result object and then process additional results.
333
resultObject = dataExchange.setData(request, this, resultObject, values);
334       // Lazy init key set, only if we're grouped by something (i.e. ukey != null)
335
if (ukey != null) {
336         if (uniqueKeys == null) {
337           uniqueKeys = new HashMap();
338           request.setUniqueKeys(this, uniqueKeys);
339         }
340         uniqueKeys.put(ukey, resultObject);
341       }
342       applyNestedResultMap(request, resultObject, values);
343     } else {
344       // Otherwise, we don't care about these results.
345
resultObject = NO_VALUE;
346     }
347
348     return resultObject;
349   }
350
351   private void applyNestedResultMap(RequestScope request, Object JavaDoc resultObject, Object JavaDoc[] values) {
352     if (resultObject != null && resultObject != NO_VALUE) {
353       if (nestedResultMappings != null) {
354         for (int i = 0, n = nestedResultMappings.size(); i < n; i++) {
355           BasicResultMapping resultMapping = (BasicResultMapping) nestedResultMappings.get(i);
356           setNestedResultMappingValue(resultMapping, request, resultObject, values);
357         }
358       }
359     }
360   }
361
362   protected void setNestedResultMappingValue(BasicResultMapping mapping, RequestScope request, Object JavaDoc resultObject, Object JavaDoc[] values) {
363     try {
364
365       String JavaDoc resultMapName = mapping.getNestedResultMapName();
366       ResultMap resultMap = getDelegate().getResultMap(resultMapName);
367       Class JavaDoc type = mapping.getJavaType();
368       String JavaDoc propertyName = mapping.getPropertyName();
369
370       Collection c = (Collection) PROBE.getObject(resultObject, propertyName);
371
372       if (c == null) {
373         if (type == null) {
374           type = PROBE.getPropertyTypeForSetter(resultObject, propertyName);
375         }
376         if (type == Collection.class || type == List.class || type == ArrayList.class) {
377           c = new ArrayList();
378         } else if (type == Set JavaDoc.class || type == HashSet.class) {
379           c = new HashSet();
380         } else {
381           try {
382             c = (Collection) type.newInstance();
383           } catch (Exception JavaDoc e) {
384             throw new SqlMapException("Error instantiating collection property for mapping '" + mapping.getPropertyName() + "'. Cause: " + e, e);
385           }
386         }
387         PROBE.setObject(resultObject, propertyName, c);
388       }
389
390       values = resultMap.getResults(request, request.getResultSet());
391       if (request.isRowDataFound()) {
392         Object JavaDoc o = resultMap.setResultObjectValues(request, null, values);
393         if (o != NO_VALUE) {
394           c.add(o);
395         }
396       }
397     } catch (SQLException e) {
398       throw new SqlMapException("Error getting nested result map values for '" + mapping.getPropertyName() + "'. Cause: " + e, e);
399     }
400
401   }
402
403   protected Object JavaDoc getNestedSelectMappingValue(RequestScope request, ResultSet JavaDoc rs, BasicResultMapping mapping, Class JavaDoc targetType)
404       throws SQLException {
405     try {
406       TypeHandlerFactory typeHandlerFactory = getDelegate().getTypeHandlerFactory();
407
408       String JavaDoc statementName = mapping.getStatementName();
409       ExtendedSqlMapClient client = (ExtendedSqlMapClient) request.getSession().getSqlMapClient();
410
411       MappedStatement mappedStatement = client.getMappedStatement(statementName);
412       Class JavaDoc parameterType = mappedStatement.getParameterClass();
413       Object JavaDoc parameterObject = null;
414
415       if (parameterType == null) {
416         parameterObject = prepareBeanParameterObject(rs, mapping, parameterType);
417       } else {
418         if (typeHandlerFactory.hasTypeHandler(parameterType)) {
419           parameterObject = preparePrimitiveParameterObject(rs, mapping, parameterType);
420         } else if (DomTypeMarker.class.isAssignableFrom(parameterType)) {
421           parameterObject = prepareDomParameterObject(rs, mapping);
422         } else {
423           parameterObject = prepareBeanParameterObject(rs, mapping, parameterType);
424         }
425       }
426
427       Object JavaDoc result = null;
428       if (parameterObject != null) {
429
430         Sql sql = mappedStatement.getSql();
431         ResultMap resultMap = sql.getResultMap(request, parameterObject);
432         Class JavaDoc resultClass = resultMap.getResultClass();
433
434         if (resultClass != null && !DomTypeMarker.class.isAssignableFrom(targetType)) {
435           if (DomCollectionTypeMarker.class.isAssignableFrom(resultClass)) {
436             targetType = DomCollectionTypeMarker.class;
437           } else if (DomTypeMarker.class.isAssignableFrom(resultClass)) {
438             targetType = DomTypeMarker.class;
439           }
440         }
441
442         result = ResultLoader.loadResult(client, statementName, parameterObject, targetType);
443
444         String JavaDoc nullValue = mapping.getNullValue();
445         if (result == null && nullValue != null) {
446           TypeHandler typeHandler = typeHandlerFactory.getTypeHandler(targetType);
447           if (typeHandler != null) {
448             result = typeHandler.valueOf(nullValue);
449           }
450         }
451       }
452       return result;
453     } catch (InstantiationException JavaDoc e) {
454       throw new NestedSQLException("Error setting nested bean property. Cause: " + e, e);
455     } catch (IllegalAccessException JavaDoc e) {
456       throw new NestedSQLException("Error setting nested bean property. Cause: " + e, e);
457     }
458
459   }
460
461   private Object JavaDoc preparePrimitiveParameterObject(ResultSet JavaDoc rs, BasicResultMapping mapping, Class JavaDoc parameterType) throws SQLException {
462     Object JavaDoc parameterObject;
463     TypeHandlerFactory typeHandlerFactory = getDelegate().getTypeHandlerFactory();
464     TypeHandler th = typeHandlerFactory.getTypeHandler(parameterType);
465     parameterObject = th.getResult(rs, mapping.getColumnName());
466     return parameterObject;
467   }
468
469   private Document JavaDoc newDocument(String JavaDoc root) {
470     try {
471       Document JavaDoc doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
472       doc.appendChild(doc.createElement(root));
473       return doc;
474     } catch (ParserConfigurationException JavaDoc e) {
475       throw new NestedRuntimeException("Error creating XML document. Cause: " + e);
476     }
477   }
478
479   private Object JavaDoc prepareDomParameterObject(ResultSet JavaDoc rs, BasicResultMapping mapping) throws SQLException {
480     TypeHandlerFactory typeHandlerFactory = getDelegate().getTypeHandlerFactory();
481
482     Document JavaDoc doc = newDocument("parameter");
483     Probe probe = ProbeFactory.getProbe(doc);
484
485     String JavaDoc complexName = mapping.getColumnName();
486
487     TypeHandler stringTypeHandler = typeHandlerFactory.getTypeHandler(String JavaDoc.class);
488     if (complexName.indexOf('=') > -1) {
489       // old 1.x style multiple params
490
StringTokenizer parser = new StringTokenizer(complexName, "{}=, ", false);
491       while (parser.hasMoreTokens()) {
492         String JavaDoc propName = parser.nextToken();
493         String JavaDoc colName = parser.nextToken();
494         Object JavaDoc propValue = stringTypeHandler.getResult(rs, colName);
495         probe.setObject(doc, propName, propValue.toString());
496       }
497     } else {
498       // single param
499
Object JavaDoc propValue = stringTypeHandler.getResult(rs, complexName);
500       probe.setObject(doc, "value", propValue.toString());
501     }
502
503     return doc;
504   }
505
506
507   private Object JavaDoc prepareBeanParameterObject(ResultSet JavaDoc rs, BasicResultMapping mapping, Class JavaDoc parameterType)
508       throws InstantiationException JavaDoc, IllegalAccessException JavaDoc, SQLException {
509     TypeHandlerFactory typeHandlerFactory = getDelegate().getTypeHandlerFactory();
510
511     Object JavaDoc parameterObject;
512     if (parameterType == null) {
513       parameterObject = new HashMap();
514     } else {
515       parameterObject = Resources.instantiate(parameterType);
516     }
517     String JavaDoc complexName = mapping.getColumnName();
518
519     if (complexName.indexOf('=') > -1
520         || complexName.indexOf(',') > -1) {
521       StringTokenizer parser = new StringTokenizer(complexName, "{}=, ", false);
522       while (parser.hasMoreTokens()) {
523         String JavaDoc propName = parser.nextToken();
524         String JavaDoc colName = parser.nextToken();
525         Class JavaDoc propType = PROBE.getPropertyTypeForSetter(parameterObject, propName);
526         TypeHandler propTypeHandler = typeHandlerFactory.getTypeHandler(propType);
527         Object JavaDoc propValue = propTypeHandler.getResult(rs, colName);
528         PROBE.setObject(parameterObject, propName, propValue);
529       }
530     } else {
531       // single param
532
TypeHandler propTypeHandler = typeHandlerFactory.getTypeHandler(parameterType);
533       if (propTypeHandler == null) {
534         propTypeHandler = typeHandlerFactory.getUnkownTypeHandler();
535       }
536       parameterObject = propTypeHandler.getResult(rs, complexName);
537     }
538
539     return parameterObject;
540   }
541
542   protected Object JavaDoc getPrimitiveResultMappingValue(ResultSet JavaDoc rs, BasicResultMapping mapping) throws SQLException {
543     Object JavaDoc value = null;
544     TypeHandler typeHandler = mapping.getTypeHandler();
545     if (typeHandler != null) {
546       String JavaDoc columnName = mapping.getColumnName();
547       int columnIndex = mapping.getColumnIndex();
548       String JavaDoc nullValue = mapping.getNullValue();
549       if (columnName == null) {
550         value = typeHandler.getResult(rs, columnIndex);
551       } else {
552         value = typeHandler.getResult(rs, columnName);
553       }
554       if (value == null && nullValue != null) {
555         value = typeHandler.valueOf(nullValue);
556       }
557     } else {
558       throw new SqlMapException("No type handler could be found to map the property '" + mapping.getPropertyName() + "' to the column '" + mapping.getColumnName() + "'. One or both of the types, or the combination of types is not supported.");
559     }
560     return value;
561   }
562
563
564 }
565
566
Popular Tags