diff --git a/.travis.yml b/.travis.yml index 593876855..880602d32 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,6 @@ language: java +sudo: false + jdk: - oraclejdk8 - oraclejdk7 diff --git a/pom.xml b/pom.xml index 0bb52ab26..0bfd60daf 100644 --- a/pom.xml +++ b/pom.xml @@ -20,16 +20,17 @@ org.mybatis mybatis-parent - 24 + 26 + mybatis - 3.3.0 + 3.3.2-SNAPSHOT jar mybatis - The MyBatis data mapper framework makes it easier to use a relational database with object-oriented + The MyBatis SQL mapper framework makes it easier to use a relational database with object-oriented applications. MyBatis couples objects with stored procedures or SQL statements using a XML descriptor or annotations. Simplicity is the biggest advantage of the MyBatis data mapper over object relational mapping tools. @@ -107,7 +108,7 @@ http://github.com/mybatis/mybatis-3 scm:git:ssh://github.com/mybatis/mybatis-3.git scm:git:ssh://git@github.com/mybatis/mybatis-3.git - mybatis-3.3.0 + HEAD GitHub Issue Management @@ -119,13 +120,14 @@ - github - gitsite:git@github.com/mybatis/mybatis-3.git + gh-pages + Mybatis GitHub Pages + git:ssh://git@github.com/mybatis/mybatis-3.git?gh-pages# - 3.2.8 + 3.3.0 org.apache.ibatis.* org.apache.ibatis.*;version=${project.version};-noimport:=true *;resolution:=optional @@ -136,8 +138,8 @@ ognl ognl - 3.0.11 - provided + 3.1.2 + compile true @@ -146,24 +148,23 @@ - org.javassist javassist - 3.18.2-GA - provided + 3.20.0-GA + compile true org.slf4j slf4j-api - 1.7.12 + 1.7.14 true org.slf4j slf4j-log4j12 - 1.7.12 + 1.7.14 true @@ -172,10 +173,11 @@ 1.2.17 true + org.apache.logging.log4j log4j-core - 2.2 + 2.3 true @@ -187,7 +189,7 @@ cglib cglib - 3.1 + 3.2.0 true @@ -201,13 +203,13 @@ org.hsqldb hsqldb - 2.3.2 + 2.3.3 test org.apache.derby derby - 10.11.1.1 + 10.12.1.1 test @@ -216,6 +218,7 @@ 1.10.19 test + commons-dbcp commons-dbcp @@ -249,12 +252,13 @@ org.apache.maven.plugins maven-surefire-plugin + -Xmx1024m derby.stream.error.file target/derby.log - + @@ -262,52 +266,36 @@ maven-pdf-plugin - org.sonatype.plugins - jarjar-maven-plugin - - {classes} - {test-classes} - - ognl:ognl - org.javassist:javassist - - - - ognl.** - org.apache.ibatis.ognl.@1 - - - javassist.** - org.apache.ibatis.javassist.@1 - - - org.apache.ibatis.** - - - true - + org.apache.maven.plugins + maven-shade-plugin - jarjar-classes - process-test-classes + package - jarjar + shade - {classes} - - - - jarjar-test-classes - process-test-classes - - jarjar - - - {test-classes} - - - + false + + + org.mybatis:mybatis + ognl:ognl + org.javassist:javassist + + + + + ognl + org.apache.ibatis.ognl + + + javassist + org.apache.ibatis.javassist + + + + + org.apache.maven.plugins @@ -317,27 +305,14 @@ - org.codehaus.mojo - cobertura-maven-plugin + org.jacoco + jacoco-maven-plugin - - - org.apache.ibatis.ognl.* - org.apache.ibatis.javassist.* - - - org/apache/ibatis/ognl/**/*.class - org/apache/ibatis/javassist/**/*.class - - + + org.apache.ibatis.ognl.* + org.apache.ibatis.javassist.* + - - - - clean - - - diff --git a/src/main/java/org/apache/ibatis/annotations/Options.java b/src/main/java/org/apache/ibatis/annotations/Options.java index 671fa1947..c0df8f19f 100644 --- a/src/main/java/org/apache/ibatis/annotations/Options.java +++ b/src/main/java/org/apache/ibatis/annotations/Options.java @@ -46,4 +46,5 @@ String keyProperty() default "id"; String keyColumn() default ""; + } diff --git a/src/main/java/org/apache/ibatis/annotations/Results.java b/src/main/java/org/apache/ibatis/annotations/Results.java index 00ad42106..45c36453c 100644 --- a/src/main/java/org/apache/ibatis/annotations/Results.java +++ b/src/main/java/org/apache/ibatis/annotations/Results.java @@ -26,5 +26,9 @@ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Results { + /** + * The name of the result map. + */ + String id() default ""; Result[] value() default {}; } diff --git a/src/main/java/org/apache/ibatis/builder/MapperBuilderAssistant.java b/src/main/java/org/apache/ibatis/builder/MapperBuilderAssistant.java index 676085fe1..d43c1787b 100644 --- a/src/main/java/org/apache/ibatis/builder/MapperBuilderAssistant.java +++ b/src/main/java/org/apache/ibatis/builder/MapperBuilderAssistant.java @@ -128,11 +128,9 @@ public Cache useNewCache(Class typeClass, boolean readWrite, boolean blocking, Properties props) { - typeClass = valueOrDefault(typeClass, PerpetualCache.class); - evictionClass = valueOrDefault(evictionClass, LruCache.class); Cache cache = new CacheBuilder(currentNamespace) - .implementation(typeClass) - .addDecorator(evictionClass) + .implementation(valueOrDefault(typeClass, PerpetualCache.class)) + .addDecorator(valueOrDefault(evictionClass, LruCache.class)) .clearInterval(flushInterval) .size(size) .readWrite(readWrite) @@ -146,8 +144,7 @@ public Cache useNewCache(Class typeClass, public ParameterMap addParameterMap(String id, Class parameterClass, List parameterMappings) { id = applyCurrentNamespace(id, false); - ParameterMap.Builder parameterMapBuilder = new ParameterMap.Builder(configuration, id, parameterClass, parameterMappings); - ParameterMap parameterMap = parameterMapBuilder.build(); + ParameterMap parameterMap = new ParameterMap.Builder(configuration, id, parameterClass, parameterMappings).build(); configuration.addParameterMap(parameterMap); return parameterMap; } @@ -167,13 +164,13 @@ public ParameterMapping buildParameterMapping( Class javaTypeClass = resolveParameterJavaType(parameterType, property, javaType, jdbcType); TypeHandler typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler); - ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, javaTypeClass); - builder.jdbcType(jdbcType); - builder.resultMapId(resultMap); - builder.mode(parameterMode); - builder.numericScale(numericScale); - builder.typeHandler(typeHandlerInstance); - return builder.build(); + return new ParameterMapping.Builder(configuration, property, javaTypeClass) + .jdbcType(jdbcType) + .resultMapId(resultMap) + .mode(parameterMode) + .numericScale(numericScale) + .typeHandler(typeHandlerInstance) + .build(); } public ResultMap addResultMap( @@ -186,7 +183,6 @@ public ResultMap addResultMap( id = applyCurrentNamespace(id, false); extend = applyCurrentNamespace(extend, true); - ResultMap.Builder resultMapBuilder = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping); if (extend != null) { if (!configuration.hasResultMap(extend)) { throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'"); @@ -212,8 +208,9 @@ public ResultMap addResultMap( } resultMappings.addAll(extendedResultMappings); } - resultMapBuilder.discriminator(discriminator); - ResultMap resultMap = resultMapBuilder.build(); + ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping) + .discriminator(discriminator) + .build(); configuration.addResultMap(resultMap); return resultMap; } @@ -246,8 +243,7 @@ public Discriminator buildDiscriminator( resultMap = applyCurrentNamespace(resultMap, true); namespaceDiscriminatorMap.put(e.getKey(), resultMap); } - Discriminator.Builder discriminatorBuilder = new Discriminator.Builder(configuration, resultMapping, namespaceDiscriminatorMap); - return discriminatorBuilder.build(); + return new Discriminator.Builder(configuration, resultMapping, namespaceDiscriminatorMap).build(); } public MappedStatement addMappedStatement( @@ -279,22 +275,28 @@ public MappedStatement addMappedStatement( id = applyCurrentNamespace(id, false); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; - MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType); - statementBuilder.resource(resource); - statementBuilder.fetchSize(fetchSize); - statementBuilder.statementType(statementType); - statementBuilder.keyGenerator(keyGenerator); - statementBuilder.keyProperty(keyProperty); - statementBuilder.keyColumn(keyColumn); - statementBuilder.databaseId(databaseId); - statementBuilder.lang(lang); - statementBuilder.resultOrdered(resultOrdered); - statementBuilder.resulSets(resultSets); - setStatementTimeout(timeout, statementBuilder); - - setStatementParameterMap(parameterMap, parameterType, statementBuilder); - setStatementResultMap(resultMap, resultType, resultSetType, statementBuilder); - setStatementCache(isSelect, flushCache, useCache, currentCache, statementBuilder); + MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType) + .resource(resource) + .fetchSize(fetchSize) + .timeout(timeout) + .statementType(statementType) + .keyGenerator(keyGenerator) + .keyProperty(keyProperty) + .keyColumn(keyColumn) + .databaseId(databaseId) + .lang(lang) + .resultOrdered(resultOrdered) + .resulSets(resultSets) + .resultMaps(getStatementResultMaps(resultMap, resultType, id)) + .resultSetType(resultSetType) + .flushCacheRequired(valueOrDefault(flushCache, !isSelect)) + .useCache(valueOrDefault(useCache, isSelect)) + .cache(currentCache); + + ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id); + if (statementParameterMap != null) { + statementBuilder.parameterMap(statementParameterMap); + } MappedStatement statement = statementBuilder.build(); configuration.addMappedStatement(statement); @@ -305,47 +307,33 @@ private T valueOrDefault(T value, T defaultValue) { return value == null ? defaultValue : value; } - private void setStatementCache( - boolean isSelect, - boolean flushCache, - boolean useCache, - Cache cache, - MappedStatement.Builder statementBuilder) { - flushCache = valueOrDefault(flushCache, !isSelect); - useCache = valueOrDefault(useCache, isSelect); - statementBuilder.flushCacheRequired(flushCache); - statementBuilder.useCache(useCache); - statementBuilder.cache(cache); - } - - private void setStatementParameterMap( - String parameterMap, + private ParameterMap getStatementParameterMap( + String parameterMapName, Class parameterTypeClass, - MappedStatement.Builder statementBuilder) { - parameterMap = applyCurrentNamespace(parameterMap, true); - - if (parameterMap != null) { + String statementId) { + parameterMapName = applyCurrentNamespace(parameterMapName, true); + ParameterMap parameterMap = null; + if (parameterMapName != null) { try { - statementBuilder.parameterMap(configuration.getParameterMap(parameterMap)); + parameterMap = configuration.getParameterMap(parameterMapName); } catch (IllegalArgumentException e) { - throw new IncompleteElementException("Could not find parameter map " + parameterMap, e); + throw new IncompleteElementException("Could not find parameter map " + parameterMapName, e); } } else if (parameterTypeClass != null) { List parameterMappings = new ArrayList(); - ParameterMap.Builder inlineParameterMapBuilder = new ParameterMap.Builder( + parameterMap = new ParameterMap.Builder( configuration, - statementBuilder.id() + "-Inline", + statementId + "-Inline", parameterTypeClass, - parameterMappings); - statementBuilder.parameterMap(inlineParameterMapBuilder.build()); + parameterMappings).build(); } + return parameterMap; } - private void setStatementResultMap( + private List getStatementResultMaps( String resultMap, Class resultType, - ResultSetType resultSetType, - MappedStatement.Builder statementBuilder) { + String statementId) { resultMap = applyCurrentNamespace(resultMap, true); List resultMaps = new ArrayList(); @@ -359,24 +347,15 @@ private void setStatementResultMap( } } } else if (resultType != null) { - ResultMap.Builder inlineResultMapBuilder = new ResultMap.Builder( + ResultMap inlineResultMap = new ResultMap.Builder( configuration, - statementBuilder.id() + "-Inline", + statementId + "-Inline", resultType, new ArrayList(), - null); - resultMaps.add(inlineResultMapBuilder.build()); - } - statementBuilder.resultMaps(resultMaps); - - statementBuilder.resultSetType(resultSetType); - } - - private void setStatementTimeout(Integer timeout, MappedStatement.Builder statementBuilder) { - if (timeout == null) { - timeout = configuration.getDefaultStatementTimeout(); + null).build(); + resultMaps.add(inlineResultMap); } - statementBuilder.timeout(timeout); + return resultMaps; } public ResultMapping buildResultMapping( @@ -400,19 +379,19 @@ public ResultMapping buildResultMapping( if (composites.size() > 0) { column = null; } - ResultMapping.Builder builder = new ResultMapping.Builder(configuration, property, column, javaTypeClass); - builder.jdbcType(jdbcType); - builder.nestedQueryId(applyCurrentNamespace(nestedSelect, true)); - builder.nestedResultMapId(applyCurrentNamespace(nestedResultMap, true)); - builder.resultSet(resultSet); - builder.typeHandler(typeHandlerInstance); - builder.flags(flags == null ? new ArrayList() : flags); - builder.composites(composites); - builder.notNullColumns(parseMultipleColumnNames(notNullColumn)); - builder.columnPrefix(columnPrefix); - builder.foreignColumn(foreignColumn); - builder.lazy(lazy); - return builder.build(); + return new ResultMapping.Builder(configuration, property, column, javaTypeClass) + .jdbcType(jdbcType) + .nestedQueryId(applyCurrentNamespace(nestedSelect, true)) + .nestedResultMapId(applyCurrentNamespace(nestedResultMap, true)) + .resultSet(resultSet) + .typeHandler(typeHandlerInstance) + .flags(flags == null ? new ArrayList() : flags) + .composites(composites) + .notNullColumns(parseMultipleColumnNames(notNullColumn)) + .columnPrefix(columnPrefix) + .foreignColumn(foreignColumn) + .lazy(lazy) + .build(); } private Set parseMultipleColumnNames(String columnName) { @@ -438,8 +417,9 @@ private List parseCompositeColumnName(String columnName) { while (parser.hasMoreTokens()) { String property = parser.nextToken(); String column = parser.nextToken(); - ResultMapping.Builder complexBuilder = new ResultMapping.Builder(configuration, property, column, configuration.getTypeHandlerRegistry().getUnknownTypeHandler()); - composites.add(complexBuilder.build()); + ResultMapping complexResultMapping = new ResultMapping.Builder( + configuration, property, column, configuration.getTypeHandlerRegistry().getUnknownTypeHandler()).build(); + composites.add(complexResultMapping); } } return composites; diff --git a/src/main/java/org/apache/ibatis/builder/annotation/MapperAnnotationBuilder.java b/src/main/java/org/apache/ibatis/builder/annotation/MapperAnnotationBuilder.java index 749174357..3a4455d6f 100644 --- a/src/main/java/org/apache/ibatis/builder/annotation/MapperAnnotationBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/annotation/MapperAnnotationBuilder.java @@ -196,6 +196,10 @@ private String parseResultMap(Method method) { } private String generateResultMapName(Method method) { + Results results = method.getAnnotation(Results.class); + if (results != null && !results.id().isEmpty()) { + return type.getName() + "." + results.id(); + } StringBuilder suffix = new StringBuilder(); for (Class c : method.getParameterTypes()) { suffix.append("-"); diff --git a/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java b/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java index 6c503ec82..24454908c 100644 --- a/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java @@ -18,6 +18,7 @@ import java.io.InputStream; import java.io.Reader; import java.util.Properties; +import java.util.Set; import javax.sql.DataSource; @@ -100,12 +101,14 @@ private void parseConfiguration(XNode root) { try { //issue #117 read properties first propertiesElement(root.evalNode("properties")); + Properties settings = settingsAsProperties(root.evalNode("settings")); + loadCustomVfs(settings); typeAliasesElement(root.evalNode("typeAliases")); pluginElement(root.evalNode("plugins")); objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); reflectionFactoryElement(root.evalNode("reflectionFactory")); - settingsElement(root.evalNode("settings")); + settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 environmentsElement(root.evalNode("environments")); databaseIdProviderElement(root.evalNode("databaseIdProvider")); @@ -116,6 +119,33 @@ private void parseConfiguration(XNode root) { } } + private Properties settingsAsProperties(XNode context) { + if (context == null) { + return new Properties(); + } + Properties props = context.getChildrenAsProperties(); + // Check that all settings are known to the configuration class + MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory); + for (Object key : props.keySet()) { + if (!metaConfig.hasSetter(String.valueOf(key))) { + throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive)."); + } + } + return props; + } + + private void loadCustomVfs(Properties props) throws ClassNotFoundException { + String value = props.getProperty("vfsImpl"); + if (value != null) { + String[] clazzes = value.split(","); + for (String clazz : clazzes) { + if (!clazz.isEmpty()) { + configuration.setVfsImpl(Resources.classForName(clazz)); + } + } + } + } + private void typeAliasesElement(XNode parent) { if (parent != null) { for (XNode child : parent.getChildren()) { @@ -200,39 +230,29 @@ private void propertiesElement(XNode context) throws Exception { } } - private void settingsElement(XNode context) throws Exception { - if (context != null) { - Properties props = context.getChildrenAsProperties(); - // Check that all settings are known to the configuration class - MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory); - for (Object key : props.keySet()) { - if (!metaConfig.hasSetter(String.valueOf(key))) { - throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive)."); - } - } - configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL"))); - configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true)); - configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory"))); - configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false)); - configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), true)); - configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true)); - configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true)); - configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false)); - configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE"))); - configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null)); - configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null)); - configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false)); - configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false)); - configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION"))); - configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER"))); - configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString")); - configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true)); - configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage"))); - configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false)); - configuration.setLogPrefix(props.getProperty("logPrefix")); - configuration.setLogImpl(resolveClass(props.getProperty("logImpl"))); - configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory"))); - } + private void settingsElement(Properties props) throws Exception { + configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL"))); + configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true)); + configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory"))); + configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false)); + configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), true)); + configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true)); + configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true)); + configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false)); + configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE"))); + configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null)); + configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null)); + configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false)); + configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false)); + configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION"))); + configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER"))); + configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString")); + configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true)); + configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage"))); + configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false)); + configuration.setLogPrefix(props.getProperty("logPrefix")); + configuration.setLogImpl(resolveClass(props.getProperty("logImpl"))); + configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory"))); } private void environmentsElement(XNode context) throws Exception { diff --git a/src/main/java/org/apache/ibatis/executor/keygen/Jdbc3KeyGenerator.java b/src/main/java/org/apache/ibatis/executor/keygen/Jdbc3KeyGenerator.java index eb28acc90..5b17bcdd7 100644 --- a/src/main/java/org/apache/ibatis/executor/keygen/Jdbc3KeyGenerator.java +++ b/src/main/java/org/apache/ibatis/executor/keygen/Jdbc3KeyGenerator.java @@ -19,14 +19,14 @@ import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.executor.ExecutorException; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.TypeHandler; import org.apache.ibatis.type.TypeHandlerRegistry; @@ -42,12 +42,10 @@ public void processBefore(Executor executor, MappedStatement ms, Statement stmt, @Override public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) { - List parameters = new ArrayList(); - parameters.add(parameter); - processBatch(ms, stmt, parameters); + processBatch(ms, stmt, getParameters(parameter)); } - public void processBatch(MappedStatement ms, Statement stmt, List parameters) { + public void processBatch(MappedStatement ms, Statement stmt, Collection parameters) { ResultSet rs = null; try { rs = stmt.getGeneratedKeys(); @@ -64,7 +62,7 @@ public void processBatch(MappedStatement ms, Statement stmt, List parame } final MetaObject metaParam = configuration.newMetaObject(parameter); if (typeHandlers == null) { - typeHandlers = getTypeHandlers(typeHandlerRegistry, metaParam, keyProperties); + typeHandlers = getTypeHandlers(typeHandlerRegistry, metaParam, keyProperties, rsmd); } populateKeys(rs, metaParam, keyProperties, typeHandlers); } @@ -82,12 +80,33 @@ public void processBatch(MappedStatement ms, Statement stmt, List parame } } - private TypeHandler[] getTypeHandlers(TypeHandlerRegistry typeHandlerRegistry, MetaObject metaParam, String[] keyProperties) { + private Collection getParameters(Object parameter) { + Collection parameters = null; + if (parameter instanceof Collection) { + parameters = (Collection) parameter; + } else if (parameter instanceof Map) { + Map parameterMap = (Map) parameter; + if (parameterMap.containsKey("collection")) { + parameters = (Collection) parameterMap.get("collection"); + } else if (parameterMap.containsKey("list")) { + parameters = (List) parameterMap.get("list"); + } else if (parameterMap.containsKey("array")) { + parameters = Arrays.asList((Object[]) parameterMap.get("array")); + } + } + if (parameters == null) { + parameters = new ArrayList(); + parameters.add(parameter); + } + return parameters; + } + + private TypeHandler[] getTypeHandlers(TypeHandlerRegistry typeHandlerRegistry, MetaObject metaParam, String[] keyProperties, ResultSetMetaData rsmd) throws SQLException { TypeHandler[] typeHandlers = new TypeHandler[keyProperties.length]; for (int i = 0; i < keyProperties.length; i++) { if (metaParam.hasSetter(keyProperties[i])) { Class keyPropertyType = metaParam.getSetterType(keyProperties[i]); - TypeHandler th = typeHandlerRegistry.getTypeHandler(keyPropertyType); + TypeHandler th = typeHandlerRegistry.getTypeHandler(keyPropertyType, JdbcType.forCode(rsmd.getColumnType(i + 1))); typeHandlers[i] = th; } } diff --git a/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java b/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java index a025097cf..9a4e5dcb9 100644 --- a/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java +++ b/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java @@ -60,6 +60,7 @@ /** * @author Clinton Begin * @author Eduardo Macarron + * @author Iwao AVE! */ public class DefaultResultSetHandler implements ResultSetHandler { @@ -78,18 +79,34 @@ public class DefaultResultSetHandler implements ResultSetHandler { // nested resultmaps private final Map nestedResultObjects = new HashMap(); - private final Map ancestorObjects = new HashMap(); + private final Map ancestorObjects = new HashMap(); private final Map ancestorColumnPrefix = new HashMap(); // multiple resultsets private final Map nextResultMaps = new HashMap(); private final Map> pendingRelations = new HashMap>(); + // Cached Automappings + private final Map> autoMappingsCache = new HashMap>(); + private static class PendingRelation { public MetaObject metaObject; public ResultMapping propertyMapping; } + private static class UnMappedColumAutoMapping { + private final String column; + private final String property; + private final TypeHandler typeHandler; + private final boolean primitive; + public UnMappedColumAutoMapping(String column, String property, TypeHandler typeHandler, boolean primitive) { + this.column = column; + this.property = property; + this.typeHandler = typeHandler; + this.primitive = primitive; + } + } + public DefaultResultSetHandler(Executor executor, MappedStatement mappedStatement, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql, RowBounds rowBounds) { this.executor = executor; @@ -127,6 +144,9 @@ public void handleOutputParameters(CallableStatement cs) throws SQLException { } private void handleRefCursorOutputParameter(ResultSet rs, ParameterMapping parameterMapping, MetaObject metaParam) throws SQLException { + if (rs == null) { + return; + } try { final String resultMapId = parameterMapping.getResultMapId(); final ResultMap resultMap = configuration.getResultMap(resultMapId); @@ -390,7 +410,7 @@ private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, && (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive()))) { metaObject.setValue(property, value); } - if (value != null || value == DEFERED) { + if (property != null && (value != null || value == DEFERED)) { foundValues = true; } } @@ -412,33 +432,49 @@ private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject } } - private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException { - final List unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix); - boolean foundValues = false; - for (String columnName : unmappedColumnNames) { - String propertyName = columnName; - if (columnPrefix != null && !columnPrefix.isEmpty()) { - // When columnPrefix is specified, - // ignore columns without the prefix. - if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) { - propertyName = columnName.substring(columnPrefix.length()); - } else { - continue; + private List createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException { + final String mapKey = resultMap.getId() + ":" + columnPrefix; + List autoMapping = autoMappingsCache.get(mapKey); + if (autoMapping == null) { + autoMapping = new ArrayList(); + final List unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix); + for (String columnName : unmappedColumnNames) { + String propertyName = columnName; + if (columnPrefix != null && !columnPrefix.isEmpty()) { + // When columnPrefix is specified, + // ignore columns without the prefix. + if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) { + propertyName = columnName.substring(columnPrefix.length()); + } else { + continue; + } + } + final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase()); + if (property != null && metaObject.hasSetter(property)) { + final Class propertyType = metaObject.getSetterType(property); + if (typeHandlerRegistry.hasTypeHandler(propertyType)) { + final TypeHandler typeHandler = rsw.getTypeHandler(propertyType, columnName); + autoMapping.add(new UnMappedColumAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive())); + } } } - final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase()); - if (property != null && metaObject.hasSetter(property)) { - final Class propertyType = metaObject.getSetterType(property); - if (typeHandlerRegistry.hasTypeHandler(propertyType)) { - final TypeHandler typeHandler = rsw.getTypeHandler(propertyType, columnName); - final Object value = typeHandler.getResult(rsw.getResultSet(), columnName); - // issue #377, call setter on nulls - if (value != null || configuration.isCallSettersOnNulls()) { - if (value != null || !propertyType.isPrimitive()) { - metaObject.setValue(property, value); - } - foundValues = true; + autoMappingsCache.put(mapKey, autoMapping); + } + return autoMapping; + } + + private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException { + List autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix); + boolean foundValues = false; + if (autoMapping.size() > 0) { + for (UnMappedColumAutoMapping mapping : autoMapping) { + final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column); + // issue #377, call setter on nulls + if (value != null || configuration.isCallSettersOnNulls()) { + if (value != null || !mapping.primitive) { + metaObject.setValue(mapping.property, value); } + foundValues = true; } } } @@ -752,9 +788,9 @@ private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap r nestedResultObjects.clear(); storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet()); } - rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, rowKey, null, partialObject); + rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject); } else { - rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, rowKey, null, partialObject); + rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject); if (partialObject == null) { storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet()); } @@ -769,14 +805,14 @@ private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap r // GET VALUE FROM ROW FOR NESTED RESULT MAP // - private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey combinedKey, CacheKey absoluteKey, String columnPrefix, Object partialObject) throws SQLException { + private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey combinedKey, String columnPrefix, Object partialObject) throws SQLException { final String resultMapId = resultMap.getId(); Object resultObject = partialObject; if (resultObject != null) { final MetaObject metaObject = configuration.newMetaObject(resultObject); - putAncestor(absoluteKey, resultObject, resultMapId, columnPrefix); + putAncestor(resultObject, resultMapId, columnPrefix); applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false); - ancestorObjects.remove(absoluteKey); + ancestorObjects.remove(resultMapId); } else { final ResultLoaderMap lazyLoader = new ResultLoaderMap(); resultObject = createResultObject(rsw, resultMap, lazyLoader, columnPrefix); @@ -787,9 +823,9 @@ private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey c foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues; } foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues; - putAncestor(absoluteKey, resultObject, resultMapId, columnPrefix); + putAncestor(resultObject, resultMapId, columnPrefix); foundValues = applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues; - ancestorObjects.remove(absoluteKey); + ancestorObjects.remove(resultMapId); foundValues = lazyLoader.size() > 0 || foundValues; resultObject = foundValues ? resultObject : null; } @@ -800,11 +836,11 @@ private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey c return resultObject; } - private void putAncestor(CacheKey rowKey, Object resultObject, String resultMapId, String columnPrefix) { + private void putAncestor(Object resultObject, String resultMapId, String columnPrefix) { if (!ancestorColumnPrefix.containsKey(resultMapId)) { ancestorColumnPrefix.put(resultMapId, columnPrefix); } - ancestorObjects.put(rowKey, resultObject); + ancestorObjects.put(resultMapId, resultObject); } // @@ -819,24 +855,19 @@ private boolean applyNestedResultMappings(ResultSetWrapper rsw, ResultMap result try { final String columnPrefix = getColumnPrefix(parentPrefix, resultMapping); final ResultMap nestedResultMap = getNestedResultMap(rsw.getResultSet(), nestedResultMapId, columnPrefix); - CacheKey rowKey = null; - Object ancestorObject = null; - if (ancestorColumnPrefix.containsKey(nestedResultMapId)) { - rowKey = createRowKey(nestedResultMap, rsw, ancestorColumnPrefix.get(nestedResultMapId)); - ancestorObject = ancestorObjects.get(rowKey); - } + Object ancestorObject = ancestorObjects.get(nestedResultMapId); if (ancestorObject != null) { if (newObject) { linkObjects(metaObject, resultMapping, ancestorObject); // issue #385 } } else { - rowKey = createRowKey(nestedResultMap, rsw, columnPrefix); + final CacheKey rowKey = createRowKey(nestedResultMap, rsw, columnPrefix); final CacheKey combinedKey = combineKeys(rowKey, parentRowKey); Object rowValue = nestedResultObjects.get(combinedKey); boolean knownValue = (rowValue != null); instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject); // mandatory if (anyNotNullColumnHasValue(resultMapping, columnPrefix, rsw.getResultSet())) { - rowValue = getRowValue(rsw, nestedResultMap, combinedKey, rowKey, columnPrefix, rowValue); + rowValue = getRowValue(rsw, nestedResultMap, combinedKey, columnPrefix, rowValue); if (rowValue != null && !knownValue) { linkObjects(metaObject, resultMapping, rowValue); foundValues = true; @@ -900,6 +931,9 @@ private CacheKey createRowKey(ResultMap resultMap, ResultSetWrapper rsw, String } else { createRowKeyForMappedProperties(resultMap, rsw, cacheKey, resultMappings, columnPrefix); } + if (cacheKey.getUpdateCount() < 2) { + return CacheKey.NULL_CACHE_KEY; + } return cacheKey; } @@ -1013,6 +1047,6 @@ private Object instantiateCollectionPropertyIfAppropriate(ResultMapping resultMa return propertyValue; } return null; - } - + } + } diff --git a/src/main/java/org/apache/ibatis/executor/statement/BaseStatementHandler.java b/src/main/java/org/apache/ibatis/executor/statement/BaseStatementHandler.java index b81a2b2a0..2858566ee 100644 --- a/src/main/java/org/apache/ibatis/executor/statement/BaseStatementHandler.java +++ b/src/main/java/org/apache/ibatis/executor/statement/BaseStatementHandler.java @@ -102,10 +102,12 @@ public Statement prepare(Connection connection) throws SQLException { protected void setStatementTimeout(Statement stmt) throws SQLException { Integer timeout = mappedStatement.getTimeout(); - Integer defaultTimeout = configuration.getDefaultStatementTimeout(); if (timeout != null) { stmt.setQueryTimeout(timeout); - } else if (defaultTimeout != null) { + return; + } + Integer defaultTimeout = configuration.getDefaultStatementTimeout(); + if (defaultTimeout != null) { stmt.setQueryTimeout(defaultTimeout); } } diff --git a/src/main/java/org/apache/ibatis/logging/jdbc/BaseJdbcLogger.java b/src/main/java/org/apache/ibatis/logging/jdbc/BaseJdbcLogger.java index 4f2b47d1b..dae045dc2 100644 --- a/src/main/java/org/apache/ibatis/logging/jdbc/BaseJdbcLogger.java +++ b/src/main/java/org/apache/ibatis/logging/jdbc/BaseJdbcLogger.java @@ -59,6 +59,7 @@ public BaseJdbcLogger(Log log, int queryStack) { static { SET_METHODS.add("setString"); + SET_METHODS.add("setNString"); SET_METHODS.add("setInt"); SET_METHODS.add("setByte"); SET_METHODS.add("setShort"); @@ -76,7 +77,9 @@ public BaseJdbcLogger(Log log, int queryStack) { SET_METHODS.add("setBoolean"); SET_METHODS.add("setBytes"); SET_METHODS.add("setCharacterStream"); + SET_METHODS.add("setNCharacterStream"); SET_METHODS.add("setClob"); + SET_METHODS.add("setNClob"); SET_METHODS.add("setObject"); SET_METHODS.add("setNull"); diff --git a/src/main/java/org/apache/ibatis/mapping/BoundSql.java b/src/main/java/org/apache/ibatis/mapping/BoundSql.java index c0bd094a3..aa45f6ac4 100644 --- a/src/main/java/org/apache/ibatis/mapping/BoundSql.java +++ b/src/main/java/org/apache/ibatis/mapping/BoundSql.java @@ -20,6 +20,7 @@ import java.util.Map; import org.apache.ibatis.reflection.MetaObject; +import org.apache.ibatis.reflection.property.PropertyTokenizer; import org.apache.ibatis.session.Configuration; /** @@ -62,7 +63,9 @@ public Object getParameterObject() { } public boolean hasAdditionalParameter(String name) { - return metaParameters.hasGetter(name); + PropertyTokenizer prop = new PropertyTokenizer(name); + String indexedName = prop.getIndexedName(); + return additionalParameters.containsKey(indexedName); } public void setAdditionalParameter(String name, Object value) { diff --git a/src/main/java/org/apache/ibatis/mapping/MappedStatement.java b/src/main/java/org/apache/ibatis/mapping/MappedStatement.java index c343e4a93..2b19b7530 100644 --- a/src/main/java/org/apache/ibatis/mapping/MappedStatement.java +++ b/src/main/java/org/apache/ibatis/mapping/MappedStatement.java @@ -71,7 +71,6 @@ public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlC mappedStatement.statementType = StatementType.PREPARED; mappedStatement.parameterMap = new ParameterMap.Builder(configuration, "defaultParameterMap", null, new ArrayList()).build(); mappedStatement.resultMaps = new ArrayList(); - mappedStatement.timeout = configuration.getDefaultStatementTimeout(); mappedStatement.sqlCommandType = sqlCommandType; mappedStatement.keyGenerator = configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType) ? new Jdbc3KeyGenerator() : new NoKeyGenerator(); String logId = id; diff --git a/src/main/java/org/apache/ibatis/parsing/GenericTokenParser.java b/src/main/java/org/apache/ibatis/parsing/GenericTokenParser.java index 09a948e7f..45103195d 100644 --- a/src/main/java/org/apache/ibatis/parsing/GenericTokenParser.java +++ b/src/main/java/org/apache/ibatis/parsing/GenericTokenParser.java @@ -31,26 +31,42 @@ public GenericTokenParser(String openToken, String closeToken, TokenHandler hand } public String parse(String text) { - StringBuilder builder = new StringBuilder(); + final StringBuilder builder = new StringBuilder(); + final StringBuilder expression = new StringBuilder(); if (text != null && text.length() > 0) { char[] src = text.toCharArray(); int offset = 0; + // search open token int start = text.indexOf(openToken, offset); while (start > -1) { if (start > 0 && src[start - 1] == '\\') { - // the variable is escaped. remove the backslash. + // this open token is escaped. remove the backslash and continue. builder.append(src, offset, start - offset - 1).append(openToken); offset = start + openToken.length(); } else { - int end = text.indexOf(closeToken, start); + // found open token. let's search close token. + expression.setLength(0); + builder.append(src, offset, start - offset); + offset = start + openToken.length(); + int end = text.indexOf(closeToken, offset); + while (end > -1) { + if (end > offset && src[end - 1] == '\\') { + // this close token is escaped. remove the backslash and continue. + expression.append(src, offset, end - offset - 1).append(closeToken); + offset = end + closeToken.length(); + end = text.indexOf(closeToken, offset); + } else { + expression.append(src, offset, end - offset); + offset = end + closeToken.length(); + break; + } + } if (end == -1) { - builder.append(src, offset, src.length - offset); + // close token was not found. + builder.append(src, start, src.length - start); offset = src.length; } else { - builder.append(src, offset, start - offset); - offset = start + openToken.length(); - String content = new String(src, offset, end - offset); - builder.append(handler.handleToken(content)); + builder.append(handler.handleToken(expression.toString())); offset = end + closeToken.length(); } } @@ -62,5 +78,4 @@ public String parse(String text) { } return builder.toString(); } - } diff --git a/src/main/java/org/apache/ibatis/session/Configuration.java b/src/main/java/org/apache/ibatis/session/Configuration.java index 57201d084..e6c63f356 100644 --- a/src/main/java/org/apache/ibatis/session/Configuration.java +++ b/src/main/java/org/apache/ibatis/session/Configuration.java @@ -53,6 +53,7 @@ import org.apache.ibatis.executor.resultset.ResultSetHandler; import org.apache.ibatis.executor.statement.RoutingStatementHandler; import org.apache.ibatis.executor.statement.StatementHandler; +import org.apache.ibatis.io.VFS; import org.apache.ibatis.logging.Log; import org.apache.ibatis.logging.LogFactory; import org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl; @@ -108,6 +109,7 @@ public class Configuration { protected String logPrefix; protected Class logImpl; + protected Class vfsImpl; protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION; protected JdbcType jdbcTypeForNull = JdbcType.OTHER; protected Set lazyLoadTriggerMethods = new HashSet(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" })); @@ -219,6 +221,18 @@ public void setLogImpl(Class logImpl) { } } + public Class getVfsImpl() { + return this.vfsImpl; + } + + @SuppressWarnings("unchecked") + public void setVfsImpl(Class vfsImpl) { + if (vfsImpl != null) { + this.vfsImpl = (Class) vfsImpl; + VFS.addImplClass(this.vfsImpl); + } + } + public boolean isCallSettersOnNulls() { return callSettersOnNulls; } diff --git a/src/main/java/org/apache/ibatis/type/NStringTypeHandler.java b/src/main/java/org/apache/ibatis/type/NStringTypeHandler.java index 52d59b40d..91c5b632d 100644 --- a/src/main/java/org/apache/ibatis/type/NStringTypeHandler.java +++ b/src/main/java/org/apache/ibatis/type/NStringTypeHandler.java @@ -28,28 +28,25 @@ public class NStringTypeHandler extends BaseTypeHandler { @Override public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException { -// ps.setNString(i, ((String) parameter)); - ps.setString(i, parameter); + ps.setNString(i, parameter); } @Override public String getNullableResult(ResultSet rs, String columnName) throws SQLException { -// return rs.getNString(columnName); - return rs.getString(columnName); + return rs.getNString(columnName); } @Override public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException { - return rs.getString(columnIndex); + return rs.getNString(columnIndex); } @Override public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { -// return cs.getNString(columnIndex); - return cs.getString(columnIndex); + return cs.getNString(columnIndex); } -} \ No newline at end of file +} diff --git a/src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java b/src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java index 57e5aaa79..776415cd3 100644 --- a/src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java +++ b/src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java @@ -29,6 +29,7 @@ import java.util.Set; import org.apache.ibatis.io.ResolverUtil; +import org.apache.ibatis.io.Resources; /** * @author Clinton Begin @@ -120,6 +121,19 @@ public TypeHandlerRegistry() { register(java.sql.Time.class, new SqlTimeTypeHandler()); register(java.sql.Timestamp.class, new SqlTimestampTypeHandler()); + // mybatis-typehandlers-jsr310 + try { + register("java.time.Instant", "org.apache.ibatis.type.InstantTypeHandler"); + register("java.time.LocalDateTime", "org.apache.ibatis.type.LocalDateTimeTypeHandler"); + register("java.time.LocalDate", "org.apache.ibatis.type.LocalDateTypeHandler"); + register("java.time.LocalTime", "org.apache.ibatis.type.LocalTimeTypeHandler"); + register("java.time.OffsetDateTime", "org.apache.ibatis.type.OffsetDateTimeTypeHandler"); + register("java.time.OffsetTime", "org.apache.ibatis.type.OffsetTimeTypeHandler"); + register("java.time.ZonedDateTime", "org.apache.ibatis.type.ZonedDateTimeTypeHandler"); + } catch (ClassNotFoundException e) { + // no JSR-310 handlers + } + // issue #273 register(Character.class, new CharacterTypeHandler()); register(char.class, new CharacterTypeHandler()); @@ -285,6 +299,10 @@ public void register(Class typeHandlerClass) { // java type + handler type + public void register(String javaTypeClassName, String typeHandlerClassName) throws ClassNotFoundException { + register(Resources.classForName(javaTypeClassName), Resources.classForName(typeHandlerClassName)); + } + public void register(Class javaTypeClass, Class typeHandlerClass) { register(javaTypeClass, getInstance(javaTypeClass, typeHandlerClass)); } diff --git a/src/site/es/xdoc/configuration.xml b/src/site/es/xdoc/configuration.xml index acb713ff5..2f14e2a4c 100644 --- a/src/site/es/xdoc/configuration.xml +++ b/src/site/es/xdoc/configuration.xml @@ -226,6 +226,21 @@ SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment, Sin valor (null) + + + defaultFetchSize + + + Sets the driver a hint as to control fetching size for return results. + This parameter value can be override by a query setting. + + + Cualquier entero positivo + + + Sin valor (null) + + safeRowBoundsEnabled @@ -367,7 +382,7 @@ SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment, CGLIB | JAVASSIST - CGLIB + JAVASSIST (MyBatis 3.3 or above) @@ -384,6 +399,7 @@ A continuación se muestra un ejemplo del elemento settings al completo: + diff --git a/src/site/es/xdoc/java-api.xml b/src/site/es/xdoc/java-api.xml index b4a916709..aaf8f4319 100644 --- a/src/site/es/xdoc/java-api.xml +++ b/src/site/es/xdoc/java-api.xml @@ -224,6 +224,10 @@ public interface ResultHandler {

