1 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 ; 37 38 import javax.xml.parsers.DocumentBuilderFactory ; 39 import javax.xml.parsers.ParserConfigurationException ; 40 import java.sql.ResultSet ; 41 import java.sql.SQLException ; 42 import java.util.*; 43 44 47 public class BasicResultMap implements ResultMap { 48 49 private static final Probe PROBE = ProbeFactory.getProbe(); 50 51 private String id; 52 private Class resultClass; 53 54 private ResultMapping[] resultMappings; 56 private ThreadLocal remappableResultMappings = new ThreadLocal (); 57 58 private DataExchange dataExchange; 59 60 private List nestedResultMappings; 61 62 private Discriminator discriminator; 63 64 private Set groupByProps; 65 66 private String xmlName; 67 68 private String resource; 69 70 private SqlMapExecutorDelegate delegate; 71 72 protected boolean allowRemapping = false; 73 74 79 public BasicResultMap(SqlMapExecutorDelegate delegate) { 80 this.delegate = delegate; 81 } 82 83 88 public SqlMapExecutorDelegate getDelegate() { 89 return delegate; 90 } 91 92 public String getId() { 93 return id; 94 } 95 96 101 public void setId(String id) { 102 this.id = id; 103 } 104 105 public Class getResultClass() { 106 return resultClass; 107 } 108 109 public Object getUniqueKey(Object [] values) { 110 if (groupByProps != null) { 111 StringBuffer keyBuffer = new StringBuffer (); 112 for (int i = 0; i < getResultMappings().length; i++) { 113 String 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 134 public void setResultClass(Class resultClass) { 135 this.resultClass = resultClass; 136 } 137 138 143 public DataExchange getDataExchange() { 144 return dataExchange; 145 } 146 147 152 public void setDataExchange(DataExchange dataExchange) { 153 this.dataExchange = dataExchange; 154 } 155 156 161 public String getXmlName() { 162 return xmlName; 163 } 164 165 170 public void setXmlName(String xmlName) { 171 this.xmlName = xmlName; 172 } 173 174 179 public String getResource() { 180 return resource; 181 } 182 183 188 public void setResource(String resource) { 189 this.resource = resource; 190 } 191 192 public void addGroupByProperty(String 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 rs) throws SQLException { 234 ResultMap subMap = this; 235 if (discriminator != null) { 236 BasicResultMapping mapping = (BasicResultMapping)discriminator.getResultMapping(); 237 Object 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 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 272 public int getResultCount() { 273 return this.getResultMappings().length; 274 } 275 276 281 public Object [] getResults(RequestScope request, ResultSet 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 [] columnValues = new Object [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 .class); 299 } else if (DomTypeMarker.class.isAssignableFrom(resultClass)) { 300 Class 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 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 setResultObjectValues(RequestScope request, Object resultObject, Object [] values) { 322 Object ukey = getUniqueKey(values); 323 324 Map uniqueKeys = request.getUniqueKeys(this); 325 326 if (uniqueKeys != null && uniqueKeys.containsKey(ukey)) { 327 resultObject = uniqueKeys.get(ukey); 329 applyNestedResultMap(request, resultObject, values); 330 resultObject = NO_VALUE; 331 } else if (ukey == null || uniqueKeys == null || !uniqueKeys.containsKey(ukey)) { 332 resultObject = dataExchange.setData(request, this, resultObject, values); 334 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 resultObject = NO_VALUE; 346 } 347 348 return resultObject; 349 } 350 351 private void applyNestedResultMap(RequestScope request, Object resultObject, Object [] 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 resultObject, Object [] values) { 363 try { 364 365 String resultMapName = mapping.getNestedResultMapName(); 366 ResultMap resultMap = getDelegate().getResultMap(resultMapName); 367 Class type = mapping.getJavaType(); 368 String 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 .class || type == HashSet.class) { 379 c = new HashSet(); 380 } else { 381 try { 382 c = (Collection) type.newInstance(); 383 } catch (Exception 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 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 getNestedSelectMappingValue(RequestScope request, ResultSet rs, BasicResultMapping mapping, Class targetType) 404 throws SQLException { 405 try { 406 TypeHandlerFactory typeHandlerFactory = getDelegate().getTypeHandlerFactory(); 407 408 String statementName = mapping.getStatementName(); 409 ExtendedSqlMapClient client = (ExtendedSqlMapClient) request.getSession().getSqlMapClient(); 410 411 MappedStatement mappedStatement = client.getMappedStatement(statementName); 412 Class parameterType = mappedStatement.getParameterClass(); 413 Object 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 result = null; 428 if (parameterObject != null) { 429 430 Sql sql = mappedStatement.getSql(); 431 ResultMap resultMap = sql.getResultMap(request, parameterObject); 432 Class 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 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 e) { 454 throw new NestedSQLException("Error setting nested bean property. Cause: " + e, e); 455 } catch (IllegalAccessException e) { 456 throw new NestedSQLException("Error setting nested bean property. Cause: " + e, e); 457 } 458 459 } 460 461 private Object preparePrimitiveParameterObject(ResultSet rs, BasicResultMapping mapping, Class parameterType) throws SQLException { 462 Object 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 newDocument(String root) { 470 try { 471 Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); 472 doc.appendChild(doc.createElement(root)); 473 return doc; 474 } catch (ParserConfigurationException e) { 475 throw new NestedRuntimeException("Error creating XML document. Cause: " + e); 476 } 477 } 478 479 private Object prepareDomParameterObject(ResultSet rs, BasicResultMapping mapping) throws SQLException { 480 TypeHandlerFactory typeHandlerFactory = getDelegate().getTypeHandlerFactory(); 481 482 Document doc = newDocument("parameter"); 483 Probe probe = ProbeFactory.getProbe(doc); 484 485 String complexName = mapping.getColumnName(); 486 487 TypeHandler stringTypeHandler = typeHandlerFactory.getTypeHandler(String .class); 488 if (complexName.indexOf('=') > -1) { 489 StringTokenizer parser = new StringTokenizer(complexName, "{}=, ", false); 491 while (parser.hasMoreTokens()) { 492 String propName = parser.nextToken(); 493 String colName = parser.nextToken(); 494 Object propValue = stringTypeHandler.getResult(rs, colName); 495 probe.setObject(doc, propName, propValue.toString()); 496 } 497 } else { 498 Object propValue = stringTypeHandler.getResult(rs, complexName); 500 probe.setObject(doc, "value", propValue.toString()); 501 } 502 503 return doc; 504 } 505 506 507 private Object prepareBeanParameterObject(ResultSet rs, BasicResultMapping mapping, Class parameterType) 508 throws InstantiationException , IllegalAccessException , SQLException { 509 TypeHandlerFactory typeHandlerFactory = getDelegate().getTypeHandlerFactory(); 510 511 Object parameterObject; 512 if (parameterType == null) { 513 parameterObject = new HashMap(); 514 } else { 515 parameterObject = Resources.instantiate(parameterType); 516 } 517 String 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 propName = parser.nextToken(); 524 String colName = parser.nextToken(); 525 Class propType = PROBE.getPropertyTypeForSetter(parameterObject, propName); 526 TypeHandler propTypeHandler = typeHandlerFactory.getTypeHandler(propType); 527 Object propValue = propTypeHandler.getResult(rs, colName); 528 PROBE.setObject(parameterObject, propName, propValue); 529 } 530 } else { 531 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 getPrimitiveResultMappingValue(ResultSet rs, BasicResultMapping mapping) throws SQLException { 543 Object value = null; 544 TypeHandler typeHandler = mapping.getTypeHandler(); 545 if (typeHandler != null) { 546 String columnName = mapping.getColumnName(); 547 int columnIndex = mapping.getColumnIndex(); 548 String 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 |