Skip to content

Commit fbf1051

Browse files
committed
fixes #372
2 parents bd60088 + 942c2c7 commit fbf1051

19 files changed

+208
-121
lines changed

src/main/java/org/apache/ibatis/builder/MapperBuilderAssistant.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -271,11 +271,11 @@ public MappedStatement addMappedStatement(
271271
String databaseId,
272272
LanguageDriver lang,
273273
String resultSets) {
274-
274+
275275
if (unresolvedCacheRef) {
276276
throw new IncompleteElementException("Cache-ref not yet resolved");
277277
}
278-
278+
279279
id = applyCurrentNamespace(id, false);
280280
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
281281

@@ -392,7 +392,7 @@ public ResultMapping buildResultMapping(
392392
Class<? extends TypeHandler<?>> typeHandler,
393393
List<ResultFlag> flags,
394394
String resultSet,
395-
String foreignColumn,
395+
String foreignColumn,
396396
boolean lazy) {
397397
Class<?> javaTypeClass = resolveResultJavaType(resultType, property, javaType);
398398
TypeHandler<?> typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler);
@@ -448,7 +448,7 @@ private List<ResultMapping> parseCompositeColumnName(String columnName) {
448448
private Class<?> resolveResultJavaType(Class<?> resultType, String property, Class<?> javaType) {
449449
if (javaType == null && property != null) {
450450
try {
451-
MetaClass metaResultType = MetaClass.forClass(resultType);
451+
MetaClass metaResultType = MetaClass.forClass(resultType, configuration.getReflectorFactory());
452452
javaType = metaResultType.getSetterType(property);
453453
} catch (Exception e) {
454454
//ignore, following null check statement will deal with the situation
@@ -467,7 +467,7 @@ private Class<?> resolveParameterJavaType(Class<?> resultType, String property,
467467
} else if (Map.class.isAssignableFrom(resultType)) {
468468
javaType = Object.class;
469469
} else {
470-
MetaClass metaResultType = MetaClass.forClass(resultType);
470+
MetaClass metaResultType = MetaClass.forClass(resultType, configuration.getReflectorFactory());
471471
javaType = metaResultType.getGetterType(property);
472472
}
473473
}
@@ -491,9 +491,9 @@ public ResultMapping buildResultMapping(
491491
Class<? extends TypeHandler<?>> typeHandler,
492492
List<ResultFlag> flags) {
493493
return buildResultMapping(
494-
resultType, property, column, javaType, jdbcType, nestedSelect,
494+
resultType, property, column, javaType, jdbcType, nestedSelect,
495495
nestedResultMap, notNullColumn, columnPrefix, typeHandler, flags, null, null, configuration.isLazyLoadingEnabled());
496-
}
496+
}
497497

498498
public LanguageDriver getLanguageDriver(Class<?> langClass) {
499499
if (langClass != null) {
@@ -526,9 +526,9 @@ public MappedStatement addMappedStatement(
526526
String databaseId,
527527
LanguageDriver lang) {
528528
return addMappedStatement(
529-
id, sqlSource, statementType, sqlCommandType, fetchSize, timeout,
530-
parameterMap, parameterType, resultMap, resultType, resultSetType,
531-
flushCache, useCache, resultOrdered, keyGenerator, keyProperty,
529+
id, sqlSource, statementType, sqlCommandType, fetchSize, timeout,
530+
parameterMap, parameterType, resultMap, resultType, resultSetType,
531+
flushCache, useCache, resultOrdered, keyGenerator, keyProperty,
532532
keyColumn, databaseId, lang, null);
533533
}
534534

src/main/java/org/apache/ibatis/builder/SqlSourceBuilder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ private ParameterMapping buildParameterMapping(String content) {
7979
} else if (JdbcType.CURSOR.name().equals(propertiesMap.get("jdbcType"))) {
8080
propertyType = java.sql.ResultSet.class;
8181
} else if (property != null) {
82-
MetaClass metaClass = MetaClass.forClass(parameterType);
82+
MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory());
8383
if (metaClass.hasGetter(property)) {
8484
propertyType = metaClass.getGetterType(property);
8585
} else {
@@ -133,5 +133,5 @@ private Map<String, String> parseParameterMapping(String content) {
133133
}
134134
}
135135
}
136-
136+
137137
}

src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@
3232
import org.apache.ibatis.parsing.XNode;
3333
import org.apache.ibatis.parsing.XPathParser;
3434
import org.apache.ibatis.plugin.Interceptor;
35+
import org.apache.ibatis.reflection.DefaultReflectorFactory;
3536
import org.apache.ibatis.reflection.MetaClass;
37+
import org.apache.ibatis.reflection.ReflectorFactory;
3638
import org.apache.ibatis.reflection.factory.ObjectFactory;
3739
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
3840
import org.apache.ibatis.session.AutoMappingBehavior;
@@ -50,6 +52,7 @@ public class XMLConfigBuilder extends BaseBuilder {
5052
private boolean parsed;
5153
private XPathParser parser;
5254
private String environment;
55+
private ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
5356

5457
public XMLConfigBuilder(Reader reader) {
5558
this(reader, null, null);
@@ -101,6 +104,7 @@ private void parseConfiguration(XNode root) {
101104
pluginElement(root.evalNode("plugins"));
102105
objectFactoryElement(root.evalNode("objectFactory"));
103106
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
107+
reflectionFactoryElement(root.evalNode("reflectionFactory"));
104108
settingsElement(root.evalNode("settings"));
105109
// read it after objectFactory and objectWrapperFactory issue #631
106110
environmentsElement(root.evalNode("environments"));
@@ -166,6 +170,14 @@ private void objectWrapperFactoryElement(XNode context) throws Exception {
166170
}
167171
}
168172

173+
private void reflectionFactoryElement(XNode context) throws Exception {
174+
if (context != null) {
175+
String type = context.getStringAttribute("type");
176+
ReflectorFactory factory = (ReflectorFactory) resolveClass(type).newInstance();
177+
configuration.setReflectorFactory(factory);
178+
}
179+
}
180+
169181
private void propertiesElement(XNode context) throws Exception {
170182
if (context != null) {
171183
Properties defaults = context.getChildrenAsProperties();
@@ -192,7 +204,7 @@ private void settingsElement(XNode context) throws Exception {
192204
if (context != null) {
193205
Properties props = context.getChildrenAsProperties();
194206
// Check that all settings are known to the configuration class
195-
MetaClass metaConfig = MetaClass.forClass(Configuration.class);
207+
MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
196208
for (Object key : props.keySet()) {
197209
if (!metaConfig.hasSetter(String.valueOf(key))) {
198210
throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
@@ -222,7 +234,7 @@ private void settingsElement(XNode context) throws Exception {
222234
configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
223235
}
224236
}
225-
237+
226238
private void environmentsElement(XNode context) throws Exception {
227239
if (context != null) {
228240
if (environment == null) {

src/main/java/org/apache/ibatis/executor/result/DefaultMapResultHandler.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.Map;
1919

2020
import org.apache.ibatis.reflection.MetaObject;
21+
import org.apache.ibatis.reflection.ReflectorFactory;
2122
import org.apache.ibatis.reflection.factory.ObjectFactory;
2223
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
2324
import org.apache.ibatis.session.ResultContext;
@@ -32,19 +33,21 @@ public class DefaultMapResultHandler<K, V> implements ResultHandler<V> {
3233
private final String mapKey;
3334
private final ObjectFactory objectFactory;
3435
private final ObjectWrapperFactory objectWrapperFactory;
36+
private final ReflectorFactory reflectorFactory;
3537

3638
@SuppressWarnings("unchecked")
37-
public DefaultMapResultHandler(String mapKey, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {
39+
public DefaultMapResultHandler(String mapKey, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
3840
this.objectFactory = objectFactory;
3941
this.objectWrapperFactory = objectWrapperFactory;
42+
this.reflectorFactory = reflectorFactory;
4043
this.mappedResults = objectFactory.create(Map.class);
4144
this.mapKey = mapKey;
4245
}
4346

4447
@Override
4548
public void handleResult(ResultContext<? extends V> context) {
4649
final V value = context.getResultObject();
47-
final MetaObject mo = MetaObject.forObject(value, objectFactory, objectWrapperFactory);
50+
final MetaObject mo = MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
4851
// TODO is that assignment always true?
4952
final K key = (K) mo.getValue(mapKey);
5053
mappedResults.put(key, value);

src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.apache.ibatis.mapping.ResultMapping;
4848
import org.apache.ibatis.reflection.MetaClass;
4949
import org.apache.ibatis.reflection.MetaObject;
50+
import org.apache.ibatis.reflection.ReflectorFactory;
5051
import org.apache.ibatis.reflection.factory.ObjectFactory;
5152
import org.apache.ibatis.session.AutoMappingBehavior;
5253
import org.apache.ibatis.session.Configuration;
@@ -73,6 +74,7 @@ public class DefaultResultSetHandler implements ResultSetHandler {
7374
private final BoundSql boundSql;
7475
private final TypeHandlerRegistry typeHandlerRegistry;
7576
private final ObjectFactory objectFactory;
77+
private final ReflectorFactory reflectorFactory;
7678

7779
// nested resultmaps
7880
private final Map<CacheKey, Object> nestedResultObjects = new HashMap<CacheKey, Object>();
@@ -82,12 +84,12 @@ public class DefaultResultSetHandler implements ResultSetHandler {
8284
// multiple resultsets
8385
private final Map<String, ResultMapping> nextResultMaps = new HashMap<String, ResultMapping>();
8486
private final Map<CacheKey, List<PendingRelation>> pendingRelations = new HashMap<CacheKey, List<PendingRelation>>();
85-
87+
8688
private static class PendingRelation {
8789
public MetaObject metaObject;
8890
public ResultMapping propertyMapping;
8991
}
90-
92+
9193
public DefaultResultSetHandler(Executor executor, MappedStatement mappedStatement, ParameterHandler parameterHandler, ResultHandler<?> resultHandler, BoundSql boundSql,
9294
RowBounds rowBounds) {
9395
this.executor = executor;
@@ -98,6 +100,7 @@ public DefaultResultSetHandler(Executor executor, MappedStatement mappedStatemen
98100
this.boundSql = boundSql;
99101
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
100102
this.objectFactory = configuration.getObjectFactory();
103+
this.reflectorFactory = configuration.getReflectorFactory();
101104
this.resultHandler = resultHandler;
102105
}
103106

@@ -143,7 +146,7 @@ private void handleRefCursorOutputParameter(ResultSet rs, ParameterMapping param
143146
@Override
144147
public List<Object> handleResultSets(Statement stmt) throws SQLException {
145148
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
146-
149+
147150
final List<Object> multipleResults = new ArrayList<Object>();
148151

149152
int resultSetCount = 0;
@@ -269,7 +272,7 @@ private void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHa
269272
} else {
270273
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
271274
}
272-
}
275+
}
273276

274277
private void ensureNoRowBounds() {
275278
if (configuration.isSafeRowBoundsEnabled() && rowBounds != null && (rowBounds.getLimit() < RowBounds.NO_ROW_LIMIT || rowBounds.getOffset() > RowBounds.NO_ROW_OFFSET)) {
@@ -281,10 +284,10 @@ private void ensureNoRowBounds() {
281284
protected void checkResultHandler() {
282285
if (resultHandler != null && configuration.isSafeResultHandlerEnabled() && !mappedStatement.isResultOrdered()) {
283286
throw new ExecutorException("Mapped Statements with nested result mappings cannot be safely used with a custom ResultHandler. "
284-
+ "Use safeResultHandlerEnabled=false setting to bypass this check "
287+
+ "Use safeResultHandlerEnabled=false setting to bypass this check "
285288
+ "or ensure your statement returns ordered data and set resultOrdered=true on it.");
286289
}
287-
}
290+
}
288291

289292
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
290293
throws SQLException {
@@ -337,7 +340,7 @@ private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQL
337340
if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
338341
final MetaObject metaObject = configuration.newMetaObject(resultObject);
339342
boolean foundValues = !resultMap.getConstructorResultMappings().isEmpty();
340-
if (shouldApplyAutomaticMappings(resultMap, false)) {
343+
if (shouldApplyAutomaticMappings(resultMap, false)) {
341344
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
342345
}
343346
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
@@ -374,9 +377,9 @@ private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap,
374377
if (propertyMapping.getNestedResultMapId() != null) {
375378
// the user added a column attribute to a nested result map, ignore it
376379
column = null;
377-
}
378-
if (propertyMapping.isCompositeResult()
379-
|| (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
380+
}
381+
if (propertyMapping.isCompositeResult()
382+
|| (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
380383
|| propertyMapping.getResultSet() != null) {
381384
Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
382385
// issue #541 make property optional
@@ -518,7 +521,7 @@ private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, Res
518521
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
519522
throws SQLException {
520523
final Class<?> resultType = resultMap.getType();
521-
final MetaClass metaType = MetaClass.forClass(resultType);
524+
final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
522525
final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
523526
if (typeHandlerRegistry.hasTypeHandler(resultType)) {
524527
return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
@@ -761,7 +764,7 @@ private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap r
761764
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
762765
}
763766
}
764-
767+
765768
//
766769
// GET VALUE FROM ROW FOR NESTED RESULT MAP
767770
//
@@ -782,7 +785,7 @@ private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey c
782785
boolean foundValues = !resultMap.getConstructorResultMappings().isEmpty();
783786
if (shouldApplyAutomaticMappings(resultMap, true)) {
784787
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
785-
}
788+
}
786789
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
787790
putAncestor(absoluteKey, resultObject, resultMapId, columnPrefix);
788791
foundValues = applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues;
@@ -822,13 +825,13 @@ private boolean applyNestedResultMappings(ResultSetWrapper rsw, ResultMap result
822825
rowKey = createRowKey(nestedResultMap, rsw, ancestorColumnPrefix.get(nestedResultMapId));
823826
ancestorObject = ancestorObjects.get(rowKey);
824827
}
825-
if (ancestorObject != null) {
828+
if (ancestorObject != null) {
826829
if (newObject) {
827830
linkObjects(metaObject, resultMapping, ancestorObject); // issue #385
828831
}
829832
} else {
830833
rowKey = createRowKey(nestedResultMap, rsw, columnPrefix);
831-
final CacheKey combinedKey = combineKeys(rowKey, parentRowKey);
834+
final CacheKey combinedKey = combineKeys(rowKey, parentRowKey);
832835
Object rowValue = nestedResultObjects.get(combinedKey);
833836
boolean knownValue = (rowValue != null);
834837
instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject); // mandatory
@@ -946,7 +949,7 @@ private void createRowKeyForMappedProperties(ResultMap resultMap, ResultSetWrapp
946949
}
947950

948951
private void createRowKeyForUnmappedProperties(ResultMap resultMap, ResultSetWrapper rsw, CacheKey cacheKey, String columnPrefix) throws SQLException {
949-
final MetaClass metaType = MetaClass.forClass(resultMap.getType());
952+
final MetaClass metaType = MetaClass.forClass(resultMap.getType(), reflectorFactory);
950953
List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
951954
for (String column : unmappedColumnNames) {
952955
String property = column;
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* Copyright 2009-2015 the original author or authors.
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 org.apache.ibatis.reflection;
17+
18+
import java.util.concurrent.ConcurrentHashMap;
19+
import java.util.concurrent.ConcurrentMap;
20+
21+
public class DefaultReflectorFactory implements ReflectorFactory {
22+
private boolean classCacheEnabled = true;
23+
private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<Class<?>, Reflector>();
24+
25+
public DefaultReflectorFactory() {
26+
}
27+
28+
@Override
29+
public boolean isClassCacheEnabled() {
30+
return classCacheEnabled;
31+
}
32+
33+
@Override
34+
public void setClassCacheEnabled(boolean classCacheEnabled) {
35+
this.classCacheEnabled = classCacheEnabled;
36+
}
37+
38+
@Override
39+
public Reflector findForClass(Class<?> type) {
40+
if (classCacheEnabled) {
41+
// synchronized (type) removed see issue #461
42+
Reflector cached = reflectorMap.get(type);
43+
if (cached == null) {
44+
cached = new Reflector(type);
45+
reflectorMap.put(type, cached);
46+
}
47+
return cached;
48+
} else {
49+
return new Reflector(type);
50+
}
51+
}
52+
53+
}

0 commit comments

Comments
 (0)