El parámetro ResultContext te da acceso al objeto resultado en sí mismo, un contador del número de objetos creados y un método booleano stop() que te permite indicar a MyBatis que pare la carga de datos.

+
Batch update statement Flush Method
+

There is method for flushing(executing) batch update statements that stored in a JDBC driver class at any timing. This method can be used when you use the ExecutorType.BATCH as ExecutorType.

+ flushStatements()]]> +
Métodos de control de transacción

El parámetro ResultContext te da acceso al objeto resultado en sí mismo, un contador del número de objetos creados y un método booleano stop() que te permite indicar a MyBatis que pare la carga de datos.

void commit() @@ -463,6 +467,12 @@ try (SqlSession session = sqlSessionFactory.openSession()) { el valor de retorno será void y por tanto se requiere incluir esta anotación (o @ResultMap). La anotación se ignora si el tipo devuelto por el méotdo no es void. + + @Flush + Method + N/A + If this annotation is used, it can be called the SqlSession#flushStatements() via method defined at a Mapper interface.(MyBatis 3.3 or above) + @@ -476,6 +486,10 @@ try (SqlSession session = sqlSessionFactory.openSession()) { @Insert("insert into table2 (name) values(#{name})") @SelectKey(statement="call identity()", keyProperty="nameId", before=false, resultType=int.class) int insertTable2(Name name); + +

This example shows using the @Flush annotation to call the SqlSession#flushStatements():

+ flush();]]> diff --git a/src/site/ja/xdoc/configuration.xml b/src/site/ja/xdoc/configuration.xml index 1d467ec45..9c2da4cfb 100644 --- a/src/site/ja/xdoc/configuration.xml +++ b/src/site/ja/xdoc/configuration.xml @@ -257,6 +257,21 @@ SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment, なし (null) + + + defaultFetchSize + + + 検索結果のフェッチサイズを制御するためのドライバヒントを設定します。 + このパラメータ値はクエリ毎の設定で上書きできます。 + + + 正の整数 + + + なし (null) + + safeRowBoundsEnabled @@ -397,7 +412,7 @@ SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment, CGLIB | JAVASSIST - CGLIB + JAVASSIST (MyBatis 3.3 以上) @@ -414,6 +429,7 @@ SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment, + diff --git a/src/site/ja/xdoc/java-api.xml b/src/site/ja/xdoc/java-api.xml index d7cf28033..34577a413 100644 --- a/src/site/ja/xdoc/java-api.xml +++ b/src/site/ja/xdoc/java-api.xml @@ -232,6 +232,10 @@ public interface ResultHandler {
  • 複雑な ResultMap では複数行のデータがひとつのオブジェクトにマッピングされることもあります。こうした ResultMap を ResultHandler と併用する際、association や collection のデータがマッピングされる前の状態のオブジェクトが渡される場合があります。
  • +
    バッチ更新ステートメントをフラッシュするメソッド
    +

    バッチ更新用に JDBC ドライバ内に蓄積されたステートメントを任意のタイミングでデータベースへフラッシュ(実行)するメソッドがあります。このメソッドは、 ExecutorType として ExecutorType.BATCH を使用している場合に使用することができます。

    + flushStatements()]]> +
    トランザクションを制御するメソッド

    トランザクションのスコープを制御するメソッドは4つあります。当然ですが、auto-commit を使用する場合や、外部のトランザクションマネージャーを使っている場合、これらのメソッドは効果がありません。しかし、Connection のインスタンスによって管理されている JDBC トランザクションマネージャーを利用している場合は便利なメソッドです。

    void commit() @@ -481,6 +485,12 @@ try (SqlSession session = sqlSessionFactory.openSession()) { N/A ResultHandler を使うメソッドでは戻り値の型が void となるので、このアノテーションを使って各行のデータをどのクラスにマップするかを指定します。XMLの ResultMap が存在する場合は @ResultMap アノテーションで指定することができます。XML の <select> 要素で resultType が指定されている場合はアノテーションによる指定は不要です。それ以外の場合、例えば @Select アノテーションが付加された引数に ResultHandler を含むメソッドの場合は戻り値の型は void である必要があるので、このアノテーション(あるいは @ResultMap)を使って型を指定する必要があります。メソッドの戻り値の型が void 以外の場合、このアノテーションは無視されます。 + + @Flush + Method + N/A + このアノテーションを使用すると、SqlSession#flushStatements()メソッドを Mapper インタフェースに定義したメソッド経由で呼び出すことができます。(MyBatis 3.3以上) + @@ -494,6 +504,10 @@ try (SqlSession session = sqlSessionFactory.openSession()) { @Insert("insert into table2 (name) values(#{name})") @SelectKey(statement="call identity()", keyProperty="nameId", before=false, resultType=int.class) int insertTable2(Name name); + +

    次のコードは @Flush アノテーションを使って SqlSession#flushStatements()メソッドを呼び出す例です。

    + flush();]]> diff --git a/src/site/ko/xdoc/configuration.xml b/src/site/ko/xdoc/configuration.xml index a722d2d0d..0a9e57460 100644 --- a/src/site/ko/xdoc/configuration.xml +++ b/src/site/ko/xdoc/configuration.xml @@ -209,6 +209,21 @@ SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment, 셋팅되지 않음(null) + + + defaultFetchSize + + + Sets the driver a hint as to control fetching size for return results. + This parameter value can be override by a query setting. + + + 양수 + + + 셋팅되지 않음(null) + + safeRowBoundsEnabled @@ -352,7 +367,7 @@ SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment, CGLIB | JAVASSIST - CGLIB + JAVASSIST (MyBatis 3.3 or above) @@ -367,6 +382,7 @@ SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment, + diff --git a/src/site/ko/xdoc/java-api.xml b/src/site/ko/xdoc/java-api.xml index c1515cbf3..64e671326 100644 --- a/src/site/ko/xdoc/java-api.xml +++ b/src/site/ko/xdoc/java-api.xml @@ -271,6 +271,10 @@ public interface ResultHandler {

    ResultContext 파라미터는 결과 객체에 접근할 수 있도록 해준다.

    +
    Batch update statement Flush Method
    +

    There is method for flushing(executing) batch update statements that stored in a JDBC driver class at any timing. This method can be used when you use the ExecutorType.BATCH as ExecutorType.

    + flushStatements()]]> +
    트랙잭션 제어 메서드

    트랜잭션을 제어하기 위해 4 개의 메서드가 있다. 물론 자동커밋을 선택하였거나 외부 트랜잭션 관리자를 사용하면 영향이 없다. 어쨌든, Connection 인스턴스에 의해 관리되고 JDBC 트랜잭션 관리자를 사용하면, 이 4 개의 메서드를 사용할 수 @@ -622,7 +626,13 @@ id 를 제공하기 위해 사용된다. XML 에 정의된 결과 예를들어, @Select 애노테이션이 선언되어 있다면 메소드는 결과 핸들러를 사용할 것이다. 결과 타입은 void여야만 하고 이 애노테이션(이나 @ResultMap)을 반드시 사용해야 한다. 이 애노테이션은 메소드 리턴타입이 void가 아니라면 무시한다. - + + + @Flush + Method + N/A + If this annotation is used, it can be called the SqlSession#flushStatements() via method defined at a Mapper interface.(MyBatis 3.3 or above) + @@ -636,6 +646,10 @@ id 를 제공하기 위해 사용된다. XML 에 정의된 결과 @Insert("insert into table2 (name) values(#{name})") @SelectKey(statement="call identity()", keyProperty="nameId", before=false, resultType=int.class) int insertTable2(Name name); + +

    This example shows using the @Flush annotation to call the SqlSession#flushStatements():

    + flush();]]> diff --git a/src/site/xdoc/configuration.xml b/src/site/xdoc/configuration.xml index 416b19767..36bd57946 100644 --- a/src/site/xdoc/configuration.xml +++ b/src/site/xdoc/configuration.xml @@ -315,6 +315,21 @@ SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment, Not Set (null) + + + defaultFetchSize + + + Sets the driver a hint as to control fetching size for return results. + This parameter value can be override by a query setting. + + + Any positive integer + + + Not Set (null) + + safeRowBoundsEnabled @@ -459,7 +474,7 @@ SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment, CGLIB | JAVASSIST - CGLIB + JAVASSIST (MyBatis 3.3 or above) @@ -476,6 +491,7 @@ SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment, + diff --git a/src/site/xdoc/java-api.xml b/src/site/xdoc/java-api.xml index 0a1d62c18..26e190ac0 100644 --- a/src/site/xdoc/java-api.xml +++ b/src/site/xdoc/java-api.xml @@ -237,6 +237,10 @@ public interface ResultHandler {
  • When using advanced resultmaps MyBatis will probably require several rows to build an object. If a ResultHandler is used you may be given an object whose associations or collections are not yet filled.
  • +
    Batch update statement Flush Method
    +

    There is method for flushing(executing) batch update statements that stored in a JDBC driver class at any timing. This method can be used when you use the ExecutorType.BATCH as ExecutorType.

    + flushStatements()]]> +
    Transaction Control Methods

    There are four methods for controlling the scope of a transaction. Of course, these have no effect if you've chosen to use auto-commit or if you're using an external transaction manager. However, if you're using the JDBC transaction manager, managed by the Connection instance, then the four methods that will come in handy are:

    void commit() @@ -530,6 +534,12 @@ try (SqlSession session = sqlSessionFactory.openSession()) { will use a result handler, the return type must be void and this annotation (or @ResultMap) is required. This annotation is ignored unless the method return type is void. + + @Flush + Method + N/A + If this annotation is used, it can be called the SqlSession#flushStatements() via method defined at a Mapper interface.(MyBatis 3.3 or above) + @@ -543,6 +553,10 @@ try (SqlSession session = sqlSessionFactory.openSession()) { @Insert("insert into table2 (name) values(#{name})") @SelectKey(statement="call identity()", keyProperty="nameId", before=false, resultType=int.class) int insertTable2(Name name); + +

    This example shows using the @Flush annotation to call the SqlSession#flushStatements():

    + flush();]]> diff --git a/src/site/zh/xdoc/configuration.xml b/src/site/zh/xdoc/configuration.xml index 048ee55d8..bba32de2d 100644 --- a/src/site/zh/xdoc/configuration.xml +++ b/src/site/zh/xdoc/configuration.xml @@ -238,6 +238,21 @@ SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment, Not Set (null) + + + defaultFetchSize + + + Sets the driver a hint as to control fetching size for return results. + This parameter value can be override by a query setting. + + + Any positive integer + + + Not Set (null) + + safeRowBoundsEnabled @@ -378,7 +393,7 @@ SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment, CGLIB | JAVASSIST - CGLIB + JAVASSIST (MyBatis 3.3 or above) @@ -395,6 +410,7 @@ SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment, + diff --git a/src/site/zh/xdoc/java-api.xml b/src/site/zh/xdoc/java-api.xml index d206db072..633c63204 100644 --- a/src/site/zh/xdoc/java-api.xml +++ b/src/site/zh/xdoc/java-api.xml @@ -364,6 +364,11 @@ public interface ResultHandler { 尔返回值的 stop()方法来停止 MyBatis 加载更多的结果。

    +
    Batch update statement Flush Method
    +

    There is method for flushing(executing) batch update statements that stored in a JDBC driver class at any timing. This method can be used when you use the ExecutorType.BATCH as ExecutorType.

    + flushStatements()]]> + +
    事务控制方法

    控制事务范围有四个方法。 @@ -800,6 +805,12 @@ type,method。type 属性是类的完全限 will use a result handler, the return type must be void and this annotation (or @ResultMap) is required. This annotation is ignored unless the method return type is void. + + @Flush + Method + N/A + If this annotation is used, it can be called the SqlSession#flushStatements() via method defined at a Mapper interface.(MyBatis 3.3 or above) + @@ -813,6 +824,10 @@ type,method。type 属性是类的完全限 @Insert("insert into table2 (name) values(#{name})") @SelectKey(statement="call identity()", keyProperty="nameId", before=false, resultType=int.class) int insertTable2(Name name); + +

    This example shows using the @Flush annotation to call the SqlSession#flushStatements():

    + flush();]]> diff --git a/src/test/java/org/apache/ibatis/builder/CustomizedSettingsMapperConfig.xml b/src/test/java/org/apache/ibatis/builder/CustomizedSettingsMapperConfig.xml index 539471226..9467e18aa 100644 --- a/src/test/java/org/apache/ibatis/builder/CustomizedSettingsMapperConfig.xml +++ b/src/test/java/org/apache/ibatis/builder/CustomizedSettingsMapperConfig.xml @@ -23,6 +23,10 @@ + + + + @@ -38,13 +42,14 @@ - + + diff --git a/src/test/java/org/apache/ibatis/builder/XmlConfigBuilderTest.java b/src/test/java/org/apache/ibatis/builder/XmlConfigBuilderTest.java index d6aefdf52..18c7d3eaa 100644 --- a/src/test/java/org/apache/ibatis/builder/XmlConfigBuilderTest.java +++ b/src/test/java/org/apache/ibatis/builder/XmlConfigBuilderTest.java @@ -28,6 +28,7 @@ import org.apache.ibatis.builder.xml.XMLConfigBuilder; import org.apache.ibatis.executor.loader.cglib.CglibProxyFactory; import org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory; +import org.apache.ibatis.io.JBoss6VFS; import org.apache.ibatis.io.Resources; import org.apache.ibatis.logging.slf4j.Slf4jImpl; import org.apache.ibatis.scripting.defaults.RawLanguageDriver; @@ -164,6 +165,7 @@ public void shouldSuccessfullyLoadXMLConfigFile() throws Exception { assertThat(config.isCallSettersOnNulls(), is(true)); assertThat(config.getLogPrefix(), is("mybatis_")); assertThat(config.getLogImpl().getName(), is(Slf4jImpl.class.getName())); + assertThat(config.getVfsImpl().getName(), is(JBoss6VFS.class.getName())); assertThat(config.getConfigurationFactory().getName(), is(String.class.getName())); } diff --git a/src/test/java/org/apache/ibatis/builder/xml/dynamic/DynamicSqlSourceTest.java b/src/test/java/org/apache/ibatis/builder/xml/dynamic/DynamicSqlSourceTest.java index bdba78579..07b9bad68 100644 --- a/src/test/java/org/apache/ibatis/builder/xml/dynamic/DynamicSqlSourceTest.java +++ b/src/test/java/org/apache/ibatis/builder/xml/dynamic/DynamicSqlSourceTest.java @@ -317,6 +317,17 @@ public void shouldIterateOnceForEachItemInCollection() throws Exception { assertEquals("__frch_item_2", boundSql.getParameterMappings().get(2).getProperty()); } + @Test + public void shouldHandleOgnlExpression() throws Exception { + final HashMap parameterObject = new HashMap() {{ + put("name", "Steve"); + }}; + final String expected = "Expression test: 3 / yes."; + DynamicSqlSource source = createDynamicSqlSource(new TextSqlNode("Expression test: ${name.indexOf('v')} / ${name in {'Bob', 'Steve'\\} ? 'yes' : 'no'}.")); + BoundSql boundSql = source.getBoundSql(parameterObject); + assertEquals(expected, boundSql.getSql()); + } + @Test public void shouldSkipForEachWhenCollectionIsEmpty() throws Exception { final HashMap parameterObject = new HashMap() {{ diff --git a/src/test/java/org/apache/ibatis/executor/ExecutorTestHelper.java b/src/test/java/org/apache/ibatis/executor/ExecutorTestHelper.java index b40a6609c..baadf3a22 100644 --- a/src/test/java/org/apache/ibatis/executor/ExecutorTestHelper.java +++ b/src/test/java/org/apache/ibatis/executor/ExecutorTestHelper.java @@ -197,7 +197,7 @@ public static MappedStatement prepareSelectAllAuthorsAutoMappedStatement(final C } }).build()); } - }).fetchSize(1000).build(); + }).fetchSize(1000).timeout(2000).build(); } public static MappedStatement prepareSelectOneAuthorMappedStatementWithConstructorResults(final Configuration config) { diff --git a/src/test/java/org/apache/ibatis/mapping/BoundSqlTest.java b/src/test/java/org/apache/ibatis/mapping/BoundSqlTest.java new file mode 100644 index 000000000..439fbbf94 --- /dev/null +++ b/src/test/java/org/apache/ibatis/mapping/BoundSqlTest.java @@ -0,0 +1,59 @@ +/** + * Copyright 2009-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.mapping; + +import static org.junit.Assert.*; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.ibatis.session.Configuration; +import org.junit.Test; + +public class BoundSqlTest { + + @Test + public void testHasAdditionalParameter() throws Exception { + List params = Collections.emptyList(); + BoundSql boundSql = new BoundSql(new Configuration(), "some sql", params, new Object()); + + Map map = new HashMap(); + map.put("key1", "value1"); + boundSql.setAdditionalParameter("map", map); + + Person bean = new Person(); + bean.id = 1; + boundSql.setAdditionalParameter("person", bean); + + assertFalse(boundSql.hasAdditionalParameter("pet")); + assertFalse(boundSql.hasAdditionalParameter("pet.name")); + + assertTrue(boundSql.hasAdditionalParameter("map")); + assertTrue(boundSql.hasAdditionalParameter("map.key1")); + assertTrue("should return true even if the child property does not exists.", boundSql.hasAdditionalParameter("map.key2")); + + assertTrue(boundSql.hasAdditionalParameter("person")); + assertTrue(boundSql.hasAdditionalParameter("person.id")); + assertTrue("should return true even if the child property does not exists.", boundSql.hasAdditionalParameter("person.name")); + } + + public static class Person { + public Integer id; + } + +} diff --git a/src/test/java/org/apache/ibatis/parsing/GenericTokenParserTest.java b/src/test/java/org/apache/ibatis/parsing/GenericTokenParserTest.java index a81f510ea..404062bd9 100644 --- a/src/test/java/org/apache/ibatis/parsing/GenericTokenParserTest.java +++ b/src/test/java/org/apache/ibatis/parsing/GenericTokenParserTest.java @@ -42,6 +42,7 @@ public void shouldDemonstrateGenericTokenReplacement() { put("first_name", "James"); put("initial", "T"); put("last_name", "Kirk"); + put("var{with}brace", "Hiya"); put("", ""); } })); @@ -61,6 +62,9 @@ public void shouldDemonstrateGenericTokenReplacement() { assertEquals("{$$something}JamesTKirk", parser.parse("{$$something}${first_name}${initial}${last_name}")); assertEquals("${", parser.parse("${")); + assertEquals("${\\}", parser.parse("${\\}")); + assertEquals("Hiya", parser.parse("${var{with\\}brace}")); + assertEquals("", parser.parse("${}")); assertEquals("}", parser.parse("}")); assertEquals("Hello ${ this is a test.", parser.parse("Hello ${ this is a test.")); assertEquals("Hello } this is a test.", parser.parse("Hello } this is a test.")); diff --git a/src/test/java/org/apache/ibatis/submitted/emptycollection/Dao.java b/src/test/java/org/apache/ibatis/submitted/emptycollection/Dao.java new file mode 100644 index 000000000..f7aa7fd83 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/emptycollection/Dao.java @@ -0,0 +1,24 @@ +/** + * Copyright 2009-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.submitted.emptycollection; + +import java.util.List; + +interface Dao { + List selectWithEmptyList(); + List selectWithNonEmptyList(); + List selectWithNonEmptyList_noCollectionId(); +} diff --git a/src/test/java/org/apache/ibatis/submitted/emptycollection/Dao.xml b/src/test/java/org/apache/ibatis/submitted/emptycollection/Dao.xml new file mode 100644 index 000000000..d3c8766d6 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/emptycollection/Dao.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/org/apache/ibatis/submitted/emptycollection/DaoTest.java b/src/test/java/org/apache/ibatis/submitted/emptycollection/DaoTest.java new file mode 100644 index 000000000..e42c823f6 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/emptycollection/DaoTest.java @@ -0,0 +1,93 @@ +/** + * Copyright 2009-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.submitted.emptycollection; + +import java.io.Reader; +import java.sql.Connection; +import java.util.List; + +import org.apache.ibatis.io.Resources; +import org.apache.ibatis.jdbc.ScriptRunner; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.apache.ibatis.session.SqlSessionFactoryBuilder; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class DaoTest { + private Dao dao; + private SqlSession sqlSession; + + @Before + public void setUp() throws Exception { + Reader reader = Resources.getResourceAsReader("org/apache/ibatis/submitted/emptycollection/mybatis-config.xml"); + SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); + reader.close(); + + sqlSession = sqlSessionFactory.openSession(); + Connection conn = sqlSession.getConnection(); + ScriptRunner runner = new ScriptRunner(conn); + runner.setLogWriter(null); + dao = sqlSession.getMapper(Dao.class); + } + + @After + public void tearDown() throws Exception { + sqlSession.close(); + } + + @Test + public void testWithEmptyList() throws Exception { + final List actual = dao.selectWithEmptyList(); + Assert.assertEquals(1, actual.size()); + final List todoItems = actual.get(0).getTodoItems(); + Assert.assertEquals("expect " + todoItems + " to be empty", 0, todoItems.size()); + } + + @Test + public void testWithNonEmptyList() throws Exception { + final List actual = dao.selectWithNonEmptyList(); + checkNonEmptyList(actual); + } + + @Test + public void testWithNonEmptyList_noCollectionId() throws Exception { + final List actual = dao.selectWithNonEmptyList_noCollectionId(); + + checkNonEmptyList(actual); + } + + private void checkNonEmptyList(final List actual) { +// Assert.assertEquals("[List(1)=[a description(1), a 2nd description(2)], List(2)=[a description(1)]]", actual.toString()); + Assert.assertEquals(2, actual.size()); + + Assert.assertEquals(2, actual.get(0).getTodoItems().size()); + Assert.assertEquals(1, actual.get(0).getTodoItems().get(0).getOrder()); + Assert.assertEquals("a description", actual.get(0).getTodoItems().get(0).getDescription().trim()); + Assert.assertEquals(2, actual.get(0).getTodoItems().get(1).getOrder()); + Assert.assertEquals("a 2nd description", actual.get(0).getTodoItems().get(1).getDescription().trim()); + + Assert.assertEquals(1, actual.get(1).getTodoItems().size()); + Assert.assertEquals(1, actual.get(1).getTodoItems().get(0).getOrder()); + Assert.assertEquals("a description", actual.get(0).getTodoItems().get(0).getDescription().trim()); + + // We should have gotten three item objects. The first item from the first list and the first item from + // the second list have identical properties, but they should be distinct objects + Assert.assertNotSame(actual.get(0).getTodoItems().get(0), actual.get(1).getTodoItems().get(0)); + } +} \ No newline at end of file diff --git a/src/test/java/org/apache/ibatis/submitted/emptycollection/TodoItem.java b/src/test/java/org/apache/ibatis/submitted/emptycollection/TodoItem.java new file mode 100644 index 000000000..71a30351a --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/emptycollection/TodoItem.java @@ -0,0 +1,44 @@ +/** + * Copyright 2009-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.submitted.emptycollection; + +public class TodoItem { + + @Override + public String toString() { + return "TodoItem [order=" + order + ", description=" + description + "]"; + } + + private int order; + private String description; + + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + +} diff --git a/src/test/java/org/apache/ibatis/submitted/emptycollection/TodoLists.java b/src/test/java/org/apache/ibatis/submitted/emptycollection/TodoLists.java new file mode 100644 index 000000000..b2e0b1b4c --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/emptycollection/TodoLists.java @@ -0,0 +1,47 @@ +/** + * Copyright 2009-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.submitted.emptycollection; + +import java.util.List; + +public class TodoLists { + + @Override + public String toString() { + return "TodoLists [id=" + id + ", todoItems=" + todoItems + "]"; + } + + private int id; + + private List todoItems; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public List getTodoItems() { + return todoItems; + } + + public void setTodoItems(List todoItems) { + this.todoItems = todoItems; + } + +} diff --git a/src/test/java/org/apache/ibatis/submitted/emptycollection/mybatis-config.xml b/src/test/java/org/apache/ibatis/submitted/emptycollection/mybatis-config.xml new file mode 100644 index 000000000..871a4454e --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/emptycollection/mybatis-config.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/org/apache/ibatis/submitted/foreach/ForEachTest.java b/src/test/java/org/apache/ibatis/submitted/foreach/ForEachTest.java index 2ed0bf31e..1a8548335 100644 --- a/src/test/java/org/apache/ibatis/submitted/foreach/ForEachTest.java +++ b/src/test/java/org/apache/ibatis/submitted/foreach/ForEachTest.java @@ -18,8 +18,10 @@ import java.io.Reader; import java.sql.Connection; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import org.apache.ibatis.exceptions.PersistenceException; import org.apache.ibatis.io.Resources; import org.apache.ibatis.jdbc.ScriptRunner; import org.apache.ibatis.session.SqlSession; @@ -27,12 +29,17 @@ import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Assert; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; public class ForEachTest { private static SqlSessionFactory sqlSessionFactory; + @Rule + public ExpectedException ex = ExpectedException.none(); + @BeforeClass public static void setUp() throws Exception { // create a SqlSessionFactory @@ -124,4 +131,18 @@ public void nullItemInContext() { } } + @Test + public void shouldReportMissingPropertyName() { + ex.expect(PersistenceException.class); + ex.expectMessage("There is no getter for property named 'idd' in 'class org.apache.ibatis.submitted.foreach.User'"); + + SqlSession sqlSession = sqlSessionFactory.openSession(); + try { + Mapper mapper = sqlSession.getMapper(Mapper.class); + mapper.typoInItemProperty(Arrays.asList(new User())); + } finally { + sqlSession.close(); + } + } + } diff --git a/src/test/java/org/apache/ibatis/submitted/foreach/Mapper.java b/src/test/java/org/apache/ibatis/submitted/foreach/Mapper.java index d8a1037a4..cca0bdf30 100644 --- a/src/test/java/org/apache/ibatis/submitted/foreach/Mapper.java +++ b/src/test/java/org/apache/ibatis/submitted/foreach/Mapper.java @@ -27,4 +27,5 @@ public interface Mapper { String selectWithNullItemCheck(List users); + int typoInItemProperty(List users); } diff --git a/src/test/java/org/apache/ibatis/submitted/foreach/Mapper.xml b/src/test/java/org/apache/ibatis/submitted/foreach/Mapper.xml index 0404b85ff..63d133833 100644 --- a/src/test/java/org/apache/ibatis/submitted/foreach/Mapper.xml +++ b/src/test/java/org/apache/ibatis/submitted/foreach/Mapper.xml @@ -62,4 +62,11 @@ + + insert into users (id, name) values + + (#{item.idd}, #{item.name}) + + + diff --git a/src/test/java/org/apache/ibatis/submitted/keygen/Country.java b/src/test/java/org/apache/ibatis/submitted/keygen/Country.java new file mode 100644 index 000000000..dbcc73cdc --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/keygen/Country.java @@ -0,0 +1,63 @@ +/** + * Copyright 2009-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.submitted.keygen; + +/** + * @author liuzh + */ +public class Country { + private Integer id; + private String countryname; + private String countrycode; + + public Country() { + } + + public Country(String countryname, String countrycode) { + this.countryname = countryname; + this.countrycode = countrycode; + } + + public Country(Integer id, String countryname, String countrycode) { + this.id = id; + this.countryname = countryname; + this.countrycode = countrycode; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getCountryname() { + return countryname; + } + + public void setCountryname(String countryname) { + this.countryname = countryname; + } + + public String getCountrycode() { + return countrycode; + } + + public void setCountrycode(String countrycode) { + this.countrycode = countrycode; + } +} diff --git a/src/test/java/org/apache/ibatis/submitted/keygen/CountryMapper.java b/src/test/java/org/apache/ibatis/submitted/keygen/CountryMapper.java new file mode 100644 index 000000000..325ffd6d8 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/keygen/CountryMapper.java @@ -0,0 +1,24 @@ +/** + * Copyright 2009-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.submitted.keygen; + +import java.util.List; + +public interface CountryMapper { + + int insertList(List countries); + +} diff --git a/src/test/java/org/apache/ibatis/submitted/keygen/CountryMapper.xml b/src/test/java/org/apache/ibatis/submitted/keygen/CountryMapper.xml new file mode 100644 index 000000000..e201dce49 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/keygen/CountryMapper.xml @@ -0,0 +1,28 @@ + + + + + + insert into country (countryname,countrycode) + values + + (#{country.countryname},#{country.countrycode}) + + + diff --git a/src/test/java/org/apache/ibatis/submitted/keygen/CreateDB.sql b/src/test/java/org/apache/ibatis/submitted/keygen/CreateDB.sql new file mode 100644 index 000000000..55a4dd6b4 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/keygen/CreateDB.sql @@ -0,0 +1,23 @@ +-- +-- Copyright 2009-2016 the original author or authors. +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +DROP TABLE country IF EXISTS; + +CREATE TABLE country ( + Id int IDENTITY, + countryname varchar(255) DEFAULT NULL, + countrycode varchar(255) DEFAULT NULL, +); diff --git a/src/test/java/org/apache/ibatis/submitted/keygen/Jdbc3KeyGeneratorTest.java b/src/test/java/org/apache/ibatis/submitted/keygen/Jdbc3KeyGeneratorTest.java new file mode 100644 index 000000000..2f3fbd326 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/keygen/Jdbc3KeyGeneratorTest.java @@ -0,0 +1,76 @@ +/** + * Copyright 2009-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.submitted.keygen; + +import static org.junit.Assert.*; + +import java.io.Reader; +import java.sql.Connection; +import java.util.ArrayList; +import java.util.List; + +import org.apache.ibatis.io.Resources; +import org.apache.ibatis.jdbc.ScriptRunner; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.apache.ibatis.session.SqlSessionFactoryBuilder; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * @author liuzh + */ +public class Jdbc3KeyGeneratorTest { + + private static SqlSessionFactory sqlSessionFactory; + + @BeforeClass + public static void setUp() throws Exception { + // create an SqlSessionFactory + Reader reader = Resources.getResourceAsReader("org/apache/ibatis/submitted/keygen/MapperConfig.xml"); + sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); + reader.close(); + + // populate in-memory database + SqlSession session = sqlSessionFactory.openSession(); + Connection conn = session.getConnection(); + reader = Resources.getResourceAsReader("org/apache/ibatis/submitted/keygen/CreateDB.sql"); + ScriptRunner runner = new ScriptRunner(conn); + runner.setLogWriter(null); + runner.runScript(reader); + reader.close(); + session.close(); + } + + @Test + public void shouldInsertListAndRetrieveId() throws Exception { + SqlSession sqlSession = sqlSessionFactory.openSession(); + try { + CountryMapper mapper = sqlSession.getMapper(CountryMapper.class); + List countries = new ArrayList(); + countries.add(new Country("China", "CN")); + countries.add(new Country("United Kiongdom", "GB")); + countries.add(new Country("United States of America", "US")); + mapper.insertList(countries); + for (Country country : countries) { + assertNotNull(country.getId()); + } + } finally { + sqlSession.rollback(); + sqlSession.close(); + } + } +} diff --git a/src/test/java/org/apache/ibatis/submitted/keygen/MapperConfig.xml b/src/test/java/org/apache/ibatis/submitted/keygen/MapperConfig.xml new file mode 100644 index 000000000..2425f5801 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/keygen/MapperConfig.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler/Item.java b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler/Item.java index 70ca2e558..6a3b1d11e 100644 --- a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler/Item.java +++ b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler/Item.java @@ -19,6 +19,16 @@ public class Item { private Integer id; private String name; + public String toString(){ + return new StringBuilder() + .append("Item(") + .append(id) + .append(", ") + .append(name) + .append(" )") + .toString(); + } + public Integer getId() { return id; } diff --git a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler/Mapper.java b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler/Mapper.java index b786e03ff..fab97238f 100644 --- a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler/Mapper.java +++ b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler/Mapper.java @@ -20,4 +20,5 @@ public interface Mapper { List getPersons(); List getPersonsWithItemsOrdered(); + List getPersonItemPairs(); } diff --git a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler/Mapper.xml b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler/Mapper.xml index 380179c44..b5eb012ee 100644 --- a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler/Mapper.xml +++ b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler/Mapper.xml @@ -40,4 +40,23 @@ where p.id = i.owner order by i.name + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler/NestedResultHandlerTest.java b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler/NestedResultHandlerTest.java index c93e9eca0..0dbef4780 100644 --- a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler/NestedResultHandlerTest.java +++ b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler/NestedResultHandlerTest.java @@ -150,4 +150,24 @@ public void testGetPersonOrderedByItem() { } } + @Test //reopen issue 39? (not a bug?) + public void testGetPersonItemPairs(){ + SqlSession sqlSession = sqlSessionFactory.openSession(); + try{ + Mapper mapper = sqlSession.getMapper(Mapper.class); + List pairs = mapper.getPersonItemPairs(); + + Assert.assertNotNull( pairs ); +// System.out.println( new StringBuilder().append("selected pairs: ").append(pairs) ); + + Assert.assertEquals(5, pairs.size() ); + Assert.assertNotNull(pairs.get(0).getPerson()); + Assert.assertEquals(pairs.get(0).getPerson().getId(), Integer.valueOf(1)); + Assert.assertNotNull(pairs.get(0).getItem()); + Assert.assertEquals( pairs.get(0).getItem().getId(), Integer.valueOf(1)); + } finally{ + sqlSession.close(); + } + } + } diff --git a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler/Person.java b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler/Person.java index d76b66cf3..8e5ad3f61 100644 --- a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler/Person.java +++ b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler/Person.java @@ -22,7 +22,19 @@ public class Person { private Integer id; private String name; - private List items=new ArrayList(); + private List items=new ArrayList(); + + public String toString(){ + return new StringBuilder() + .append("Person(") + .append(id) + .append(", ") + .append(name) + .append(", ") + .append(items) + .append(" )") + .toString(); + } public Integer getId() { return id; diff --git a/src/test/java/org/apache/ibatis/submitted/nestedresulthandler/PersonItemPair.java b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler/PersonItemPair.java new file mode 100644 index 000000000..60f53a195 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/nestedresulthandler/PersonItemPair.java @@ -0,0 +1,50 @@ +/** + * Copyright 2009-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.submitted.nestedresulthandler; + +/** + * Created by eyal on 12/9/2015. + */ +public class PersonItemPair { + private Person person; + private Item item; + + public String toString(){ + return new StringBuilder() + .append("PersonItemPair(") + .append(person) + .append(", ") + .append(item) + .append(" )") + .toString(); + } + + public Person getPerson() { + return person; + } + + public void setPerson(Person person) { + this.person = person; + } + + public Item getItem() { + return item; + } + + public void setItem(Item item) { + this.item = item; + } +} diff --git a/src/test/java/org/apache/ibatis/submitted/results_id/AnotherMapper.java b/src/test/java/org/apache/ibatis/submitted/results_id/AnotherMapper.java new file mode 100644 index 000000000..bde4b6223 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/results_id/AnotherMapper.java @@ -0,0 +1,32 @@ +/** + * Copyright 2009-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ibatis.submitted.results_id; + +import java.util.List; + +import org.apache.ibatis.annotations.ResultMap; +import org.apache.ibatis.annotations.Select; + +public interface AnotherMapper { + + @ResultMap("org.apache.ibatis.submitted.results_id.Mapper.userResult") + @Select("select * from users order by uid") + List getUsers(); + + User getUser(Integer id); + +} diff --git a/src/test/java/org/apache/ibatis/submitted/results_id/AnotherMapper.xml b/src/test/java/org/apache/ibatis/submitted/results_id/AnotherMapper.xml new file mode 100644 index 000000000..84e23a364 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/results_id/AnotherMapper.xml @@ -0,0 +1,25 @@ + + + + + + + diff --git a/src/test/java/org/apache/ibatis/submitted/results_id/CreateDB.sql b/src/test/java/org/apache/ibatis/submitted/results_id/CreateDB.sql new file mode 100644 index 000000000..468b8a759 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/results_id/CreateDB.sql @@ -0,0 +1,25 @@ +-- +-- Copyright 2009-2015 the original author or authors. +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +drop table users if exists; + +create table users ( + uid int, + name varchar(20) +); + +insert into users (uid, name) values(1, 'User1'); +insert into users (uid, name) values(2, 'User2'); diff --git a/src/test/java/org/apache/ibatis/submitted/results_id/IdConflictMapper.java b/src/test/java/org/apache/ibatis/submitted/results_id/IdConflictMapper.java new file mode 100644 index 000000000..3ebb57965 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/results_id/IdConflictMapper.java @@ -0,0 +1,27 @@ +/** + * Copyright 2009-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.submitted.results_id; + +import org.apache.ibatis.annotations.Result; +import org.apache.ibatis.annotations.Results; +import org.apache.ibatis.annotations.Select; + +public interface IdConflictMapper { + + @Results(id = "userResult", value = { @Result(id = true, column = "uid", property = "id") }) + @Select("select * from users where uid = #{id}") + User getUserById(Integer id); +} diff --git a/src/test/java/org/apache/ibatis/submitted/results_id/IdConflictMapper.xml b/src/test/java/org/apache/ibatis/submitted/results_id/IdConflictMapper.xml new file mode 100644 index 000000000..8d312121b --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/results_id/IdConflictMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + diff --git a/src/test/java/org/apache/ibatis/submitted/results_id/IdConflictTest.java b/src/test/java/org/apache/ibatis/submitted/results_id/IdConflictTest.java new file mode 100644 index 000000000..a1a7f03fd --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/results_id/IdConflictTest.java @@ -0,0 +1,38 @@ +/** + * Copyright 2009-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.submitted.results_id; + +import org.apache.ibatis.session.Configuration; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class IdConflictTest { + + @Rule + public ExpectedException ex = ExpectedException.none(); + + @Test + public void shouldFailOnDuplicatedId() throws Exception { + ex.expect(RuntimeException.class); + ex.expectMessage("Result Maps collection already contains value for org.apache.ibatis.submitted.results_id.IdConflictMapper.userResult"); + + Configuration configuration = new Configuration(); + configuration.addMapper(IdConflictMapper.class); + configuration.getMappedStatements(); + } + +} diff --git a/src/test/java/org/apache/ibatis/submitted/results_id/Mapper.java b/src/test/java/org/apache/ibatis/submitted/results_id/Mapper.java new file mode 100644 index 000000000..1c593344e --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/results_id/Mapper.java @@ -0,0 +1,49 @@ +/** + * Copyright 2009-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.submitted.results_id; + +import org.apache.ibatis.annotations.Arg; +import org.apache.ibatis.annotations.ConstructorArgs; +import org.apache.ibatis.annotations.Result; +import org.apache.ibatis.annotations.ResultMap; +import org.apache.ibatis.annotations.Results; +import org.apache.ibatis.annotations.Select; + +public interface Mapper { + + @Results(id = "userResult", value = { + @Result(id = true, column = "uid", property = "id"), + @Result(column = "name", property = "name") + }) + @Select("select * from users where uid = #{id}") + User getUserById(Integer id); + + @ResultMap("userResult") + @Select("select * from users where name = #{name}") + User getUserByName(String name); + + @Results(id = "userResultConstructor") + @ConstructorArgs({ + @Arg(id = true, column = "uid", javaType = Integer.class), + @Arg(column = "name", javaType = String.class) + }) + @Select("select * from users where uid = #{id}") + User getUserByIdConstructor(Integer id); + + @ResultMap("userResultConstructor") + @Select("select * from users where name = #{name}") + User getUserByNameConstructor(String name); +} diff --git a/src/test/java/org/apache/ibatis/submitted/results_id/ResultsIdTest.java b/src/test/java/org/apache/ibatis/submitted/results_id/ResultsIdTest.java new file mode 100644 index 000000000..99621ea1f --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/results_id/ResultsIdTest.java @@ -0,0 +1,108 @@ +/** + * Copyright 2009-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.submitted.results_id; + +import static org.junit.Assert.*; + +import java.io.Reader; +import java.sql.Connection; +import java.util.List; + +import org.apache.ibatis.io.Resources; +import org.apache.ibatis.jdbc.ScriptRunner; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.apache.ibatis.session.SqlSessionFactoryBuilder; +import org.junit.BeforeClass; +import org.junit.Test; + +public class ResultsIdTest { + + private static SqlSessionFactory sqlSessionFactory; + + @BeforeClass + public static void setUp() throws Exception { + // create an SqlSessionFactory + Reader reader = Resources.getResourceAsReader("org/apache/ibatis/submitted/results_id/mybatis-config.xml"); + sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); + reader.close(); + + // populate in-memory database + SqlSession session = sqlSessionFactory.openSession(); + Connection conn = session.getConnection(); + reader = Resources.getResourceAsReader("org/apache/ibatis/submitted/results_id/CreateDB.sql"); + ScriptRunner runner = new ScriptRunner(conn); + runner.setLogWriter(null); + runner.runScript(reader); + reader.close(); + session.close(); + } + + @Test + public void testNamingResults() { + SqlSession sqlSession = sqlSessionFactory.openSession(); + try { + Mapper mapper = sqlSession.getMapper(Mapper.class); + User user = mapper.getUserByName("User2"); + assertEquals(Integer.valueOf(2), user.getId()); + assertEquals("User2", user.getName()); + } finally { + sqlSession.close(); + } + } + + @Test + public void testResultsOnlyForNaming() { + SqlSession sqlSession = sqlSessionFactory.openSession(); + try { + Mapper mapper = sqlSession.getMapper(Mapper.class); + User user = mapper.getUserByNameConstructor("User2"); + assertEquals(Integer.valueOf(2), user.getId()); + assertEquals("User2", user.getName()); + } finally { + sqlSession.close(); + } + } + + @Test + public void testReuseNamedResultsFromAnotherMapper() { + SqlSession sqlSession = sqlSessionFactory.openSession(); + try { + AnotherMapper mapper = sqlSession.getMapper(AnotherMapper.class); + List users = mapper.getUsers(); + assertEquals(2, users.size()); + assertEquals(Integer.valueOf(1), users.get(0).getId()); + assertEquals("User1", users.get(0).getName()); + assertEquals(Integer.valueOf(2), users.get(1).getId()); + assertEquals("User2", users.get(1).getName()); + } finally { + sqlSession.close(); + } + } + + @Test + public void testReuseNamedResultsFromXmlMapper() { + SqlSession sqlSession = sqlSessionFactory.openSession(); + try { + AnotherMapper mapper = sqlSession.getMapper(AnotherMapper.class); + User user = mapper.getUser(1); + assertEquals(Integer.valueOf(1), user.getId()); + assertEquals("User1", user.getName()); + } finally { + sqlSession.close(); + } + } +} diff --git a/src/test/java/org/apache/ibatis/submitted/results_id/User.java b/src/test/java/org/apache/ibatis/submitted/results_id/User.java new file mode 100644 index 000000000..0fbbaa3e8 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/results_id/User.java @@ -0,0 +1,48 @@ +/** + * Copyright 2009-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.submitted.results_id; + +public class User { + + private Integer id; + private String name; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public User() { + super(); + } + + public User(Integer id, String name) { + super(); + this.id = id; + this.name = name; + } +} diff --git a/src/test/java/org/apache/ibatis/submitted/results_id/mybatis-config.xml b/src/test/java/org/apache/ibatis/submitted/results_id/mybatis-config.xml new file mode 100644 index 000000000..db4946de3 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/results_id/mybatis-config.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + +