From 452ba6b50f31e535ea0b4f513418b61894f2e141 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Sat, 8 Oct 2016 20:55:18 +0900 Subject: [PATCH 0001/2062] Support java.util.Optional as return type of mapper method --- .../apache/ibatis/binding/MapperMethod.java | 50 ++++++- .../annotation/MapperAnnotationBuilder.java | 9 ++ .../optional_on_mapper_method/CreateDB.sql | 25 ++++ .../optional_on_mapper_method/Mapper.java | 30 ++++ .../optional_on_mapper_method/Mapper.xml | 29 ++++ .../OptionalOnMapperMethodTest.java | 136 ++++++++++++++++++ .../optional_on_mapper_method/User.java | 38 +++++ .../mybatis-config.xml | 42 ++++++ 8 files changed, 354 insertions(+), 5 deletions(-) create mode 100644 src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/CreateDB.sql create mode 100644 src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/Mapper.java create mode 100644 src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/Mapper.xml create mode 100644 src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/OptionalOnMapperMethodTest.java create mode 100644 src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/User.java create mode 100644 src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/mybatis-config.xml diff --git a/src/main/java/org/apache/ibatis/binding/MapperMethod.java b/src/main/java/org/apache/ibatis/binding/MapperMethod.java index 6069c94c3c7..2fed2ea319f 100644 --- a/src/main/java/org/apache/ibatis/binding/MapperMethod.java +++ b/src/main/java/org/apache/ibatis/binding/MapperMethod.java @@ -18,6 +18,7 @@ import org.apache.ibatis.annotations.Flush; import org.apache.ibatis.annotations.MapKey; import org.apache.ibatis.cursor.Cursor; +import org.apache.ibatis.io.Resources; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.reflection.MetaObject; @@ -28,19 +29,29 @@ import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.session.SqlSession; -import java.lang.reflect.Array; -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; +import java.lang.reflect.*; import java.util.*; /** * @author Clinton Begin * @author Eduardo Macarron * @author Lasse Voss + * @author Kazuki Shimizu */ public class MapperMethod { + private static Method optionalFactoryMethod = null; + + static { + try { + optionalFactoryMethod = Resources.classForName("java.util.Optional").getMethod("ofNullable", Object.class); + } catch (ClassNotFoundException e) { + // Ignore + } catch (NoSuchMethodException e) { + // Ignore + } + } + private final SqlCommand command; private final MethodSignature method; @@ -53,7 +64,7 @@ public Object execute(SqlSession sqlSession, Object[] args) { Object result; switch (command.getType()) { case INSERT: { - Object param = method.convertArgsToSqlCommandParam(args); + Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); break; } @@ -80,6 +91,10 @@ public Object execute(SqlSession sqlSession, Object[] args) { } else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); + if (method.returnsOptional() && + (result == null || !method.getReturnType().equals(result.getClass()))) { + result = wrapWithOptional(result); + } } break; case FLUSH: @@ -95,6 +110,20 @@ public Object execute(SqlSession sqlSession, Object[] args) { return result; } + private Object wrapWithOptional(Object result) { + if (optionalFactoryMethod == null) { + throw new BindingException("Can't use the java.util.Optional"); + } + try { + return optionalFactoryMethod.invoke(null, result); + } catch (IllegalAccessException e) { + throw new BindingException("Can't create a java.util.Optional instance.", e); + } catch (InvocationTargetException e) { + throw new BindingException("Can't create a java.util.Optional instance.", e); + } + } + + private Object rowCountResult(int rowCount) { final Object result; if (method.returnsVoid()) { @@ -246,6 +275,7 @@ public static class MethodSignature { private final boolean returnsMap; private final boolean returnsVoid; private final boolean returnsCursor; + private final boolean returnsOptional; private final Class returnType; private final String mapKey; private final Integer resultHandlerIndex; @@ -264,6 +294,7 @@ public MethodSignature(Configuration configuration, Class mapperInterface, Me this.returnsVoid = void.class.equals(this.returnType); this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray()); this.returnsCursor = Cursor.class.equals(this.returnType); + this.returnsOptional = "java.util.Optional".equals(this.returnType.getName()); this.mapKey = getMapKey(method); this.returnsMap = (this.mapKey != null); this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class); @@ -315,6 +346,15 @@ public boolean returnsCursor() { return returnsCursor; } + /** + * return whether return type is {@code java.util.Optional} + * @return return {@code true}, if return type is {@code java.util.Optional} + * @since 3.4.2 + */ + public boolean returnsOptional() { + return returnsOptional; + } + private Integer getUniqueParamIndex(Method method, Class paramType) { Integer index = null; final Class[] argTypes = method.getParameterTypes(); 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 629a0362918..1054370e475 100644 --- a/src/main/java/org/apache/ibatis/builder/annotation/MapperAnnotationBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/annotation/MapperAnnotationBuilder.java @@ -88,6 +88,7 @@ /** * @author Clinton Begin + * @author Kazuki Shimizu */ public class MapperAnnotationBuilder { @@ -421,6 +422,14 @@ private Class getReturnType(Method method) { returnType = (Class) ((ParameterizedType) returnTypeParameter).getRawType(); } } + } else if ("java.util.Optional".equals(rawType.getName())) { + Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); + if (actualTypeArguments != null && actualTypeArguments.length == 1) { + Type returnTypeParameter = actualTypeArguments[0]; + if (returnTypeParameter instanceof Class) { + returnType = (Class) returnTypeParameter; + } + } } } diff --git a/src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/CreateDB.sql b/src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/CreateDB.sql new file mode 100644 index 00000000000..54198b75f25 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/CreateDB.sql @@ -0,0 +1,25 @@ +-- +-- 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 users if exists; + +create table users ( + id int, + name varchar(20) +); + +insert into users (id, name) values +(1, 'User1'), (2, 'User2'); diff --git a/src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/Mapper.java b/src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/Mapper.java new file mode 100644 index 00000000000..7944a7f7b6f --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/Mapper.java @@ -0,0 +1,30 @@ +/** + * 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. + */ +package org.apache.ibatis.submitted.usesjava8.optional_on_mapper_method; + +import org.apache.ibatis.annotations.Select; + +import java.util.List; +import java.util.Optional; + +public interface Mapper { + + @Select("select * from users where id = #{id}") + Optional getUserUsingAnnotation(Integer id); + + Optional getUserUsingXml(Integer id); + +} diff --git a/src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/Mapper.xml b/src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/Mapper.xml new file mode 100644 index 00000000000..86622a9b590 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/Mapper.xml @@ -0,0 +1,29 @@ + + + + + + + + + diff --git a/src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/OptionalOnMapperMethodTest.java b/src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/OptionalOnMapperMethodTest.java new file mode 100644 index 00000000000..135e82a51c4 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/OptionalOnMapperMethodTest.java @@ -0,0 +1,136 @@ +/** + * 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. + */ +package org.apache.ibatis.submitted.usesjava8.optional_on_mapper_method; + +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; +import org.mockito.Mockito; + +import java.io.Reader; +import java.sql.Connection; +import java.util.Optional; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import static org.mockito.Mockito.*; + +/** + * Tests for support the {@code java.util.Optional} as return type of mapper method. + * + * @since 3.4.2 + * @author Kazuki Shimizu + */ +public class OptionalOnMapperMethodTest { + + private static SqlSessionFactory sqlSessionFactory; + + @BeforeClass + public static void setUp() throws Exception { + // create an SqlSessionFactory + Reader reader = Resources.getResourceAsReader( + "org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/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/usesjava8/optional_on_mapper_method/CreateDB.sql"); + ScriptRunner runner = new ScriptRunner(conn); + runner.setLogWriter(null); + runner.runScript(reader); + reader.close(); + session.close(); + } + + @Test + public void returnNotNullOnAnnotation() { + SqlSession sqlSession = sqlSessionFactory.openSession(); + try { + Mapper mapper = sqlSession.getMapper(Mapper.class); + Optional user = mapper.getUserUsingAnnotation(1); + assertTrue(user.isPresent()); + assertThat(user.get().getName(), is("User1")); + } finally { + sqlSession.close(); + } + } + + @Test + public void returnNullOnAnnotation() { + SqlSession sqlSession = sqlSessionFactory.openSession(); + try { + Mapper mapper = sqlSession.getMapper(Mapper.class); + Optional user = mapper.getUserUsingAnnotation(3); + assertFalse(user.isPresent()); + } finally { + sqlSession.close(); + } + } + + @Test + public void returnNotNullOnXml() { + SqlSession sqlSession = sqlSessionFactory.openSession(); + try { + Mapper mapper = sqlSession.getMapper(Mapper.class); + Optional user = mapper.getUserUsingXml(2); + assertTrue(user.isPresent()); + assertThat(user.get().getName(), is("User2")); + } finally { + sqlSession.close(); + } + } + + @Test + public void returnNullOnXml() { + SqlSession sqlSession = sqlSessionFactory.openSession(); + try { + Mapper mapper = sqlSession.getMapper(Mapper.class); + Optional user = mapper.getUserUsingXml(3); + assertFalse(user.isPresent()); + } finally { + sqlSession.close(); + } + } + + @Test + public void returnOptionalFromSqlSession() { + SqlSession sqlSession = Mockito.spy(sqlSessionFactory.openSession()); + + User mockUser = new User(); + mockUser.setName("mock user"); + Optional optionalMockUser = Optional.of(mockUser); + doReturn(optionalMockUser).when(sqlSession).selectOne(any(String.class), any(Object.class)); + + try { + Mapper mapper = sqlSession.getMapper(Mapper.class); + Optional user = mapper.getUserUsingAnnotation(3); + assertTrue(user == optionalMockUser); + } finally { + sqlSession.close(); + } + } + +} diff --git a/src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/User.java b/src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/User.java new file mode 100644 index 00000000000..5081c74c6cb --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/User.java @@ -0,0 +1,38 @@ +/** + * 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. + */ +package org.apache.ibatis.submitted.usesjava8.optional_on_mapper_method; + +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; + } +} diff --git a/src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/mybatis-config.xml b/src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/mybatis-config.xml new file mode 100644 index 00000000000..e16f6850d12 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/usesjava8/optional_on_mapper_method/mybatis-config.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + From 4a2bf469fe3c35e99bf8e79676ba3132147435c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AA=86=E5=90=91=E5=8D=97?= Date: Sat, 4 Mar 2017 00:23:31 +0800 Subject: [PATCH 0002/2062] Update JdbcTransaction.java update a variable name in JdbcTransaction class. --- .../org/apache/ibatis/transaction/jdbc/JdbcTransaction.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/apache/ibatis/transaction/jdbc/JdbcTransaction.java b/src/main/java/org/apache/ibatis/transaction/jdbc/JdbcTransaction.java index 7eca73ee577..583bd8e2ebf 100644 --- a/src/main/java/org/apache/ibatis/transaction/jdbc/JdbcTransaction.java +++ b/src/main/java/org/apache/ibatis/transaction/jdbc/JdbcTransaction.java @@ -42,12 +42,12 @@ public class JdbcTransaction implements Transaction { protected Connection connection; protected DataSource dataSource; protected TransactionIsolationLevel level; - protected boolean autoCommmit; + protected boolean autoCommit; public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) { dataSource = ds; level = desiredLevel; - autoCommmit = desiredAutoCommit; + autoCommit = desiredAutoCommit; } public JdbcTransaction(Connection connection) { @@ -139,7 +139,7 @@ protected void openConnection() throws SQLException { if (level != null) { connection.setTransactionIsolation(level.getLevel()); } - setDesiredAutoCommit(autoCommmit); + setDesiredAutoCommit(autoCommit); } @Override From 3995f8f8f949de26e08abdf3f31c5f839d63f4be Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Tue, 14 Mar 2017 10:42:14 +0900 Subject: [PATCH 0003/2062] Support xml parsing using xsd --- .../ibatis/builder/xml/XMLConfigBuilder.java | 30 +- .../ibatis/builder/xml/XMLMapperBuilder.java | 17 +- .../builder/xml/XMLMapperEntityResolver.java | 36 +- .../builder/xml/XMLStatementBuilder.java | 2 +- .../ibatis/builder/xml/mybatis-config.xsd | 192 +++++ .../ibatis/builder/xml/mybatis-mapper.xsd | 658 ++++++++++++++++++ .../apache/ibatis/parsing/XPathParser.java | 27 +- 7 files changed, 930 insertions(+), 32 deletions(-) create mode 100644 src/main/java/org/apache/ibatis/builder/xml/mybatis-config.xsd create mode 100644 src/main/java/org/apache/ibatis/builder/xml/mybatis-mapper.xsd 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 46dcaa66eea..4f3c156a779 100644 --- a/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2016 the original author or authors. + * Copyright 2009-2017 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. @@ -95,27 +95,27 @@ public Configuration parse() { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; - parseConfiguration(parser.evalNode("/configuration")); + parseConfiguration(parser.evalNode("*[local-name()='configuration']")); return configuration; } private void parseConfiguration(XNode root) { try { //issue #117 read properties first - propertiesElement(root.evalNode("properties")); - Properties settings = settingsAsProperties(root.evalNode("settings")); + propertiesElement(root.evalNode("*[local-name()='properties']")); + Properties settings = settingsAsProperties(root.evalNode("*[local-name()='settings']")); loadCustomVfs(settings); - typeAliasesElement(root.evalNode("typeAliases")); - pluginElement(root.evalNode("plugins")); - objectFactoryElement(root.evalNode("objectFactory")); - objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); - reflectorFactoryElement(root.evalNode("reflectorFactory")); + typeAliasesElement(root.evalNode("*[local-name()='typeAliases']")); + pluginElement(root.evalNode("*[local-name()='plugins']")); + objectFactoryElement(root.evalNode("*[local-name()='objectFactory']")); + objectWrapperFactoryElement(root.evalNode("*[local-name()='objectWrapperFactory']")); + reflectorFactoryElement(root.evalNode("*[local-name()='reflectorFactory']")); settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 - environmentsElement(root.evalNode("environments")); - databaseIdProviderElement(root.evalNode("databaseIdProvider")); - typeHandlerElement(root.evalNode("typeHandlers")); - mapperElement(root.evalNode("mappers")); + environmentsElement(root.evalNode("*[local-name()='environments']")); + databaseIdProviderElement(root.evalNode("*[local-name()='databaseIdProvider']")); + typeHandlerElement(root.evalNode("*[local-name()='typeHandlers']")); + mapperElement(root.evalNode("*[local-name()='mappers']")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } @@ -272,8 +272,8 @@ private void environmentsElement(XNode context) throws Exception { for (XNode child : context.getChildren()) { String id = child.getStringAttribute("id"); if (isSpecifiedEnvironment(id)) { - TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); - DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); + TransactionFactory txFactory = transactionManagerElement(child.evalNode("*[local-name()='transactionManager']")); + DataSourceFactory dsFactory = dataSourceElement(child.evalNode("*[local-name()='dataSource']")); DataSource dataSource = dsFactory.getDataSource(); Environment.Builder environmentBuilder = new Environment.Builder(id) .transactionFactory(txFactory) diff --git a/src/main/java/org/apache/ibatis/builder/xml/XMLMapperBuilder.java b/src/main/java/org/apache/ibatis/builder/xml/XMLMapperBuilder.java index 263e71eda79..6c5e8e5948d 100644 --- a/src/main/java/org/apache/ibatis/builder/xml/XMLMapperBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/xml/XMLMapperBuilder.java @@ -49,6 +49,7 @@ /** * @author Clinton Begin + * @author Kazuki Shimizu */ public class XMLMapperBuilder extends BaseBuilder { @@ -89,7 +90,7 @@ private XMLMapperBuilder(XPathParser parser, Configuration configuration, String public void parse() { if (!configuration.isResourceLoaded(resource)) { - configurationElement(parser.evalNode("/mapper")); + configurationElement(parser.evalNode("*[local-name()='mapper']")); configuration.addLoadedResource(resource); bindMapperForNamespace(); } @@ -110,12 +111,12 @@ private void configurationElement(XNode context) { throw new BuilderException("Mapper's namespace cannot be empty"); } builderAssistant.setCurrentNamespace(namespace); - cacheRefElement(context.evalNode("cache-ref")); - cacheElement(context.evalNode("cache")); - parameterMapElement(context.evalNodes("/mapper/parameterMap")); - resultMapElements(context.evalNodes("/mapper/resultMap")); - sqlElement(context.evalNodes("/mapper/sql")); - buildStatementFromContext(context.evalNodes("select|insert|update|delete")); + cacheRefElement(context.evalNode("*[local-name()='cache-ref']")); + cacheElement(context.evalNode("*[local-name()='cache']")); + parameterMapElement(context.evalNodes("*[local-name()='parameterMap']")); + resultMapElements(context.evalNodes("*[local-name()='resultMap']")); + sqlElement(context.evalNodes("*[local-name()='sql']")); + buildStatementFromContext(context.evalNodes("*[local-name()='select' or local-name()='insert' or local-name()='update' or local-name()='delete']")); } catch (Exception e) { throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e); } @@ -216,7 +217,7 @@ private void parameterMapElement(List list) throws Exception { String id = parameterMapNode.getStringAttribute("id"); String type = parameterMapNode.getStringAttribute("type"); Class parameterClass = resolveClass(type); - List parameterNodes = parameterMapNode.evalNodes("parameter"); + List parameterNodes = parameterMapNode.evalNodes("*[local-name()='parameter']"); List parameterMappings = new ArrayList(); for (XNode parameterNode : parameterNodes) { String property = parameterNode.getStringAttribute("property"); diff --git a/src/main/java/org/apache/ibatis/builder/xml/XMLMapperEntityResolver.java b/src/main/java/org/apache/ibatis/builder/xml/XMLMapperEntityResolver.java index 50b63f24c10..d5de397cb24 100644 --- a/src/main/java/org/apache/ibatis/builder/xml/XMLMapperEntityResolver.java +++ b/src/main/java/org/apache/ibatis/builder/xml/XMLMapperEntityResolver.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2016 the original author or authors. + * Copyright 2009-2017 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. @@ -25,7 +25,7 @@ import org.xml.sax.SAXException; /** - * Offline entity resolver for the MyBatis DTDs + * Offline entity resolver for the MyBatis DTDs(or XSDs) * * @author Clinton Begin * @author Eduardo Macarron @@ -36,16 +36,21 @@ public class XMLMapperEntityResolver implements EntityResolver { private static final String IBATIS_MAPPER_SYSTEM = "ibatis-3-mapper.dtd"; private static final String MYBATIS_CONFIG_SYSTEM = "mybatis-3-config.dtd"; private static final String MYBATIS_MAPPER_SYSTEM = "mybatis-3-mapper.dtd"; + private static final String MYBATIS_CONFIG = "mybatis-config.xsd"; + private static final String MYBATIS_MAPPER = "mybatis-mapper.xsd"; private static final String MYBATIS_CONFIG_DTD = "org/apache/ibatis/builder/xml/mybatis-3-config.dtd"; private static final String MYBATIS_MAPPER_DTD = "org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd"; + private static final String MYBATIS_CONFIG_XSD = "org/apache/ibatis/builder/xml/mybatis-config.xsd"; + private static final String MYBATIS_MAPPER_XSD = "org/apache/ibatis/builder/xml/mybatis-mapper.xsd"; + /* - * Converts a public DTD into a local one + * Converts a public DTD(XSD) into a local one * * @param publicId The public id that is what comes after "PUBLIC" * @param systemId The system id that is what comes after the public id. - * @return The InputSource for the DTD + * @return The InputSource for the DTD(XSD) * * @throws org.xml.sax.SAXException If anything goes wrong */ @@ -55,9 +60,13 @@ public InputSource resolveEntity(String publicId, String systemId) throws SAXExc if (systemId != null) { String lowerCaseSystemId = systemId.toLowerCase(Locale.ENGLISH); if (lowerCaseSystemId.contains(MYBATIS_CONFIG_SYSTEM) || lowerCaseSystemId.contains(IBATIS_CONFIG_SYSTEM)) { - return getInputSource(MYBATIS_CONFIG_DTD, publicId, systemId); + return getDtdInputSource(MYBATIS_CONFIG_DTD, publicId, systemId); } else if (lowerCaseSystemId.contains(MYBATIS_MAPPER_SYSTEM) || lowerCaseSystemId.contains(IBATIS_MAPPER_SYSTEM)) { - return getInputSource(MYBATIS_MAPPER_DTD, publicId, systemId); + return getDtdInputSource(MYBATIS_MAPPER_DTD, publicId, systemId); + } else if (systemId.contains(MYBATIS_CONFIG)) { + return getXsdInputSource(MYBATIS_CONFIG_XSD); + } else if (systemId.contains(MYBATIS_MAPPER)){ + return getXsdInputSource(MYBATIS_MAPPER_XSD); } } return null; @@ -66,7 +75,7 @@ public InputSource resolveEntity(String publicId, String systemId) throws SAXExc } } - private InputSource getInputSource(String path, String publicId, String systemId) { + private InputSource getDtdInputSource(String path, String publicId, String systemId) { InputSource source = null; if (path != null) { try { @@ -81,4 +90,17 @@ private InputSource getInputSource(String path, String publicId, String systemId return source; } + private InputSource getXsdInputSource(String path) { + InputSource source = null; + if (path != null) { + try { + InputStream in = Resources.getResourceAsStream(path); + source = new InputSource(in); + } catch (IOException e) { + // ignore, null is ok + } + } + return source; + } + } \ No newline at end of file diff --git a/src/main/java/org/apache/ibatis/builder/xml/XMLStatementBuilder.java b/src/main/java/org/apache/ibatis/builder/xml/XMLStatementBuilder.java index 0beaa5bcdad..5ae9ed82c6d 100644 --- a/src/main/java/org/apache/ibatis/builder/xml/XMLStatementBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/xml/XMLStatementBuilder.java @@ -113,7 +113,7 @@ public void parseStatementNode() { } private void processSelectKeyNodes(String id, Class parameterTypeClass, LanguageDriver langDriver) { - List selectKeyNodes = context.evalNodes("selectKey"); + List selectKeyNodes = context.evalNodes("*[local-name()='selectKey']"); if (configuration.getDatabaseId() != null) { parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, configuration.getDatabaseId()); } diff --git a/src/main/java/org/apache/ibatis/builder/xml/mybatis-config.xsd b/src/main/java/org/apache/ibatis/builder/xml/mybatis-config.xsd new file mode 100644 index 00000000000..54dd37eaf3b --- /dev/null +++ b/src/main/java/org/apache/ibatis/builder/xml/mybatis-config.xsd @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/org/apache/ibatis/builder/xml/mybatis-mapper.xsd b/src/main/java/org/apache/ibatis/builder/xml/mybatis-mapper.xsd new file mode 100644 index 00000000000..cb91decc24e --- /dev/null +++ b/src/main/java/org/apache/ibatis/builder/xml/mybatis-mapper.xsd @@ -0,0 +1,658 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/org/apache/ibatis/parsing/XPathParser.java b/src/main/java/org/apache/ibatis/parsing/XPathParser.java index f56ca9d90e7..bb81558f314 100644 --- a/src/main/java/org/apache/ibatis/parsing/XPathParser.java +++ b/src/main/java/org/apache/ibatis/parsing/XPathParser.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2015 the original author or authors. + * Copyright 2009-2017 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. @@ -22,6 +22,7 @@ import java.util.List; import java.util.Properties; +import javax.xml.XMLConstants; import javax.xml.namespace.QName; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -30,6 +31,7 @@ import javax.xml.xpath.XPathFactory; import org.apache.ibatis.builder.BuilderException; +import org.apache.ibatis.io.Resources; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -41,9 +43,22 @@ /** * @author Clinton Begin + * @author Kazuki Shimizu */ public class XPathParser { + /** + * The special property key that indicate whether use a xsd file on xml parsing. + *

+ * The default value is {@code false} (indicate disable using a xsd file xml parsing) + * If you specify the {@code true}, you can use a xsd file on config xml or mapper xml. + *

+ * @since 3.5.0 + */ + public static final String KEY_USE_XSD = "org.apache.ibatis.parsing.XPathParser.use-xsd"; + + private static final String USE_XSD_DEFAULT_VALUE = "false"; + private Document document; private boolean validation; private EntityResolver entityResolver; @@ -237,6 +252,12 @@ private Document createDocument(InputSource inputSource) { factory.setCoalescing(false); factory.setExpandEntityReferences(true); + boolean useXsd = Boolean.parseBoolean(getPropertyValue(KEY_USE_XSD, USE_XSD_DEFAULT_VALUE)); + if (useXsd) { + factory.setNamespaceAware(true); + factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", XMLConstants.W3C_XML_SCHEMA_NS_URI); + } + DocumentBuilder builder = factory.newDocumentBuilder(); builder.setEntityResolver(entityResolver); builder.setErrorHandler(new ErrorHandler() { @@ -268,4 +289,8 @@ private void commonConstructor(boolean validation, Properties variables, EntityR this.xpath = factory.newXPath(); } + private String getPropertyValue(String key, String defaultValue) { + return (variables == null) ? defaultValue : variables.getProperty(key, defaultValue); + } + } From 0dc00976ac3a609adc4edd1d1de14b2cb746cd23 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Tue, 14 Mar 2017 10:42:31 +0900 Subject: [PATCH 0004/2062] Add tests for support xml parsing using xsd --- .../ibatis/builder/xsd/AuthorMapper.xml | 155 ++++++++++++++++ .../apache/ibatis/builder/xsd/BlogMapper.xml | 155 ++++++++++++++++ .../builder/xsd/CachedAuthorMapper.java | 26 +++ .../ibatis/builder/xsd/CachedAuthorMapper.xml | 55 ++++++ .../xsd/CustomizedSettingsMapperConfig.xml | 108 +++++++++++ .../builder/xsd/MinimalMapperConfig.xml | 26 +++ .../ibatis/builder/xsd/NestedBlogMapper.xml | 87 +++++++++ .../builder/xsd/XmlConfigBuilderTest.java | 170 ++++++++++++++++++ .../builder/xsd/XmlMapperBuilderTest.java | 56 ++++++ 9 files changed, 838 insertions(+) create mode 100644 src/test/java/org/apache/ibatis/builder/xsd/AuthorMapper.xml create mode 100644 src/test/java/org/apache/ibatis/builder/xsd/BlogMapper.xml create mode 100644 src/test/java/org/apache/ibatis/builder/xsd/CachedAuthorMapper.java create mode 100644 src/test/java/org/apache/ibatis/builder/xsd/CachedAuthorMapper.xml create mode 100644 src/test/java/org/apache/ibatis/builder/xsd/CustomizedSettingsMapperConfig.xml create mode 100644 src/test/java/org/apache/ibatis/builder/xsd/MinimalMapperConfig.xml create mode 100644 src/test/java/org/apache/ibatis/builder/xsd/NestedBlogMapper.xml create mode 100644 src/test/java/org/apache/ibatis/builder/xsd/XmlConfigBuilderTest.java create mode 100644 src/test/java/org/apache/ibatis/builder/xsd/XmlMapperBuilderTest.java diff --git a/src/test/java/org/apache/ibatis/builder/xsd/AuthorMapper.xml b/src/test/java/org/apache/ibatis/builder/xsd/AuthorMapper.xml new file mode 100644 index 00000000000..a2f70d23055 --- /dev/null +++ b/src/test/java/org/apache/ibatis/builder/xsd/AuthorMapper.xml @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + insert into Author (username,password,email,bio) + values (#{username},#{password},#{email},#{bio}) + + + + update Author + set username=#{username, + javaType=String}, + password=#{password}, + email=#{email}, + bio=#{bio} + where id=#{id} + + + + delete from Author where id = #{id} + + + + + update Author + + username=#{username}, + password=#{password}, + email=#{email}, + bio=#{bio} + + where id=#{id} + + + + + + SELECT #{num} + + + \ No newline at end of file diff --git a/src/test/java/org/apache/ibatis/builder/xsd/BlogMapper.xml b/src/test/java/org/apache/ibatis/builder/xsd/BlogMapper.xml new file mode 100644 index 00000000000..13e5450d33c --- /dev/null +++ b/src/test/java/org/apache/ibatis/builder/xsd/BlogMapper.xml @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/org/apache/ibatis/builder/xsd/CachedAuthorMapper.java b/src/test/java/org/apache/ibatis/builder/xsd/CachedAuthorMapper.java new file mode 100644 index 00000000000..e477c4527f2 --- /dev/null +++ b/src/test/java/org/apache/ibatis/builder/xsd/CachedAuthorMapper.java @@ -0,0 +1,26 @@ +/** + * Copyright 2009-2017 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.builder.xsd; + +import org.apache.ibatis.domain.blog.Author; + +public interface CachedAuthorMapper { + Author selectAllAuthors(); + Author selectAuthorWithInlineParams(int id); + void insertAuthor(Author author); + boolean updateAuthor(Author author); + boolean deleteAuthor(int id); +} diff --git a/src/test/java/org/apache/ibatis/builder/xsd/CachedAuthorMapper.xml b/src/test/java/org/apache/ibatis/builder/xsd/CachedAuthorMapper.xml new file mode 100644 index 00000000000..959cfc6edda --- /dev/null +++ b/src/test/java/org/apache/ibatis/builder/xsd/CachedAuthorMapper.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + insert into Author (id,username,password,email,bio) + values (#{id},#{username},#{password},#{email},#{bio}) + + + + update Author + set username=#{username},password=#{password},email=#{email},bio=#{bio} + where id=#{id} + + + + delete from Author where id = #{id} + + + \ No newline at end of file diff --git a/src/test/java/org/apache/ibatis/builder/xsd/CustomizedSettingsMapperConfig.xml b/src/test/java/org/apache/ibatis/builder/xsd/CustomizedSettingsMapperConfig.xml new file mode 100644 index 00000000000..c975298b84c --- /dev/null +++ b/src/test/java/org/apache/ibatis/builder/xsd/CustomizedSettingsMapperConfig.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/org/apache/ibatis/builder/xsd/MinimalMapperConfig.xml b/src/test/java/org/apache/ibatis/builder/xsd/MinimalMapperConfig.xml new file mode 100644 index 00000000000..5933b42322b --- /dev/null +++ b/src/test/java/org/apache/ibatis/builder/xsd/MinimalMapperConfig.xml @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/src/test/java/org/apache/ibatis/builder/xsd/NestedBlogMapper.xml b/src/test/java/org/apache/ibatis/builder/xsd/NestedBlogMapper.xml new file mode 100644 index 00000000000..8cd41974dd1 --- /dev/null +++ b/src/test/java/org/apache/ibatis/builder/xsd/NestedBlogMapper.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/org/apache/ibatis/builder/xsd/XmlConfigBuilderTest.java b/src/test/java/org/apache/ibatis/builder/xsd/XmlConfigBuilderTest.java new file mode 100644 index 00000000000..2d6f2f3da4e --- /dev/null +++ b/src/test/java/org/apache/ibatis/builder/xsd/XmlConfigBuilderTest.java @@ -0,0 +1,170 @@ +/** + * Copyright 2009-2017 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.builder.xsd; + +import org.apache.ibatis.builder.CustomLongTypeHandler; +import org.apache.ibatis.builder.CustomObjectWrapperFactory; +import org.apache.ibatis.builder.CustomReflectorFactory; +import org.apache.ibatis.builder.CustomStringTypeHandler; +import org.apache.ibatis.builder.ExampleObjectFactory; +import org.apache.ibatis.builder.ExamplePlugin; +import org.apache.ibatis.builder.mapper.CustomMapper; +import org.apache.ibatis.builder.typehandler.CustomIntegerTypeHandler; +import org.apache.ibatis.builder.xml.XMLConfigBuilder; +import org.apache.ibatis.datasource.unpooled.UnpooledDataSource; +import org.apache.ibatis.domain.blog.Author; +import org.apache.ibatis.domain.blog.Blog; +import org.apache.ibatis.domain.blog.mappers.BlogMapper; +import org.apache.ibatis.domain.blog.mappers.NestedBlogMapper; +import org.apache.ibatis.domain.jpetstore.Cart; +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.mapping.Environment; +import org.apache.ibatis.parsing.XPathParser; +import org.apache.ibatis.scripting.defaults.RawLanguageDriver; +import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver; +import org.apache.ibatis.session.*; +import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; +import org.apache.ibatis.type.JdbcType; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.io.InputStream; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Properties; +import java.util.Set; + +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsInstanceOf.instanceOf; +import static org.junit.Assert.*; + +public class XmlConfigBuilderTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void shouldSuccessfullyLoadMinimalXMLConfigFile() throws Exception { + String resource = "org/apache/ibatis/builder/xsd/MinimalMapperConfig.xml"; + InputStream inputStream = Resources.getResourceAsStream(resource); + Properties variables = new Properties(); + variables.setProperty(XPathParser.KEY_USE_XSD, "true"); + XMLConfigBuilder builder = new XMLConfigBuilder(inputStream, null, variables); + Configuration config = builder.parse(); + assertNotNull(config); + assertThat(config.getAutoMappingBehavior(), is(AutoMappingBehavior.PARTIAL)); + assertThat(config.getAutoMappingUnknownColumnBehavior(), is(AutoMappingUnknownColumnBehavior.NONE)); + assertThat(config.isCacheEnabled(), is(true)); + assertThat(config.getProxyFactory(), is(instanceOf(JavassistProxyFactory.class))); + assertThat(config.isLazyLoadingEnabled(), is(false)); + assertThat(config.isAggressiveLazyLoading(), is(false)); + assertThat(config.isMultipleResultSetsEnabled(), is(true)); + assertThat(config.isUseColumnLabel(), is(true)); + assertThat(config.isUseGeneratedKeys(), is(false)); + assertThat(config.getDefaultExecutorType(), is(ExecutorType.SIMPLE)); + assertNull(config.getDefaultStatementTimeout()); + assertNull(config.getDefaultFetchSize()); + assertThat(config.isMapUnderscoreToCamelCase(), is(false)); + assertThat(config.isSafeRowBoundsEnabled(), is(false)); + assertThat(config.getLocalCacheScope(), is(LocalCacheScope.SESSION)); + assertThat(config.getJdbcTypeForNull(), is(JdbcType.OTHER)); + assertThat(config.getLazyLoadTriggerMethods(), is((Set) new HashSet(Arrays.asList("equals", "clone", "hashCode", "toString")))); + assertThat(config.isSafeResultHandlerEnabled(), is(true)); + assertThat(config.getDefaultScriptingLanguageInstance(), is(instanceOf(XMLLanguageDriver.class))); + assertThat(config.isCallSettersOnNulls(), is(false)); + assertNull(config.getLogPrefix()); + assertNull(config.getLogImpl()); + assertNull(config.getConfigurationFactory()); + } + + @Test + public void shouldSuccessfullyLoadXMLConfigFile() throws Exception { + String resource = "org/apache/ibatis/builder/xsd/CustomizedSettingsMapperConfig.xml"; + InputStream inputStream = Resources.getResourceAsStream(resource); + Properties variables = new Properties(); + variables.setProperty(XPathParser.KEY_USE_XSD, "true"); + variables.put("prop2", "cccc"); + XMLConfigBuilder builder = new XMLConfigBuilder(inputStream, null, variables); + Configuration config = builder.parse(); + + assertThat(config.getAutoMappingBehavior(), is(AutoMappingBehavior.NONE)); + assertThat(config.getAutoMappingUnknownColumnBehavior(), is(AutoMappingUnknownColumnBehavior.WARNING)); + assertThat(config.isCacheEnabled(), is(false)); + assertThat(config.getProxyFactory(), is(instanceOf(CglibProxyFactory.class))); + assertThat(config.isLazyLoadingEnabled(), is(true)); + assertThat(config.isAggressiveLazyLoading(), is(true)); + assertThat(config.isMultipleResultSetsEnabled(), is(false)); + assertThat(config.isUseColumnLabel(), is(false)); + assertThat(config.isUseGeneratedKeys(), is(true)); + assertThat(config.getDefaultExecutorType(), is(ExecutorType.BATCH)); + assertThat(config.getDefaultStatementTimeout(), is(10)); + assertThat(config.getDefaultFetchSize(), is(100)); + assertThat(config.isMapUnderscoreToCamelCase(), is(true)); + assertThat(config.isSafeRowBoundsEnabled(), is(true)); + assertThat(config.getLocalCacheScope(), is(LocalCacheScope.STATEMENT)); + assertThat(config.getJdbcTypeForNull(), is(JdbcType.NULL)); + assertThat(config.getLazyLoadTriggerMethods(), is((Set) new HashSet(Arrays.asList("equals", "clone", "hashCode", "toString", "xxx")))); + assertThat(config.isSafeResultHandlerEnabled(), is(false)); + assertThat(config.getDefaultScriptingLanguageInstance(), is(instanceOf(RawLanguageDriver.class))); + 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())); + + assertTrue(config.getTypeAliasRegistry().getTypeAliases().get("blogauthor").equals(Author.class)); + assertTrue(config.getTypeAliasRegistry().getTypeAliases().get("blog").equals(Blog.class)); + assertTrue(config.getTypeAliasRegistry().getTypeAliases().get("cart").equals(Cart.class)); + + assertThat(config.getTypeHandlerRegistry().getTypeHandler(Integer.class), is(instanceOf(CustomIntegerTypeHandler.class))); + assertThat(config.getTypeHandlerRegistry().getTypeHandler(Long.class), is(instanceOf(CustomLongTypeHandler.class))); + assertThat(config.getTypeHandlerRegistry().getTypeHandler(String.class), is(instanceOf(CustomStringTypeHandler.class))); + assertThat(config.getTypeHandlerRegistry().getTypeHandler(String.class, JdbcType.VARCHAR), is(instanceOf(CustomStringTypeHandler.class))); + + ExampleObjectFactory objectFactory = (ExampleObjectFactory)config.getObjectFactory(); + assertThat(objectFactory.getProperties().size(), is(1)); + assertThat(objectFactory.getProperties().getProperty("objectFactoryProperty"), is("100")); + + assertThat(config.getObjectWrapperFactory(), is(instanceOf(CustomObjectWrapperFactory.class))); + + assertThat(config.getReflectorFactory(), is(instanceOf(CustomReflectorFactory.class))); + + ExamplePlugin plugin = (ExamplePlugin)config.getInterceptors().get(0); + assertThat(plugin.getProperties().size(), is(1)); + assertThat(plugin.getProperties().getProperty("pluginProperty"), is("100")); + + Environment environment = config.getEnvironment(); + assertThat(environment.getId(), is("development")); + assertThat(environment.getDataSource(), is(instanceOf(UnpooledDataSource.class))); + assertThat(environment.getTransactionFactory(), is(instanceOf(JdbcTransactionFactory.class))); + + assertThat(config.getDatabaseId(), is("derby")); + + assertThat(config.getMapperRegistry().getMappers().size(), is(4)); + assertThat(config.getMapperRegistry().hasMapper(CachedAuthorMapper.class), is(true)); + assertThat(config.getMapperRegistry().hasMapper(CustomMapper.class), is(true)); + assertThat(config.getMapperRegistry().hasMapper(BlogMapper.class), is(true)); + assertThat(config.getMapperRegistry().hasMapper(NestedBlogMapper.class), is(true)); + + } + + +} diff --git a/src/test/java/org/apache/ibatis/builder/xsd/XmlMapperBuilderTest.java b/src/test/java/org/apache/ibatis/builder/xsd/XmlMapperBuilderTest.java new file mode 100644 index 00000000000..a83cd9be4eb --- /dev/null +++ b/src/test/java/org/apache/ibatis/builder/xsd/XmlMapperBuilderTest.java @@ -0,0 +1,56 @@ +/** + * Copyright 2009-2017 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.builder.xsd; + +import org.apache.ibatis.builder.xml.XMLMapperBuilder; +import org.apache.ibatis.io.Resources; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.ResultSetType; +import org.apache.ibatis.mapping.StatementType; +import org.apache.ibatis.parsing.XPathParser; +import org.apache.ibatis.session.Configuration; +import org.junit.Test; + +import java.io.InputStream; +import java.util.Properties; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.assertThat; + +public class XmlMapperBuilderTest { + + @Test + public void mappedStatementWithOptions() throws Exception { + Configuration configuration = new Configuration(); + Properties variables = new Properties(); + variables.setProperty(XPathParser.KEY_USE_XSD, "true"); + configuration.setVariables(variables); + String resource = "org/apache/ibatis/builder/xsd/AuthorMapper.xml"; + InputStream inputStream = Resources.getResourceAsStream(resource); + XMLMapperBuilder builder = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); + builder.parse(); + + MappedStatement mappedStatement = configuration.getMappedStatement("selectWithOptions"); + assertThat(mappedStatement.getFetchSize(), is(200)); + assertThat(mappedStatement.getTimeout(), is(10)); + assertThat(mappedStatement.getStatementType(), is(StatementType.PREPARED)); + assertThat(mappedStatement.getResultSetType(), is(ResultSetType.SCROLL_SENSITIVE)); + assertThat(mappedStatement.isFlushCacheRequired(), is(false)); + assertThat(mappedStatement.isUseCache(), is(false)); + + } + +} From de445eac419f20caaeca875448202b2a26e05382 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Tue, 4 Jul 2017 22:30:59 +0900 Subject: [PATCH 0005/2062] Removed comment. --- .../java/org/apache/ibatis/transaction/jdbc/JdbcTransaction.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/apache/ibatis/transaction/jdbc/JdbcTransaction.java b/src/main/java/org/apache/ibatis/transaction/jdbc/JdbcTransaction.java index 57128d3564d..583bd8e2ebf 100644 --- a/src/main/java/org/apache/ibatis/transaction/jdbc/JdbcTransaction.java +++ b/src/main/java/org/apache/ibatis/transaction/jdbc/JdbcTransaction.java @@ -42,7 +42,6 @@ public class JdbcTransaction implements Transaction { protected Connection connection; protected DataSource dataSource; protected TransactionIsolationLevel level; - // MEMO: We are aware of the typo. See #941 protected boolean autoCommit; public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) { From 31a6a810f611270b2e68450262da7847bfd2568e Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Tue, 18 Jul 2017 08:17:07 +0900 Subject: [PATCH 0006/2062] Add defaultResultSetType as global configuration --- .../apache/ibatis/annotations/Options.java | 4 +- .../annotation/MapperAnnotationBuilder.java | 6 +- .../ibatis/builder/xml/XMLConfigBuilder.java | 1 + .../builder/xml/XMLStatementBuilder.java | 3 + .../apache/ibatis/mapping/ResultSetType.java | 5 + .../apache/ibatis/session/Configuration.java | 16 +++ .../builder/AnnotationMapperBuilderTest.java | 125 ++++++++++++++++++ .../CustomizedSettingsMapperConfig.xml | 1 + .../ibatis/builder/XmlConfigBuilderTest.java | 3 + .../ibatis/builder/XmlMapperBuilderTest.java | 14 ++ 10 files changed, 174 insertions(+), 4 deletions(-) create mode 100644 src/test/java/org/apache/ibatis/builder/AnnotationMapperBuilderTest.java diff --git a/src/main/java/org/apache/ibatis/annotations/Options.java b/src/main/java/org/apache/ibatis/annotations/Options.java index ca0d937072f..1132888fe94 100644 --- a/src/main/java/org/apache/ibatis/annotations/Options.java +++ b/src/main/java/org/apache/ibatis/annotations/Options.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2016 the original author or authors. + * Copyright 2009-2017 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. @@ -48,7 +48,7 @@ public enum FlushCachePolicy { FlushCachePolicy flushCache() default FlushCachePolicy.DEFAULT; - ResultSetType resultSetType() default ResultSetType.FORWARD_ONLY; + ResultSetType resultSetType() default ResultSetType.DEFAULT; StatementType statementType() default StatementType.PREPARED; 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 54cd9a644d1..85d023d2175 100644 --- a/src/main/java/org/apache/ibatis/builder/annotation/MapperAnnotationBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/annotation/MapperAnnotationBuilder.java @@ -293,7 +293,7 @@ void parseStatement(Method method) { Integer fetchSize = null; Integer timeout = null; StatementType statementType = StatementType.PREPARED; - ResultSetType resultSetType = ResultSetType.FORWARD_ONLY; + ResultSetType resultSetType = configuration.getDefaultResultSetType(); SqlCommandType sqlCommandType = getSqlCommandType(method); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; boolean flushCache = !isSelect; @@ -329,7 +329,9 @@ void parseStatement(Method method) { fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348 timeout = options.timeout() > -1 ? options.timeout() : null; statementType = options.statementType(); - resultSetType = options.resultSetType(); + if (options.resultSetType() != ResultSetType.DEFAULT) { + resultSetType = options.resultSetType(); + } } String resultMapId = null; 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 e711e204706..0fb429ff41a 100644 --- a/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java @@ -248,6 +248,7 @@ private void settingsElement(Properties props) throws Exception { configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE"))); configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null)); configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null)); + configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType"))); configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false)); configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false)); configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION"))); diff --git a/src/main/java/org/apache/ibatis/builder/xml/XMLStatementBuilder.java b/src/main/java/org/apache/ibatis/builder/xml/XMLStatementBuilder.java index ddc7663a643..f28f4e884a3 100644 --- a/src/main/java/org/apache/ibatis/builder/xml/XMLStatementBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/xml/XMLStatementBuilder.java @@ -75,6 +75,9 @@ public void parseStatementNode() { String resultSetType = context.getStringAttribute("resultSetType"); StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString())); ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType); + if (resultSetTypeEnum == null) { + resultSetTypeEnum = configuration.getDefaultResultSetType(); + } String nodeName = context.getNode().getNodeName(); SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH)); diff --git a/src/main/java/org/apache/ibatis/mapping/ResultSetType.java b/src/main/java/org/apache/ibatis/mapping/ResultSetType.java index 7d60b88bf0b..3eebe427070 100644 --- a/src/main/java/org/apache/ibatis/mapping/ResultSetType.java +++ b/src/main/java/org/apache/ibatis/mapping/ResultSetType.java @@ -21,6 +21,11 @@ * @author Clinton Begin */ public enum ResultSetType { + /** + * Special value that indicate to use default value. + * @since 3.4.5 + */ + DEFAULT(-1), FORWARD_ONLY(ResultSet.TYPE_FORWARD_ONLY), SCROLL_INSENSITIVE(ResultSet.TYPE_SCROLL_INSENSITIVE), SCROLL_SENSITIVE(ResultSet.TYPE_SCROLL_SENSITIVE); diff --git a/src/main/java/org/apache/ibatis/session/Configuration.java b/src/main/java/org/apache/ibatis/session/Configuration.java index f2a60fe03f7..3e07f14792a 100644 --- a/src/main/java/org/apache/ibatis/session/Configuration.java +++ b/src/main/java/org/apache/ibatis/session/Configuration.java @@ -68,6 +68,7 @@ import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.ParameterMap; import org.apache.ibatis.mapping.ResultMap; +import org.apache.ibatis.mapping.ResultSetType; import org.apache.ibatis.mapping.VendorDatabaseIdProvider; import org.apache.ibatis.parsing.XNode; import org.apache.ibatis.plugin.Interceptor; @@ -118,6 +119,7 @@ public class Configuration { protected Set lazyLoadTriggerMethods = new HashSet(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" })); protected Integer defaultStatementTimeout; protected Integer defaultFetchSize; + protected ResultSetType defaultResultSetType; protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE; protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL; protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE; @@ -426,6 +428,20 @@ public void setDefaultFetchSize(Integer defaultFetchSize) { this.defaultFetchSize = defaultFetchSize; } + /** + * @since 3.4.5 + */ + public ResultSetType getDefaultResultSetType() { + return defaultResultSetType; + } + + /** + * @since 3.4.5 + */ + public void setDefaultResultSetType(ResultSetType defaultResultSetType) { + this.defaultResultSetType = defaultResultSetType; + } + public boolean isUseColumnLabel() { return useColumnLabel; } diff --git a/src/test/java/org/apache/ibatis/builder/AnnotationMapperBuilderTest.java b/src/test/java/org/apache/ibatis/builder/AnnotationMapperBuilderTest.java new file mode 100644 index 00000000000..dca9f3db709 --- /dev/null +++ b/src/test/java/org/apache/ibatis/builder/AnnotationMapperBuilderTest.java @@ -0,0 +1,125 @@ +/** + * Copyright 2009-2017 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.builder; + +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Options; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.builder.annotation.MapperAnnotationBuilder; +import org.apache.ibatis.builder.xml.XMLMapperBuilder; +import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator; +import org.apache.ibatis.io.Resources; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.ResultSetType; +import org.apache.ibatis.mapping.StatementType; +import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.type.TypeHandler; +import org.junit.Test; + +import java.io.InputStream; +import java.util.regex.Pattern; + +import static com.googlecode.catchexception.apis.BDDCatchException.caughtException; +import static com.googlecode.catchexception.apis.BDDCatchException.when; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.BDDAssertions.then; + +public class AnnotationMapperBuilderTest { + + @Test + public void withOptions() throws Exception { + Configuration configuration = new Configuration(); + MapperAnnotationBuilder builder = new MapperAnnotationBuilder(configuration, Mapper.class); + builder.parse(); + + MappedStatement mappedStatement = configuration.getMappedStatement("selectWithOptions"); + assertThat(mappedStatement.getFetchSize()).isEqualTo(200); + assertThat(mappedStatement.getTimeout()).isEqualTo(10); + assertThat(mappedStatement.getStatementType()).isEqualTo(StatementType.STATEMENT); + assertThat(mappedStatement.getResultSetType()).isEqualTo(ResultSetType.SCROLL_INSENSITIVE); + assertThat(mappedStatement.isFlushCacheRequired()).isTrue(); + assertThat(mappedStatement.isUseCache()).isFalse(); + assertThat(mappedStatement.getResultSets()).containsExactly("resultSets"); + + mappedStatement = configuration.getMappedStatement("insertWithOptions"); + assertThat(mappedStatement.getKeyGenerator()).isInstanceOf(Jdbc3KeyGenerator.class); + assertThat(mappedStatement.getKeyColumns()).containsExactly("key_column"); + assertThat(mappedStatement.getKeyProperties()).containsExactly("keyProperty"); + } + + @Test + public void withOptionsAndWithoutOptionsAttributesWhenSpecifyDefaultValue() throws Exception { + Configuration configuration = new Configuration(); + configuration.setDefaultResultSetType(ResultSetType.SCROLL_INSENSITIVE); + MapperAnnotationBuilder builder = new MapperAnnotationBuilder(configuration, Mapper.class); + builder.parse(); + + MappedStatement mappedStatement = configuration.getMappedStatement("selectWithOptionsAndWithoutOptionsAttributes"); + assertThat(mappedStatement.getResultSetType()).isEqualTo(ResultSetType.SCROLL_INSENSITIVE); + } + + + @Test + public void withOptionsAndWithoutOptionsAttributesWhenNotSpecifyDefaultValue() throws Exception { + Configuration configuration = new Configuration(); + MapperAnnotationBuilder builder = new MapperAnnotationBuilder(configuration, Mapper.class); + builder.parse(); + + MappedStatement mappedStatement = configuration.getMappedStatement("selectWithOptionsAndWithoutOptionsAttributes"); + assertThat(mappedStatement.getResultSetType()).isNull(); + } + + @Test + public void withoutOptionsWhenSpecifyDefaultValue() throws Exception { + Configuration configuration = new Configuration(); + configuration.setDefaultResultSetType(ResultSetType.SCROLL_INSENSITIVE); + MapperAnnotationBuilder builder = new MapperAnnotationBuilder(configuration, Mapper.class); + builder.parse(); + + MappedStatement mappedStatement = configuration.getMappedStatement("selectWithoutOptions"); + assertThat(mappedStatement.getResultSetType()).isEqualTo(ResultSetType.SCROLL_INSENSITIVE); + } + + @Test + public void withoutOptionsWhenNotSpecifyDefaultValue() throws Exception { + Configuration configuration = new Configuration(); + MapperAnnotationBuilder builder = new MapperAnnotationBuilder(configuration, Mapper.class); + builder.parse(); + + MappedStatement mappedStatement = configuration.getMappedStatement("selectWithoutOptions"); + assertThat(mappedStatement.getResultSetType()).isNull(); + } + + public interface Mapper { + + @Insert("insert into test (name) values(#{name})") + @Options(useGeneratedKeys = true, keyColumn = "key_column", keyProperty = "keyProperty") + void insertWithOptions(String name); + + @Select("select * from test") + @Options(fetchSize = 200, timeout = 10, statementType = StatementType.STATEMENT, resultSetType = ResultSetType.SCROLL_INSENSITIVE, flushCache = Options.FlushCachePolicy.TRUE, useCache = false, resultSets = "resultSets") + String selectWithOptions(Integer id); + + @Select("select * from test") + @Options + String selectWithOptionsAndWithoutOptionsAttributes(Integer id); + + @Select("select * from test") + String selectWithoutOptions(Integer id); + + } + +} diff --git a/src/test/java/org/apache/ibatis/builder/CustomizedSettingsMapperConfig.xml b/src/test/java/org/apache/ibatis/builder/CustomizedSettingsMapperConfig.xml index fd7bcd59f80..1452b2ec876 100644 --- a/src/test/java/org/apache/ibatis/builder/CustomizedSettingsMapperConfig.xml +++ b/src/test/java/org/apache/ibatis/builder/CustomizedSettingsMapperConfig.xml @@ -40,6 +40,7 @@ + diff --git a/src/test/java/org/apache/ibatis/builder/XmlConfigBuilderTest.java b/src/test/java/org/apache/ibatis/builder/XmlConfigBuilderTest.java index c281d2a0923..34d917303dd 100644 --- a/src/test/java/org/apache/ibatis/builder/XmlConfigBuilderTest.java +++ b/src/test/java/org/apache/ibatis/builder/XmlConfigBuilderTest.java @@ -42,6 +42,7 @@ import org.apache.ibatis.io.Resources; import org.apache.ibatis.logging.slf4j.Slf4jImpl; import org.apache.ibatis.mapping.Environment; +import org.apache.ibatis.mapping.ResultSetType; import org.apache.ibatis.scripting.defaults.RawLanguageDriver; import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver; import org.apache.ibatis.session.AutoMappingBehavior; @@ -88,6 +89,7 @@ public void shouldSuccessfullyLoadMinimalXMLConfigFile() throws Exception { assertThat(config.getDefaultExecutorType()).isEqualTo(ExecutorType.SIMPLE); assertNull(config.getDefaultStatementTimeout()); assertNull(config.getDefaultFetchSize()); + assertNull(config.getDefaultResultSetType()); assertThat(config.isMapUnderscoreToCamelCase()).isFalse(); assertThat(config.isSafeRowBoundsEnabled()).isFalse(); assertThat(config.getLocalCacheScope()).isEqualTo(LocalCacheScope.SESSION); @@ -181,6 +183,7 @@ public void shouldSuccessfullyLoadXMLConfigFile() throws Exception { assertThat(config.getDefaultExecutorType()).isEqualTo(ExecutorType.BATCH); assertThat(config.getDefaultStatementTimeout()).isEqualTo(10); assertThat(config.getDefaultFetchSize()).isEqualTo(100); + assertThat(config.getDefaultResultSetType()).isEqualTo(ResultSetType.SCROLL_INSENSITIVE); assertThat(config.isMapUnderscoreToCamelCase()).isTrue(); assertThat(config.isSafeRowBoundsEnabled()).isTrue(); assertThat(config.getLocalCacheScope()).isEqualTo(LocalCacheScope.STATEMENT); diff --git a/src/test/java/org/apache/ibatis/builder/XmlMapperBuilderTest.java b/src/test/java/org/apache/ibatis/builder/XmlMapperBuilderTest.java index 5719eda30aa..2b768cc0247 100644 --- a/src/test/java/org/apache/ibatis/builder/XmlMapperBuilderTest.java +++ b/src/test/java/org/apache/ibatis/builder/XmlMapperBuilderTest.java @@ -62,6 +62,20 @@ public void mappedStatementWithOptions() throws Exception { inputStream.close(); } + @Test + public void mappedStatementWithoutOptionsWhenSpecifyDefaultValue() throws Exception { + Configuration configuration = new Configuration(); + configuration.setDefaultResultSetType(ResultSetType.SCROLL_INSENSITIVE); + String resource = "org/apache/ibatis/builder/AuthorMapper.xml"; + InputStream inputStream = Resources.getResourceAsStream(resource); + XMLMapperBuilder builder = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); + builder.parse(); + inputStream.close(); + + MappedStatement mappedStatement = configuration.getMappedStatement("selectAuthor"); + assertThat(mappedStatement.getResultSetType()).isEqualTo(ResultSetType.SCROLL_INSENSITIVE); + } + @Test public void parseExpression() { BaseBuilder builder = new BaseBuilder(new Configuration()){{}}; From a154e545a6e65dd1bb886aea4569bd5bdd7110a0 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Mon, 23 Oct 2017 22:57:33 +0900 Subject: [PATCH 0007/2062] refs #1110 Do not try to close statetment if it is already closed as suggested by Kazuki. --- src/main/java/org/apache/ibatis/executor/BaseExecutor.java | 6 ++++-- src/main/java/org/apache/ibatis/executor/BatchExecutor.java | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/apache/ibatis/executor/BaseExecutor.java b/src/main/java/org/apache/ibatis/executor/BaseExecutor.java index 493c192f241..dd76cfc7a8a 100644 --- a/src/main/java/org/apache/ibatis/executor/BaseExecutor.java +++ b/src/main/java/org/apache/ibatis/executor/BaseExecutor.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2016 the original author or authors. + * Copyright 2009-2017 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. @@ -282,7 +282,9 @@ protected abstract Cursor doQueryCursor(MappedStatement ms, Object parame protected void closeStatement(Statement statement) { if (statement != null) { try { - statement.close(); + if (!statement.isClosed()) { + statement.close(); + } } catch (SQLException e) { // ignore } diff --git a/src/main/java/org/apache/ibatis/executor/BatchExecutor.java b/src/main/java/org/apache/ibatis/executor/BatchExecutor.java index 384aa3503cd..b2014723d88 100644 --- a/src/main/java/org/apache/ibatis/executor/BatchExecutor.java +++ b/src/main/java/org/apache/ibatis/executor/BatchExecutor.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2016 the original author or authors. + * Copyright 2009-2017 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. @@ -131,6 +131,7 @@ public List doFlushStatements(boolean isRollback) throws SQLExcepti keyGenerator.processAfter(this, ms, stmt, parameter); } } + // Close statement to close cursor #1109 closeStatement(stmt); } catch (BatchUpdateException e) { StringBuilder message = new StringBuilder(); From f1b04586974d64a59717be62d8a6489eb76863f6 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Mon, 23 Oct 2017 23:22:29 +0900 Subject: [PATCH 0008/2062] Updated license year. --- src/site/ko/xdoc/statement-builders.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/site/ko/xdoc/statement-builders.xml b/src/site/ko/xdoc/statement-builders.xml index 6fa882a9aed..de9c5cf238f 100644 --- a/src/site/ko/xdoc/statement-builders.xml +++ b/src/site/ko/xdoc/statement-builders.xml @@ -1,7 +1,7 @@ From b9b50abb83c8e386fb9ce8012b907293531ec211 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Thu, 9 Nov 2017 02:36:30 +0900 Subject: [PATCH 0017/2062] Supports CharSequence on SQL provider method return type Fixes gh-1134 --- .../builder/annotation/ProviderSqlSource.java | 21 +++++++++---------- .../sqlprovider/SqlProviderTest.java | 14 +++++++------ 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/apache/ibatis/builder/annotation/ProviderSqlSource.java b/src/main/java/org/apache/ibatis/builder/annotation/ProviderSqlSource.java index f33fdf90633..98079b31e44 100644 --- a/src/main/java/org/apache/ibatis/builder/annotation/ProviderSqlSource.java +++ b/src/main/java/org/apache/ibatis/builder/annotation/ProviderSqlSource.java @@ -63,17 +63,15 @@ public ProviderSqlSource(Configuration configuration, Object provider, Class providerMethodName = (String) provider.getClass().getMethod("method").invoke(provider); for (Method m : this.providerType.getMethods()) { - if (providerMethodName.equals(m.getName())) { - if (m.getReturnType() == String.class) { - if (providerMethod != null){ - throw new BuilderException("Error creating SqlSource for SqlProvider. Method '" - + providerMethodName + "' is found multiple in SqlProvider '" + this.providerType.getName() - + "'. Sql provider method can not overload."); - } - this.providerMethod = m; - this.providerMethodArgumentNames = new ParamNameResolver(configuration, m).getNames(); - this.providerMethodParameterTypes = m.getParameterTypes(); + if (providerMethodName.equals(m.getName()) && CharSequence.class.isAssignableFrom(m.getReturnType())) { + if (providerMethod != null){ + throw new BuilderException("Error creating SqlSource for SqlProvider. Method '" + + providerMethodName + "' is found multiple in SqlProvider '" + this.providerType.getName() + + "'. Sql provider method can not overload."); } + this.providerMethod = m; + this.providerMethodArgumentNames = new ParamNameResolver(configuration, m).getNames(); + this.providerMethodParameterTypes = m.getParameterTypes(); } } } catch (BuilderException e) { @@ -166,7 +164,8 @@ private String invokeProviderMethod(Object... args) throws Exception { if (!Modifier.isStatic(providerMethod.getModifiers())) { targetObject = providerType.newInstance(); } - return (String) providerMethod.invoke(targetObject, args); + CharSequence sql = (CharSequence) providerMethod.invoke(targetObject, args); + return sql != null ? sql.toString() : null; } private String replacePlaceholder(String sql) { diff --git a/src/test/java/org/apache/ibatis/submitted/sqlprovider/SqlProviderTest.java b/src/test/java/org/apache/ibatis/submitted/sqlprovider/SqlProviderTest.java index 857d082c2b0..f2541b38aed 100644 --- a/src/test/java/org/apache/ibatis/submitted/sqlprovider/SqlProviderTest.java +++ b/src/test/java/org/apache/ibatis/submitted/sqlprovider/SqlProviderTest.java @@ -587,22 +587,24 @@ public interface StaticMethodSqlProviderMapper { String oneArgumentAndProviderContext(Integer value); class SqlProvider { + public static String noArgument() { return "SELECT 1 FROM INFORMATION_SCHEMA.SYSTEM_USERS"; } - public static String oneArgument(Integer value) { - return "SELECT " + value + " FROM INFORMATION_SCHEMA.SYSTEM_USERS"; + public static StringBuilder oneArgument(Integer value) { + return new StringBuilder().append("SELECT ").append(value) + .append(" FROM INFORMATION_SCHEMA.SYSTEM_USERS"); } - public static String multipleArgument(@Param("value1") Integer value1, + public static CharSequence multipleArgument(@Param("value1") Integer value1, @Param("value2") Integer value2) { return "SELECT " + (value1 + value2) + " FROM INFORMATION_SCHEMA.SYSTEM_USERS"; } - public static String onlyProviderContext(ProviderContext context) { - return "SELECT '" + context.getMapperMethod().getName() - + "' FROM INFORMATION_SCHEMA.SYSTEM_USERS"; + public static CharSequence onlyProviderContext(ProviderContext context) { + return new StringBuilder().append("SELECT '").append(context.getMapperMethod().getName()) + .append("' FROM INFORMATION_SCHEMA.SYSTEM_USERS"); } public static String oneArgumentAndProviderContext(Integer value, ProviderContext context) { From 65769382ea5ce651ec123775532dc68d5e6520a4 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Sat, 11 Nov 2017 17:16:53 +0900 Subject: [PATCH 0018/2062] fixes #493 Apply ResultHandler on REFCURSOR OUT parameters. --- .../apache/ibatis/binding/MapperMethod.java | 8 +++-- .../resultset/DefaultResultSetHandler.java | 10 ++++-- .../usesjava8/refcursor/CreateDB.sql | 14 ++++++++ .../usesjava8/refcursor/OrdersMapper.java | 9 +++-- .../usesjava8/refcursor/OrdersMapper.xml | 14 ++++++++ .../usesjava8/refcursor/RefCursorTest.java | 36 +++++++++++++++++-- 6 files changed, 81 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/apache/ibatis/binding/MapperMethod.java b/src/main/java/org/apache/ibatis/binding/MapperMethod.java index b5d2b7abe01..6e58e876abc 100644 --- a/src/main/java/org/apache/ibatis/binding/MapperMethod.java +++ b/src/main/java/org/apache/ibatis/binding/MapperMethod.java @@ -20,6 +20,7 @@ import org.apache.ibatis.cursor.Cursor; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; +import org.apache.ibatis.mapping.StatementType; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.ParamNameResolver; import org.apache.ibatis.reflection.TypeParameterResolver; @@ -113,9 +114,10 @@ private Object rowCountResult(int rowCount) { private void executeWithResultHandler(SqlSession sqlSession, Object[] args) { MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName()); - if (void.class.equals(ms.getResultMaps().get(0).getType())) { - throw new BindingException("method " + command.getName() - + " needs either a @ResultMap annotation, a @ResultType annotation," + if (!StatementType.CALLABLE.equals(ms.getStatementType()) + && void.class.equals(ms.getResultMaps().get(0).getType())) { + throw new BindingException("method " + command.getName() + + " needs either a @ResultMap annotation, a @ResultType annotation," + " or a resultType attribute in XML so a ResultHandler can be used as a parameter."); } Object param = method.convertArgsToSqlCommandParam(args); 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 06124cc3e95..b9beca4ac9b 100644 --- a/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java +++ b/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java @@ -162,10 +162,14 @@ private void handleRefCursorOutputParameter(ResultSet rs, ParameterMapping param try { final String resultMapId = parameterMapping.getResultMapId(); final ResultMap resultMap = configuration.getResultMap(resultMapId); - final DefaultResultHandler resultHandler = new DefaultResultHandler(objectFactory); final ResultSetWrapper rsw = new ResultSetWrapper(rs, configuration); - handleRowValues(rsw, resultMap, resultHandler, new RowBounds(), null); - metaParam.setValue(parameterMapping.getProperty(), resultHandler.getResultList()); + if (this.resultHandler == null) { + final DefaultResultHandler resultHandler = new DefaultResultHandler(objectFactory); + handleRowValues(rsw, resultMap, resultHandler, new RowBounds(), null); + metaParam.setValue(parameterMapping.getProperty(), resultHandler.getResultList()); + } else { + handleRowValues(rsw, resultMap, resultHandler, new RowBounds(), null); + } } finally { // issue #228 (close resultsets) closeResultSet(rs); diff --git a/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/CreateDB.sql b/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/CreateDB.sql index 06a461f39a3..6bbae6fb662 100644 --- a/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/CreateDB.sql +++ b/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/CreateDB.sql @@ -78,6 +78,20 @@ $BODY$ LANGUAGE plpgsql VOLATILE COST 100 | + +CREATE OR REPLACE FUNCTION mbtest.get_order_out_params( + order_number integer, + detail_count out integer, + header_curs out refcursor +) AS $BODY$ +BEGIN + open header_curs for select * from mbtest.order_header where order_id = ORDER_NUMBER; + select count(*) into detail_count from mbtest.order_detail where order_id = ORDER_NUMBER; +END; +$BODY$ + LANGUAGE plpgsql VOLATILE + COST 100 | + -- @DELIMITER ; ALTER FUNCTION mbtest.get_order(integer) OWNER TO postgres; diff --git a/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/OrdersMapper.java b/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/OrdersMapper.java index 15e897c3526..a65d7ed9c4c 100644 --- a/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/OrdersMapper.java +++ b/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/OrdersMapper.java @@ -17,7 +17,12 @@ import java.util.Map; +import org.apache.ibatis.session.ResultHandler; + public interface OrdersMapper { - void getOrder1(Map parameter); - void getOrder2(Map parameter); + void getOrder1(Map parameter); + + void getOrder2(Map parameter); + + void getOrder3(Map parameter, ResultHandler resultHandler); } diff --git a/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/OrdersMapper.xml b/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/OrdersMapper.xml index 460c57069f6..01aaa6b1f07 100644 --- a/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/OrdersMapper.xml +++ b/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/OrdersMapper.xml @@ -40,4 +40,18 @@ { #{order,jdbcType=OTHER,mode=OUT,resultMap=org.apache.ibatis.submitted.usesjava8.refcursor.OrdersMapper.OrderResult,javaType=java.sql.ResultSet} = call mbtest.get_order(#{orderId,jdbcType=INTEGER,mode=IN}) } + + + + + + + + diff --git a/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/RefCursorTest.java b/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/RefCursorTest.java index 33e957ac328..99ba0871baf 100644 --- a/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/RefCursorTest.java +++ b/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/RefCursorTest.java @@ -15,13 +15,13 @@ */ package org.apache.ibatis.submitted.usesjava8.refcursor; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.*; import java.io.IOException; import java.io.Reader; import java.nio.file.Paths; import java.sql.Connection; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -32,6 +32,8 @@ import org.apache.ibatis.jdbc.ScriptRunner; import org.apache.ibatis.mapping.Environment; import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.session.ResultContext; +import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; @@ -114,4 +116,34 @@ public void testRefCursor2() throws IOException { assertEquals(3, order.getDetailLines().size()); } } + + @Test + public void shouldUseResultHandlerOnOutputParam() throws IOException { + class OrderResultHandler implements ResultHandler { + private List orders = new ArrayList(); + + @Override + public void handleResult(ResultContext resultContext) { + Order order = resultContext.getResultObject(); + order.setCustomerName("Anonymous"); + orders.add(order); + } + + List getResult() { + return orders; + } + } + + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class); + OrderResultHandler handler = new OrderResultHandler(); + Map parameter = new HashMap(); + parameter.put("orderId", 1); + mapper.getOrder3(parameter, handler); + + assertNull(parameter.get("order")); + assertEquals(Integer.valueOf(3), parameter.get("detailCount")); + assertEquals("Anonymous", handler.getResult().get(0).getCustomerName()); + } + } } From cfa087452229f66bcdaeecb67b6dc2490742ee33 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Wed, 15 Nov 2017 01:07:34 +0900 Subject: [PATCH 0019/2062] Update document See gh-1130 gh-1134 --- src/site/es/xdoc/java-api.xml | 9 +++++---- src/site/ja/xdoc/java-api.xml | 9 +++++---- src/site/ko/xdoc/java-api.xml | 8 ++++---- src/site/xdoc/java-api.xml | 11 ++++++----- src/site/zh/xdoc/java-api.xml | 8 ++++---- 5 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/site/es/xdoc/java-api.xml b/src/site/es/xdoc/java-api.xml index 3d00c479f03..2e5ebacae20 100644 --- a/src/site/es/xdoc/java-api.xml +++ b/src/site/es/xdoc/java-api.xml @@ -445,7 +445,8 @@ try (SqlSession session = sqlSessionFactory.openSession()) {
  • <select>
  • - Estas anotaciones SQL alternativas te permiten especificar un nombre de clases y un método que devolverán la SQL que debe ejecutarse. Cuando se ejecute el método MyBatis instanciará la clase y ejecutará el método especificados en el provider. You can pass objects that passed to arguments of a mapper method, "Mapper interface type" and "Mapper method" + Estas anotaciones SQL alternativas te permiten especificar un nombre de clases y un método que devolverán la SQL que debe ejecutarse (Since 3.4.6, you can specify the CharSequence instead of String as a method return type). + Cuando se ejecute el método MyBatis instanciará la clase y ejecutará el método especificados en el provider. You can pass objects that passed to arguments of a mapper method, "Mapper interface type" and "Mapper method" via the ProviderContext(available since MyBatis 3.4.5 or later) as method argument.(In MyBatis 3.4 or later, it's allow multiple parameters) Atributos: type, method. El atributo type es el nombre completamente cualificado de una clase. El method es el nombre un método de dicha clase. Nota: A continuación hay una sección sobre la clase, que puede ayudar a construir SQL dinámico de una forma más clara y sencilla de leer. @@ -524,7 +525,7 @@ Company getCompanyById(Integer id); List getUsersByName(String name); class UserSqlBuilder { - public String buildGetUsersByName(final String name) { + public static String buildGetUsersByName(final String name) { return new SQL(){{ SELECT("*"); FROM("users"); @@ -544,7 +545,7 @@ List getUsersByName( class UserSqlBuilder { // If not use @Param, you should be define same arguments with mapper method - public String buildGetUsersByName( + public static String buildGetUsersByName( final String name, final String orderByColumn) { return new SQL(){{ SELECT("*"); @@ -555,7 +556,7 @@ class UserSqlBuilder { } // If use @Param, you can define only arguments to be used - public String buildGetUsersByName(@Param("orderByColumn") final String orderByColumn) { + public static String buildGetUsersByName(@Param("orderByColumn") final String orderByColumn) { return new SQL(){{ SELECT("*"); FROM("users"); diff --git a/src/site/ja/xdoc/java-api.xml b/src/site/ja/xdoc/java-api.xml index faafb3b1763..b72c27ca365 100644 --- a/src/site/ja/xdoc/java-api.xml +++ b/src/site/ja/xdoc/java-api.xml @@ -464,7 +464,8 @@ try (SqlSession session = sqlSessionFactory.openSession()) {
  • <select>
  • - これらのアノテーションは動的 SQL を生成するためのものです。実行時に指定されたメソッドが呼び出され、メソッドから返された SQL ステートメントが実行されます。マップドステートメントを実行する際、プロバイダーによって指定したクラスのインスタンスが作成され、指定されたメソッドが実行されます。 + これらのアノテーションは動的 SQL を生成するためのものです。実行時に指定されたメソッドが呼び出され、メソッドから返された SQL ステートメントが実行されます (MyBatis 3.4.6以降では、メソッドの返り値として String ではなく CharSequence を指定することができます)。 + マップドステートメントを実行する際、プロバイダーによって指定したクラスのインスタンスが作成され、指定されたメソッドが実行されます。 なお、メソッド引数にはMapperメソッドの引数に渡したオブジェクトに加え、ProviderContext(MyBatis 3.4.5以降で利用可能)を介して「Mapperインタフェースの型」と「Mapperメソッド」を渡すことができます。(MyBatis 3.4以降では、複数の引数を渡すことができます) キー: type, method. type にはクラスオブジェクト、method にはメソッド名を指定します。 NOTE 次の章で、クリーンで可読性の高いコードで動的 SQL を構築するためのクラスについて説明します。 @@ -541,7 +542,7 @@ Company getCompanyById(Integer id); List getUsersByName(String name); class UserSqlBuilder { - public String buildGetUsersByName(final String name) { + public static String buildGetUsersByName(final String name) { return new SQL(){{ SELECT("*"); FROM("users"); @@ -561,7 +562,7 @@ List getUsersByName( class UserSqlBuilder { // @Paramを使わない場合は, Mapperメソッドと同じ引数で定義する必要があります。 - public String buildGetUsersByName( + public static String buildGetUsersByName( final String name, final String orderByColumn) { return new SQL(){{ SELECT("*"); @@ -572,7 +573,7 @@ class UserSqlBuilder { } // @Paramを使う場合は, 必要な引数のみ定義することができます。 - public String buildGetUsersByName(@Param("orderByColumn") final String orderByColumn) { + public static String buildGetUsersByName(@Param("orderByColumn") final String orderByColumn) { return new SQL(){{ SELECT("*"); FROM("users"); diff --git a/src/site/ko/xdoc/java-api.xml b/src/site/ko/xdoc/java-api.xml index ea0810606ab..320d8e65a67 100644 --- a/src/site/ko/xdoc/java-api.xml +++ b/src/site/ko/xdoc/java-api.xml @@ -579,7 +579,7 @@ try (SqlSession session = sqlSessionFactory.openSession()) {
  • <select>
  • - 실행시 SQL 을 리턴할 클래스 과 메소드명을 명시하도록 해주는 대체수단의 애노테이션이다. + 실행시 SQL 을 리턴할 클래스 과 메소드명을 명시하도록 해주는 대체수단의 애노테이션이다 (Since 3.4.6, you can specify the CharSequence instead of String as a method return type). 매핑된 구문을 실행할 때 마이바티스는 클래스의 인스턴스를 만들고 메소드를 실행한다. Mapper 메서드의 인수인 "Mapper interface type" 과 ProviderContext(Mybatis 3.4.5 부터) 를 이용한 "Mapper method" 로 전달 된 객체를 메서드 매개변수로 전달할 수 있다.(마이바티스 3.4이상에서는 복수 파라미터를 허용한다.) 사용가능한 속성들 : type, method. @@ -676,7 +676,7 @@ Company getCompanyById(Integer id); List getUsersByName(String name); class UserSqlBuilder { - public String buildGetUsersByName(final String name) { + public static String buildGetUsersByName(final String name) { return new SQL(){{ SELECT("*"); FROM("users"); @@ -696,7 +696,7 @@ List getUsersByName( class UserSqlBuilder { // @Param애노테이션을 사용하지 않으면 매퍼 메소드와 동일한 인자를 정의해야만 한다. - public String buildGetUsersByName( + public static String buildGetUsersByName( final String name, final String orderByColumn) { return new SQL(){{ SELECT("*"); @@ -707,7 +707,7 @@ class UserSqlBuilder { } // @Param애노테이션을 사용한다면, 오직 사용할 인자만 정의할 수 있다. - public String buildGetUsersByName(@Param("orderByColumn") final String orderByColumn) { + public static String buildGetUsersByName(@Param("orderByColumn") final String orderByColumn) { return new SQL(){{ SELECT("*"); FROM("users"); diff --git a/src/site/xdoc/java-api.xml b/src/site/xdoc/java-api.xml index 5d6f1af21c6..117af600cec 100644 --- a/src/site/xdoc/java-api.xml +++ b/src/site/xdoc/java-api.xml @@ -496,8 +496,9 @@ try (SqlSession session = sqlSessionFactory.openSession()) { Allows for creation of dynamic SQL. These alternative SQL annotations allow you to specify a class and - a method name that will return the SQL to run at execution time. Upon executing the mapped statement, MyBatis will - instantiate the class, and execute the method, as specified by the provider. + a method name that will return the SQL to run at execution time + (Since 3.4.6, you can specify the CharSequence instead of String as a method return type). + Upon executing the mapped statement, MyBatis will instantiate the class, and execute the method, as specified by the provider. You can pass objects that passed to arguments of a mapper method, "Mapper interface type" and "Mapper method" via the ProviderContext(available since MyBatis 3.4.5 or later) as method argument. (In MyBatis 3.4 or later, it's allow multiple parameters) @@ -595,7 +596,7 @@ Company getCompanyById(Integer id); List getUsersByName(String name); class UserSqlBuilder { - public String buildGetUsersByName(final String name) { + public static String buildGetUsersByName(final String name) { return new SQL(){{ SELECT("*"); FROM("users"); @@ -615,7 +616,7 @@ List getUsersByName( class UserSqlBuilder { // If not use @Param, you should be define same arguments with mapper method - public String buildGetUsersByName( + public static String buildGetUsersByName( final String name, final String orderByColumn) { return new SQL(){{ SELECT("*"); @@ -626,7 +627,7 @@ class UserSqlBuilder { } // If use @Param, you can define only arguments to be used - public String buildGetUsersByName(@Param("orderByColumn") final String orderByColumn) { + public static String buildGetUsersByName(@Param("orderByColumn") final String orderByColumn) { return new SQL(){{ SELECT("*"); FROM("users"); diff --git a/src/site/zh/xdoc/java-api.xml b/src/site/zh/xdoc/java-api.xml index 8fc43e54c60..b2db1718639 100644 --- a/src/site/zh/xdoc/java-api.xml +++ b/src/site/zh/xdoc/java-api.xml @@ -747,7 +747,7 @@ resultSets=””。 这些可选的 SQL 注解允许你指定一个 类名和一个方法在执行时来返回运行 允许创建动态 -的 SQL。 +的 SQL (Since 3.4.6, you can specify the CharSequence instead of String as a method return type)。 基于执行的映射语句, MyBatis 会实例化这个类,然后执行由 provider @@ -860,7 +860,7 @@ Company getCompanyById(Integer id); List getUsersByName(String name); class UserSqlBuilder { - public String buildGetUsersByName(final String name) { + public static String buildGetUsersByName(final String name) { return new SQL(){{ SELECT("*"); FROM("users"); @@ -880,7 +880,7 @@ List getUsersByName( class UserSqlBuilder { // If not use @Param, you should be define same arguments with mapper method - public String buildGetUsersByName( + public static String buildGetUsersByName( final String name, final String orderByColumn) { return new SQL(){{ SELECT("*"); @@ -891,7 +891,7 @@ class UserSqlBuilder { } // If use @Param, you can define only arguments to be used - public String buildGetUsersByName(@Param("orderByColumn") final String orderByColumn) { + public static String buildGetUsersByName(@Param("orderByColumn") final String orderByColumn) { return new SQL(){{ SELECT("*"); FROM("users"); From 1edff06bb46c518c88d4a8979caa0064ee39ce3d Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Wed, 15 Nov 2017 03:35:50 +0900 Subject: [PATCH 0020/2062] refs #492 Added a test case. Thanks to Erwan Letessier. https://groups.google.com/d/msg/mybatis-user/dqHzZjyZ4zo/E50uPsCgAQAJ --- .../submitted/usesjava8/refcursor/CreateDB.sql | 7 ++++++- .../usesjava8/refcursor/RefCursorTest.java | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/CreateDB.sql b/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/CreateDB.sql index 6bbae6fb662..0dfd45501ef 100644 --- a/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/CreateDB.sql +++ b/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/CreateDB.sql @@ -84,8 +84,13 @@ CREATE OR REPLACE FUNCTION mbtest.get_order_out_params( detail_count out integer, header_curs out refcursor ) AS $BODY$ +DECLARE + order_exists boolean; BEGIN - open header_curs for select * from mbtest.order_header where order_id = ORDER_NUMBER; + select order_id is not null into order_exists from mbtest.order_header where order_id = ORDER_NUMBER; + if order_exists then + open header_curs for select * from mbtest.order_header where order_id = ORDER_NUMBER; + end if; select count(*) into detail_count from mbtest.order_detail where order_id = ORDER_NUMBER; END; $BODY$ diff --git a/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/RefCursorTest.java b/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/RefCursorTest.java index 99ba0871baf..631e813e688 100644 --- a/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/RefCursorTest.java +++ b/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/RefCursorTest.java @@ -146,4 +146,20 @@ List getResult() { assertEquals("Anonymous", handler.getResult().get(0).getCustomerName()); } } + + @Test + public void shouldNullResultSetNotCauseNpe() throws IOException { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class); + Map parameter = new HashMap(); + parameter.put("orderId", 99); + mapper.getOrder3(parameter, new ResultHandler() { + @Override + public void handleResult(ResultContext resultContext) { + // won't be used + } + }); + assertEquals(Integer.valueOf(0), parameter.get("detailCount")); + } + } } From 149cb01053d31c8e5dad6ba36cbaae1480e81944 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Wed, 15 Nov 2017 04:28:44 +0900 Subject: [PATCH 0021/2062] Added a few missing lines in the doc. --- src/site/es/xdoc/java-api.xml | 7 +++++++ src/site/ko/xdoc/java-api.xml | 7 +++++++ src/site/zh/xdoc/java-api.xml | 7 +++++++ 3 files changed, 21 insertions(+) diff --git a/src/site/es/xdoc/java-api.xml b/src/site/es/xdoc/java-api.xml index 2e5ebacae20..6b4236a8335 100644 --- a/src/site/es/xdoc/java-api.xml +++ b/src/site/es/xdoc/java-api.xml @@ -223,6 +223,13 @@ 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.

    +

    Using a ResultHandler has two limitations that you should be aware of:

    + +
      +
    • Data got from an method called with a ResultHandler will not be cached.
    • +
    • 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()]]> diff --git a/src/site/ko/xdoc/java-api.xml b/src/site/ko/xdoc/java-api.xml index 320d8e65a67..cf6de6d2524 100644 --- a/src/site/ko/xdoc/java-api.xml +++ b/src/site/ko/xdoc/java-api.xml @@ -293,6 +293,13 @@ public interface ResultHandler {

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

    +

    Using a ResultHandler has two limitations that you should be aware of:

    + +
      +
    • Data got from an method called with a ResultHandler will not be cached.
    • +
    • 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.
    • +
    +
    배치 수정시 flush메소드

    어떤 시점에 JDBC드라이버 클래스에 저장된 배치 수정구문을 지울(flushing(executing)) 방법이 있다. 이 방법은 ExecutorTypeExecutorType.BATCH로 설정한 경우 사용가능하다.

    diff --git a/src/site/zh/xdoc/java-api.xml b/src/site/zh/xdoc/java-api.xml index b2db1718639..bb9e4b5254d 100644 --- a/src/site/zh/xdoc/java-api.xml +++ b/src/site/zh/xdoc/java-api.xml @@ -364,6 +364,13 @@ public interface ResultHandler { 尔返回值的 stop()方法来停止 MyBatis 加载更多的结果。

    +

    Using a ResultHandler has two limitations that you should be aware of:

    + +
      +
    • Data got from an method called with a ResultHandler will not be cached.
    • +
    • 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.
    • +
    +
    批量立即更新方法(Flush Method)

    有一个方法可以刷新(执行)存储在JDBC驱动类中的批量更新语句。当你将ExecutorType.BATCH作为ExecutorType使用时可以采用此方法。

    flushStatements()]]> From 361e497951defdfae0b1f30aa152e65902911515 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Wed, 15 Nov 2017 04:30:12 +0900 Subject: [PATCH 0022/2062] refs #493 Added a note on the doc. --- src/site/es/xdoc/java-api.xml | 1 + src/site/ja/xdoc/java-api.xml | 1 + src/site/ko/xdoc/java-api.xml | 1 + src/site/xdoc/java-api.xml | 1 + src/site/zh/xdoc/java-api.xml | 1 + 5 files changed, 5 insertions(+) diff --git a/src/site/es/xdoc/java-api.xml b/src/site/es/xdoc/java-api.xml index 6b4236a8335..fbe198014bd 100644 --- a/src/site/es/xdoc/java-api.xml +++ b/src/site/es/xdoc/java-api.xml @@ -215,6 +215,7 @@ RowBounds rowBounds = new RowBounds(offset, limit);

    El rendimiento de algunos drivers puede variar mucho en este aspecto. Para un rendimiento optimo, usa tipos de ResultSet SCROLL_SENSITIVE o SCROLL_INSENSITIVE (es decir, no FORWARD_ONLY)

    El parámetro ResultHandler te permite manejar cada fila como tú quieras. Puedes añadirla a una lista, crear un Map, un Set, o descartar cada resultado y guardar solo cálculos. Puedes hacer casi todo lo que quieras con un ResultHandler, de hecho, es lo que MyBatis usa internamente para construir listas de ResultSets.

    +

    Since 3.4.6, ResultHandler passed to a CALLABLE statement is used on every REFCURSOR output parameter of the stored procedure if there is any.

    La interfaz es muy sencilla:

    { diff --git a/src/site/ja/xdoc/java-api.xml b/src/site/ja/xdoc/java-api.xml index b72c27ca365..869ab4fecbf 100644 --- a/src/site/ja/xdoc/java-api.xml +++ b/src/site/ja/xdoc/java-api.xml @@ -216,6 +216,7 @@ RowBounds rowBounds = new RowBounds(offset, limit);

    ドライバーによって得られる効果は異なります。SCROLL_SENSITIVE または SCROLL_INSENSITIVE (つまり FORWARD_ONLY 以外)の結果セットタイプを使った時、最も良いパフォーマンスが得られます。

    ResultHandler を渡すと、各行を自由に処理することができます。List に追加したり、Map や Set を作成することもできますし、結果を捨てて合計値のみを返すこともできます。ResultHandler を使えば好きな処理を行うことも可能で、MyBatis 自身も内部的に結果リストを構築するために ResultHandler を利用しています。

    +

    3.4.6 以降では、CALLABLE ステートメントに渡された ResultHandler は、指定されたストアド・プロシージャで宣言されている REFCURSOR 型の OUT 引数全てに対して適用されます。

    ResultHandler インターフェイスは非常にシンプルです。

    { diff --git a/src/site/ko/xdoc/java-api.xml b/src/site/ko/xdoc/java-api.xml index cf6de6d2524..9a685ed97d7 100644 --- a/src/site/ko/xdoc/java-api.xml +++ b/src/site/ko/xdoc/java-api.xml @@ -285,6 +285,7 @@ RowBounds rowBounds = new RowBounds(offset, limit);

    ResultHandler파라미터는 레코드별로 다룰수 있도록 해준다. List에 추가할수도 있고 Map, Set을 만들수도 있으며 각각의 결과를 그냥 던질수도 있다. ResultHandler로 많은 것을 할 수 있고 마이바티스는 결과셋을 다루기 위해 내부적으로 사용한다.

    +

    Since 3.4.6, ResultHandler passed to a CALLABLE statement is used on every REFCURSOR output parameter of the stored procedure if there is any.

    인터페이스는 매우 간단하다.

    { diff --git a/src/site/xdoc/java-api.xml b/src/site/xdoc/java-api.xml index 117af600cec..2bdf5709024 100644 --- a/src/site/xdoc/java-api.xml +++ b/src/site/xdoc/java-api.xml @@ -221,6 +221,7 @@ RowBounds rowBounds = new RowBounds(offset, limit);

    Different drivers are able to achieve different levels of efficiency in this regard. For the best performance, use result set types of SCROLL_SENSITIVE or SCROLL_INSENSITIVE (in other words: not FORWARD_ONLY).

    The ResultHandler parameter allows you to handle each row however you like. You can add it to a List, create a Map, Set, or throw each result away and instead keep only rolled up totals of calculations. You can do pretty much anything with the ResultHandler, and it's what MyBatis uses internally itself to build result set lists.

    +

    Since 3.4.6, ResultHandler passed to a CALLABLE statement is used on every REFCURSOR output parameter of the stored procedure if there is any.

    The interface is very simple.

    { diff --git a/src/site/zh/xdoc/java-api.xml b/src/site/zh/xdoc/java-api.xml index bb9e4b5254d..e813611e24a 100644 --- a/src/site/zh/xdoc/java-api.xml +++ b/src/site/zh/xdoc/java-api.xml @@ -351,6 +351,7 @@ Set 你可以使用 ResultHandler 做很多漂亮的事, 那就是 MyBatis 内部创建结果集列表。

    +

    Since 3.4.6, ResultHandler passed to a CALLABLE statement is used on every REFCURSOR output parameter of the stored procedure if there is any.

    它的接口很简单。

    { From 9773ed151cf074c79d7be2de74e763a90880cf6e Mon Sep 17 00:00:00 2001 From: MouseJW Date: Thu, 16 Nov 2017 17:46:08 +0800 Subject: [PATCH 0023/2062] =?UTF-8?q?=E6=9B=B4=E6=94=B9=E4=B8=AD=E6=96=87?= =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更改中文文档中拗口、未翻译、有错字的地方 --- src/site/zh/xdoc/configuration.xml | 24 +++---- src/site/zh/xdoc/getting-started.xml | 6 +- src/site/zh/xdoc/java-api.xml | 20 ++---- src/site/zh/xdoc/sqlmap-xml.xml | 97 ++++++++++++---------------- 4 files changed, 62 insertions(+), 85 deletions(-) diff --git a/src/site/zh/xdoc/configuration.xml b/src/site/zh/xdoc/configuration.xml index fa58ee6a4a5..69396a45632 100644 --- a/src/site/zh/xdoc/configuration.xml +++ b/src/site/zh/xdoc/configuration.xml @@ -62,7 +62,7 @@ ]]> -

    其中的属性就可以在整个配置文件中使用来替换需要动态配置的属性值。比如: +

    然后其中的属性就可以在整个配置文件中被用来替换需要动态配置的属性值。比如:

    @@ -146,7 +146,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ cacheEnabled - 该配置影响的所有映射器中配置的缓存的全局开关。 + 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。 true | false @@ -375,7 +375,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 - JdbcType enumeration. Most common are: NULL, VARCHAR and OTHER + JdbcType 常量. 大多都为: NULL, VARCHAR and OTHER OTHER @@ -389,7 +389,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ 指定哪个对象的方法触发一次延迟加载。 - A method name list separated by commas + 用逗号分隔的方法列表。 equals,clone,hashCode,toString @@ -403,7 +403,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ 指定动态 SQL 生成的默认语言。 - A type alias or fully qualified class name. + 一个类型别名或完全限定类名。 org.apache.ibatis.scripting.xmltags.XMLLanguageDriver @@ -414,10 +414,10 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ defaultEnumTypeHandler - Specifies the TypeHandler used by default for Enum. (Since: 3.4.5) + 指定 Enum 使用的默认 TypeHandler 。 (从3.4.5开始) - A type alias or fully qualified class name. + 一个类型别名或完全限定类名。 org.apache.ibatis.type.EnumTypeHandler @@ -461,7 +461,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ 指定 MyBatis 增加到日志名称的前缀。 - Any String + 任何字符串 Not set @@ -529,8 +529,8 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ configurationFactory - 指定一个提供Configuration实例的类. - 这个被返回的Configuration实例是用来加载被反序列化对象的懒加载属性值. + 指定一个提供Configuration实例的类。 + 这个被返回的Configuration实例用来加载被反序列化对象的懒加载属性值。 这个类必须包含一个签名方法static Configuration getConfiguration(). (从 3.2.3 版本开始) @@ -589,7 +589,7 @@ public class Author { ... } ]]> -

    已经为许多常见的 Java 类型内建了相应的类型别名。它们都是大小写不敏感的,需要注意的是由基本类型名称重复导致的特殊处理。

    +

    这是一些为常见的 Java 类型内建的相应的类型别名。它们都是大小写不敏感的,需要注意的是由基本类型名称重复导致的特殊处理。

    @@ -826,7 +826,7 @@ public class Author { 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器。

    NOTE - Since version 3.4.5, The MyBatis has been supported JSR-310(Date and Time API) by default. + 从 3.4.5 开始,MyBatis 默认支持 JSR-310(日期和时间 API) 。

    diff --git a/src/site/zh/xdoc/getting-started.xml b/src/site/zh/xdoc/getting-started.xml index 00ee212a476..280cadd4904 100644 --- a/src/site/zh/xdoc/getting-started.xml +++ b/src/site/zh/xdoc/getting-started.xml @@ -113,13 +113,13 @@ try { ]]>

    对于这个简单的例子来说似乎有点小题大做了,但实际上它是非常轻量级的。在一个 XML 映射文件中,你想定义多少个映射语句都是可以的,这样下来,XML 头部和文档类型声明占去的部分就显得微不足道了。文件的剩余部分具有很好的自解释性。在命名空间“org.mybatis.example.BlogMapper”中定义了一个名为“selectBlog”的映射语句,这样它就允许你使用指定的完全限定名“org.mybatis.example.BlogMapper.selectBlog”来调用映射语句,就像上面的例子中做的那样:

    -

    你可能注意到这和使用完全限定名调用 Java 对象的方法是相似的,之所以这样做是有原因的。这个命名可以直接映射到在命名空间中同名的 Mapper 类,并将已映射的 select 语句中的名字、参数和返回类型匹配成方法。这样你就可以向上面那样很容易地调用这个对应 Mapper 接口的方法。不过让我们再看一遍下面的例子:

    +

    你可能注意到这和使用完全限定名调用 Java 对象的方法是相似的,之所以这样做是有原因的。这个命名可以直接映射到在命名空间中同名的 Mapper 类,并将已映射的 select 语句中的名字、参数和返回类型匹配成方法。这样你就可以像上面那样很容易地调用这个对应 Mapper 接口的方法。不过让我们再看一遍下面的例子:

    第二种方法有很多优势,首先它不是基于字符串常量的,就会更安全;其次,如果你的 IDE 有代码补全功能,那么你可以在有了已映射 SQL 语句的基础之上利用这个功能。


    提示命名空间的一点注释

    -

    命名空间(Namespaces)在之前版本的 MyBatis 中是可选的,容易引起混淆因此是没有益处的。现在的命名空间则是必须的,目的是希望能比只是简单的使用更长的完全限定名来更进一步区分语句。

    +

    命名空间(Namespaces)在之前版本的 MyBatis 中是可选的,这样容易引起混淆因此毫无益处。现在命名空间则是必须的,且意于简单地用更长的完全限定名来隔离语句。

    命名空间使得你所见到的接口绑定成为可能,尽管你觉得这些东西未必用得上,你还是应该遵循这里的规定以防哪天你改变了主意。出于长远考虑,使用命名空间,并将它置于合适的 Java 包命名空间之下,你将拥有一份更加整洁的代码并提高了 MyBatis 的可用性。

    命名解析:为了减少输入量,MyBatis 对所有的命名配置元素(包括语句,结果映射,缓存等)使用了如下的命名解析规则。 @@ -162,7 +162,7 @@ try { }]]>

    在你的所有的代码中一致性地使用这种模式来保证所有数据库资源都能被正确地关闭。

    映射器实例(Mapper Instances)

    -

    映射器是创建用来绑定映射语句的接口。映射器接口的实例是从 SqlSession 中获得的。因此从技术层面讲,映射器实例的最大作用域是和 SqlSession 相同的,因为它们都是从 SqlSession 里被请求的。尽管如此,映射器实例的最佳作用域是方法作用域。也就是说,映射器实例应该在调用它们的方法中被请求,用过之后即可废弃。并不需要显式地关闭映射器实例,尽管在整个请求作用域(request scope)保持映射器实例也不会有什么问题,但是很快你会发现,像 SqlSession 一样,在这个作用域上管理太多的资源的话会难于控制。所以要保持简单,最好把映射器放在方法作用域(method scope)内。下面的示例就展示了这个实践:

    +

    映射器是一个你创建来绑定你映射的语句的接口。映射器接口的实例是从 SqlSession 中获得的。因此从技术层面讲,任何映射器实例的最大作用域是和请求它们的 SqlSession 相同的。尽管如此,映射器实例的最佳作用域是方法作用域。也就是说,映射器实例应该在调用它们的方法中被请求,用过之后即可废弃。并不需要显式地关闭映射器实例,尽管在整个请求作用域(request scope)保持映射器实例也不会有什么问题,但是很快你会发现,像 SqlSession 一样,在这个作用域上管理太多的资源的话会难于控制。所以要保持简单,最好把映射器放在方法作用域(method scope)内。下面的示例就展示了这个实践:

    SqlSessionFactory

    -SqlSessionFactory 有六个方法可以用来创建 SqlSession 实例。通常来说,如何决定是你 -选择下面这些方法时: +SqlSessionFactory 有六个方法可以用来创建 SqlSession 实例。通常来说,当你选择如下的一个方法时你将要作出决定:

    • Transaction (事务): 你想为 session 使用事务或者使用自动提交(通常意味着很多 数据库和/或 JDBC 驱动没有事务)?
    • -
    • Connection (连接): 你想 MyBatis 获得来自配置的数据源的连接还是提供你自己
    • +
    • Connection (连接): 你想 MyBatis 获得来自配置的数据源的连接还是提供你自己的
    • Execution (执行): 你想 MyBatis 复用预处理语句和/或批量更新语句(包括插入和 删除)?

    @@ -245,7 +244,7 @@ Configuration getConfiguration();

    • 会开启一个事务(也就是不自动提交)
    • -
    • 连接对象会从由活动环境配置的数据源实例中得到。
    • +
    • 将从由当前使用的环境配置的 DataSource 实例(数据源实例)获取中获取 Connection 对象(连接对象)
    • 事务隔离级别将会使用驱动或数据源的默认设置。
    • 预处理语句不会被复用,也不会批量处理更新。
    @@ -290,8 +289,7 @@ BLE)
    语句执行方法

    -这些方法被用来执行定义在 SQL 映射的 XML 文件中的 SELECT,INSERT,UPDA E -T +这些方法被用来执行定义在 SQL 映射的 XML 文件中的 SELECT,INSERT,UPDATE 和 DELETE 语句。它们都会自行解释,每一句都使用语句的 ID 属性和参数对象,参数可以 是原生类型(自动装箱或包装类) ,JavaBean,POJO 或 Map。 @@ -304,11 +302,7 @@ int update(String statement, Object parameter) int delete(String statement, Object parameter)]]>

    selectOne 和 selectList 的不同仅仅是 selectOne 必须返回一个对象。 -如果多余一个, -或者 -没有返回 -(或返回了 null) 那么就会抛出异常。 -, +如果多于一个,或者没有返回(或返回了 null) 那么就会抛出异常。 如果你不知道需要多少对象, 使用 selectList。

    @@ -342,7 +336,7 @@ int limit = 25; RowBounds rowBounds = new RowBounds(offset, limit);

    不同的驱动会实现这方面的不同级别的效率。对于最佳的表现,使用结果集类型的 -SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE(或句话说:不是 FORWARD_ONLY)。 +SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE(换句话说:不是 FORWARD_ONLY)。

    ResultHandler 参数允许你按你喜欢的方式处理每一行。你可以将它添加到 List 中,创 @@ -509,7 +503,7 @@ try (SqlSession session = sqlSessionFactory.openSession()) { 基础,也是新的基于注解配置的基础。注解提供了一种简单的方式来实现简单映射语句,而 不会引入大量的开销。

    -

    注意 不幸的是,Java 注解限制了它们的表现和灵活。尽管很多时间都花调查,设计和 +

    注意 不幸的是,Java 注解限制了它们的表现和灵活。尽管很多时间都花在调查,设计和 实验上,最强大的 MyBatis 映射不能用注解来构建,那并不可笑。C#属性(做示例)就没 有这些限制,因此 MyBatis.NET 将会比 XML 有更丰富的选择。也就是说,基于 Java 注解 的配置离不开它的特性。 diff --git a/src/site/zh/xdoc/sqlmap-xml.xml b/src/site/zh/xdoc/sqlmap-xml.xml index 40c279f4587..72d1c2206c0 100644 --- a/src/site/zh/xdoc/sqlmap-xml.xml +++ b/src/site/zh/xdoc/sqlmap-xml.xml @@ -339,7 +339,7 @@ ps.setInt(1,id);]]> ]]> -

    对于不支持自动生成类型的数据库或可能不支持自动生成主键 JDBC 驱动来说,MyBatis 有另外一种方法来生成主键。 +

    对于不支持自动生成类型的数据库或可能不支持自动生成主键的 JDBC 驱动,MyBatis 有另外一种方法来生成主键。

    这里有一个简单(甚至很傻)的示例,它可以生成一个随机 ID(你最好不要这么做,但这里展示了 MyBatis 处理问题的灵活性及其所关心的广度): @@ -420,7 +420,7 @@ ps.setInt(1,id);]]> ]]>

    - 属性值可以用于包含的refid属性或者包含的字句里面的属性值,例如: + 属性值也可以被用在 include 元素的 refid 属性里(]]>)或 include 内部语句中(),例如:

    @@ -463,7 +463,7 @@ ps.setInt(1,id);]]>

    如果 User 类型的参数对象传递到了语句中,id、username 和 password 属性将会被查找,然后将它们的值传入预处理语句的参数中。

    -

    这点对于向语句中传参是比较好的而且又简单,不过参数映射的功能远不止于此。 +

    这点相对于向语句中传参是比较好的,而且又简单,不过参数映射的功能远不止于此。

    首先,像 MyBatis 的其他部分一样,参数也可以指定一个特殊的数据类型。 @@ -471,10 +471,10 @@ ps.setInt(1,id);]]> -

    像 MyBatis 的剩余部分一样,javaType 通常可以从参数对象中来去确定,前提是只要对象不是一个 HashMap。那么 javaType 应该被确定来保证使用正确类型处理器。 +

    像 MyBatis 的剩余部分一样,javaType 通常可以由参数对象确定,除非该对象是一个 HashMap。这时所使用的 TypeHandler 应该明确指明 javaType。

    -

    NOTE 如果 null 被当作值来传递,对于所有可能为空的列,JDBC Type 是需要的。你可以自己通过阅读预处理语句的 setNull() 方法的 JavaDocs 文档来研究这种情况。 +

    NOTE 如果 null 被当作值来传递,对于所有可能为空的列,JDBC Type 是必需的。你可以通过自己阅读预处理语句的 setNull() 方法的 JavaDocs 文档来研究这种情况。

    为了以后定制类型处理方式,你也可以指定一个特殊的类型处理器类(或别名),比如: @@ -500,7 +500,7 @@ ps.setInt(1,id);]]> -

    尽管所有这些强大的选项很多时候你只简单指定属性名,其他的事情 MyBatis 会自己去推断,最多你需要为可能为空的列名指定 jdbcType。 +

    尽管所有这些选项很强大,但大多时候你只须简单地指定属性名,其他的事情 MyBatis 会自己去推断,你最多需要为可能为空的列名指定 jdbcType

    -resultMap 元素是 MyBatis 中最重要最强大的元素。它就是让你远离 90%的需要从结果 -集中取出数据的 JDBC 代码的那个东西, -而且在一些情形下允许你做一些 JDBC 不支持的事 -情。 +resultMap 元素是 MyBatis 中最重要最强大的元素。它就是让你远离 90%的需要从结果集中取出数据的 JDBC 代码的那个东西, +而且在一些情形下允许你做一些 JDBC 不支持的事情。 事实上, 编写相似于对复杂语句联合映射这些等同的代码, 也许可以跨过上千行的代码。 @@ -548,10 +546,10 @@ ResultMap 的设计就是简单语句不需要明确的结果映射,而很多复 ]]>

    -这样一个语句简单作用于所有列被自动映射到 HashMap 的键上,这由 resultType 属性 -指定。这在很多情况下是有用的,但是 HashMap 不能很好描述一个领域模型。那样你的应 -用程序将会使用 JavaBeans 或 POJOs(Plain Old Java Objects,普通 Java 对象)来作为领域 -模型。MyBatis 对两者都支持。看看下面这个 JavaBean: +这样一个语句只是简单地将结果自动映射到 HashMap 的键上,这由 resultType 属性指定。 +这在很多情况下是有用的,但是 HashMap 不能很好描述一个领域模型。 +那样你的应用程序将会使用 JavaBeans 或 POJOs(Plain Old Java Objects,普通 Java 对象)来作为领域模型。 +MyBatis 对两者都支持。看看下面这个 JavaBean:

    ]]>

    -ResultMap 最优秀的地方你已经了解了很多了,但是你还没有真正的看到一个。这些简 -单的示例不需要比你看到的更多东西。 +ResultMap 最优秀的地方你已经了解了很多了,但是你还没有真正的看到一个。 +这些简单的示例不需要比你看到的更多东西。 只是出于示例的原因, -让我们来看看最后一个示例中 -外部的 resultMap 是什么样子的,这也是解决列名不匹配的另外一种方式。 +让我们来看看最后一个示例中外部的 resultMap 是什么样子的,这也是解决列名不匹配的另外一种方式。

    @@ -654,10 +651,10 @@ ResultMap 最优秀的地方你已经了解了很多了,但是你还没有真正

    高级结果映射

    -MyBatis 创建的一个想法:数据库不用永远是你想要的或需要它们是什么样的。而我们 -最喜欢的数据库最好是第三范式或 BCNF 模式,但它们有时不是。如果可能有一个单独的 -数据库映射,所有应用程序都可以使用它,这是非常好的,但有时也不是。结果映射就是 -MyBatis 提供处理这个问题的答案。 +MyBatis MyBatis 创建的一个想法:数据库不用永远是你所想或所需的。 +而我们最喜欢的数据库最好是第三范式或 BCNF 模式,但它们有时不是。 +如果可能有一个单独的数据库映射,所有应用程序都可以使用它,这是非常好的,但有时也不是。 +结果映射就是结果映射就是 MyBatis 提供处理这个问题的答案。

    @@ -700,17 +697,12 @@ MyBatis 提供处理这个问题的答案。 ]]>

    -你可能想把它映射到一个智能的对象模型,包含一个作者写的博客,有很多的博文,每 -篇博文有零条或多条的评论和标签。 +你可能想把它映射到一个智能的对象模型,包含一个作者写的博客, +有很多的博文,每篇博文有零条或多条的评论和标签。 下面是一个完整的复杂结果映射例子 -(假设作者, -博客, -博文, -评论和标签都是类型的别名) 我们来看看, -。 -但是不用紧张, -我们会一步一步来说明。 -当天最初它看起来令人生畏,但实际上非常简单。 +(假设作者,博客,博文,评论和标签都是类型的别名) 我们来看看。 +但是不用紧张,我们会一步一步来说明。 +虽然它可能看起来令人望而生畏,但实际上非常简单。

    @@ -814,13 +806,11 @@ resultMap 元素有很多子元素和一个值得讨论的结构。

    - 最佳实践 通常逐步建立结果映射。单元测试的真正帮助在这里。如果你尝试创建 -一次创建一个向上面示例那样的巨大的结果映射, -那么可能会有错误而且很难去控制它 -来工作。开始简单一些,一步一步的发展。而且要进行单元测试!使用该框架的缺点是 -它们有时是黑盒(是否可见源代码) -。你确定你实现想要的行为的最好选择是编写单元 -测试。它也可以你帮助得到提交时的错误。 + 最佳实践 +通常逐步建立结果映射。单元测试的真正帮助在这里。如果你尝试一次创建一个像上面示例那样的巨大的结果映射, +那么可能会有错误而且很难去控制它来工作。 +开始时简单一些,一步一步的发展。而且要进行单元测试!使用该框架的缺点是它们有时是黑盒(是否可见源代码)。 +你确定你实现想要的行为的最好选择是编写单元测试。它也可以你帮助得到提交时的错误。

    @@ -839,15 +829,15 @@ resultMap 元素有很多子元素和一个值得讨论的结构。

    这两者之间的唯一不同是 id 表示的结果将是当比较对象实例时用到的标识属性。这帮 -助来改进整体表现,特别是缓存和嵌入结果映射(也就是联合映射) -。

    +助来改进整体表现,特别是缓存和嵌入结果映射(也就是联合映射)。 +

    每个都有一些属性:

    - + @@ -858,11 +848,8 @@ resultMap 元素有很多子元素和一个值得讨论的结构。 @@ -1041,7 +1028,6 @@ resultSet.getString(columnName)方法的字符串是相同的。 在这个表格之前的所支持的 JDBC 类型列表中的类型。JDBC 类型是仅仅 需要对插入, 更新和删除操作可能为空的列进行处理。这是 JDBC 的需要, -jdbcType 而不是 MyBatis 的。如果你直接使用 JDBC 编程,你需要指定这个类型-但 仅仅对可能为空的值。 @@ -1120,11 +1106,8 @@ jdbcType - +
    Id and Result AttributesId 和 Result 的属性
    属性
    property -映射到列结果的字段或属性。如果匹配的是存在的,和给定名称相同 -的 JavaBeans 的属性,那么就会使用。否则 MyBatis 将会寻找给定名称 -property -的字段。这两种情形你可以使用通常点式的复杂属性导航。比如,你 -可以这样映射一些东西: +映射到列结果的字段或属性。如果用来匹配的 JavaBeans 存在给定名字的属性,那么它将会被使用。否则 MyBatis 将会寻找给定名称 property 的字段。 +这两种情形你可以使用通常点式的复杂属性导航。比如,你可以这样映射一些东西: “username” ,或者映射到一些复杂的东西: “address.street.number” @@ -961,7 +948,7 @@ jdbcType

    对于大多数数据传输对象(Data Transfer Object,DTO)类型,属性可以起作用,而且像 你绝大多数的领域模型, -指令也许是你想使不可变类的地方。 +可能有些情况下你想使用不可变类。 通常包含引用或查询数 据的表很少或基本不变的话对不可变类来说是合适的。 构造方法注入允许你在初始化时 @@ -1022,7 +1009,7 @@ jdbcType

    column -来自数据库的类名,或重命名的列标签。这和通常传递给 +来自数据库的列名,或重命名的列标签。这和通常传递给 resultSet.getString(columnName)方法的字符串是相同的。
    property -映射到列结果的字段或属性。如果匹配的是存在的,和给定名称相同的 -property -JavaBeans 的属性, -那么就会使用。 -否则 MyBatis 将会寻找给定名称的字段。 +映射到列结果的字段或属性。如果用来匹配的 JavaBeans 存在给定名字的属性,那么它将会被使用。 +否则 MyBatis 将会寻找与给定名称相同的字段。 这两种情形你可以使用通常点式的复杂属性导航。比如,你可以这样映射 一 些 东 西 :“ username ”, 或 者 映 射 到 一 些 复 杂 的 东 西 : “address.street.number” @@ -1178,7 +1161,7 @@ typeHandler
    column -来自数据库的类名,或重命名的列标签。这和通常传递给 +来自数据库的列名,或重命名的列标签。这和通常传递给 resultSet.getString(columnName)方法的字符串是相同的。 column 注 意 : 要 处 理 复 合 主 键 , 你 可 以 指 定 多 个 列 名 通 过 column= ” @@ -1505,7 +1488,7 @@ javaType 属性是不需要的,因为 MyBatis 在很多情况下会为你算出 用了一个“ofType”属性

    -

    First, let's look at the SQL:

    +

    首先, 让我们看看 SQL:

    select From 519cf8e47ca106c361bd23817b72fa921a0a1860 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Thu, 16 Nov 2017 22:56:37 +0900 Subject: [PATCH 0024/2062] [pom] Update OGNL to 3.1.16. Should resolve #929 . --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 95d3115737f..e0605232183 100644 --- a/pom.xml +++ b/pom.xml @@ -146,7 +146,7 @@ ognl ognl - 3.1.15 + 3.1.16 compile true From 742718af767829b6b515127a69d49b57885c1455 Mon Sep 17 00:00:00 2001 From: CodeingBoy Date: Fri, 17 Nov 2017 10:05:32 +0800 Subject: [PATCH 0025/2062] Fix typo --- src/site/zh/xdoc/configuration.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/site/zh/xdoc/configuration.xml b/src/site/zh/xdoc/configuration.xml index 69396a45632..eabb5c7a19f 100644 --- a/src/site/zh/xdoc/configuration.xml +++ b/src/site/zh/xdoc/configuration.xml @@ -1309,7 +1309,7 @@ public class ExampleTypeHandler extends BaseTypeHandler {

    当决定在ResultMap中使用某一TypeHandler时,此时java类型是已知的(从结果类型中获得),但是JDBC类型是未知的。 因此Mybatis使用javaType=[TheJavaType], jdbcType=null的组合来选择一个TypeHandler。 - 这意味着使用@MappedJdbcTypes注解可以限制TypeHandler的范围,同时除非显示的设置,否则TypeHandler在ResultMap中将是无效的。 + 这意味着使用@MappedJdbcTypes注解可以限制TypeHandler的范围,同时除非显式的设置,否则TypeHandler在ResultMap中将是无效的。 如果希望在ResultMap中使用TypeHandler,那么设置@MappedJdbcTypes注解的includeNullJdbcType=true即可。 然而从Mybatis 3.4.0开始,如果只有一个注册的TypeHandler来处理Java类型,那么它将是ResultMap使用Java类型时的默认值(即使没有includeNullJdbcType=true)。

    From 1e81a1137ead9c487611f549ed7c31d37e1db256 Mon Sep 17 00:00:00 2001 From: CodeingBoy Date: Fri, 17 Nov 2017 11:19:07 +0800 Subject: [PATCH 0026/2062] Improve translation quality Fix typos and give better translations according to english documentation --- src/site/zh/xdoc/configuration.xml | 69 +++++++++++++++--------------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/src/site/zh/xdoc/configuration.xml b/src/site/zh/xdoc/configuration.xml index eabb5c7a19f..176a996a9f6 100644 --- a/src/site/zh/xdoc/configuration.xml +++ b/src/site/zh/xdoc/configuration.xml @@ -1322,8 +1322,8 @@ public class ExampleTypeHandler extends BaseTypeHandler { ]]>

    注意在使用自动检索(autodiscovery)功能的时候,只能通过注解方式来指定 JDBC 的类型。

    -

    你能创建一个泛型类型处理器,它可以处理多于一个类。为达到此目的, - 需要增加一个接收该类作为参数的构造器,这样在构造一个类型处理器的时候 MyBatis 就会传入一个具体的类。

    +

    你可以创建一个能够处理多个类的泛型类型处理器。为了使用泛型类型处理器, + 需要增加一个接受该类的 class 作为参数的构造器,这样在构造一个类型处理器的时候 MyBatis 就会传入一个具体的类。

    extends BaseTypeHandler { @@ -1361,9 +1361,9 @@ public class GenericTypeHandler extends BaseTypeHandler {

    但是怎样能将同样的 Enum 既映射成字符串又映射成整形呢?

    自动映射器(auto-mapper)会自动地选用 EnumOrdinalTypeHandler 来处理, - 所以如果我们想用普通的 EnumTypeHandler,就非要为那些 SQL 语句显式地设置要用到的类型处理器不可。 + 所以如果我们想用普通的 EnumTypeHandler,就必须要显式地为那些 SQL 语句设置要使用的类型处理器。

    -

    (下一节才开始讲映射器文件,所以如果是首次阅读该文档,你可能需要先越过这一步,过会再来看。)

    +

    (下一节才开始介绍映射器文件,如果你是首次阅读该文档,你可能需要先跳过这里,过会再来看。)

    @@ -1458,11 +1458,11 @@ public class ExampleObjectFactory extends DefaultObjectFactory { (prepare, parameterize, batch, update, query) -

    这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 的发行包中的源代码。 - 假设你想做的不仅仅是监控方法的调用,那么你应该很好的了解正在重写的方法的行为。 +

    这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。 + 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为如果在试图修改或重写已有方法的行为的时候,你很可能在破坏 MyBatis 的核心模块。 这些都是更低层的类和方法,所以使用插件的时候要特别当心。

    -

    通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定了想要拦截的方法签名即可。

    +

    通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。

    +SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);]]>

    如果忽略了环境参数,那么默认环境将会被加载,如下所示:

    +SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, properties);]]>

    环境元素定义了如何配置环境。

    @@ -1542,19 +1542,19 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader,properti

    • - 默认的环境 ID(比如:default=”development”)。 + 默认的环境 ID(比如:default="development")。
    • - 每个 environment 元素定义的环境 ID(比如:id=”development”)。 + 每个 environment 元素定义的环境 ID(比如:id="development")。
    • - 事务管理器的配置(比如:type=”JDBC”)。 + 事务管理器的配置(比如:type="JDBC")。
    • - 数据源的配置(比如:type=”POOLED”)。 + 数据源的配置(比如:type="POOLED")。
    -

    默认的环境和环境 ID 是一目了然的。随你怎么命名,只要保证默认环境要匹配其中一个环境ID。 +

    默认的环境和环境 ID 是自解释的,因此一目了然。你可以对环境随意命名,但一定要保证默认的环境 ID 要匹配其中一个环境 ID。

    事务管理器(transactionManager) @@ -1596,15 +1596,15 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader,properti

    dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。

      -
    • 许多 MyBatis 的应用程序将会按示例中的例子来配置数据源。然而它并不是必须的。要知道为了方便使用延迟加载,数据源才是必须的。 +
    • 许多 MyBatis 的应用程序会按示例中的例子来配置数据源。虽然这是可选的,但为了使用延迟加载,数据源是必须配置的。

    有三种内建的数据源类型(也就是 type=”[UNPOOLED|POOLED|JNDI]”):

    - UNPOOLED– 这个数据源的实现只是每次被请求时打开和关闭连接。虽然一点慢,它对在及时可用连接方面没有性能要求的简单应用程序是一个很好的选择。 - 不同的数据库在这方面表现也是不一样的,所以对某些数据库来说使用连接池并不重要,这个配置也是理想的。UNPOOLED 类型的数据源仅仅需要配置以下 5 种属性:

    + UNPOOLED– 这个数据源的实现只是每次被请求时打开和关闭连接。虽然有点慢,但对于在数据库连接可用性方面没有太高要求的简单应用程序来说,是一个很好的选择。 + 不同的数据库在性能方面的表现也是不一样的,对于某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。UNPOOLED 类型的数据源仅仅需要配置以下 5 种属性:

      -
    • driver – 这是 JDBC 驱动的 Java 类的完全限定名(并不是JDBC驱动中可能包含的数据源类)。 +
    • driver – 这是 JDBC 驱动的 Java 类的完全限定名(并不是 JDBC 驱动中可能包含的数据源类)。
    • url – 这是数据库的 JDBC URL 地址。
    • @@ -1620,13 +1620,13 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader,properti
      • driver.encoding=UTF8
      -

      这将通过DriverManager.getConnection(url,driverProperties)方法传递值为 UTF8encoding 属性给数据库驱动。 +

      这将通过 DriverManager.getConnection(url,driverProperties) 方法传递值为 UTF8encoding 属性给数据库驱动。

      POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这是一种使得并发 Web 应用快速响应请求的流行处理方式。

      -

      除了上述提到 UNPOOLED 下的属性外,会有更多属性用来配置 POOLED 的数据源:

      +

      除了上述提到 UNPOOLED 下的属性外,还有更多属性用来配置 POOLED 的数据源:

      • poolMaximumActiveConnections – 在任意时间可以存在的活动(也就是正在使用)连接数量,默认值:10
      • @@ -1635,22 +1635,21 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader,properti
      • poolMaximumCheckoutTime – 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒)
      • -
      • poolTimeToWait – 这是一个底层设置,如果获取连接花费的相当长的时间,它会给连接池打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直安静的失败),默认值:20000 毫秒(即 20 秒)。 +
      • poolTimeToWait – 这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直安静的失败),默认值:20000 毫秒(即 20 秒)。
      • poolMaximumLocalBadConnectionTolerance – 这是一个关于坏连接容忍度的底层设置, - 作用于每一个尝试从缓存池获取连接的线程. 如果这个线程获取到的是一个坏的连接,那么这个数据源允许这 - 个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过 poolMaximumIdleConnections - 与 poolMaximumLocalBadConnectionTolerance 之和。 默认值:3 (Since: 3.4.5) + 作用于每一个尝试从缓存池获取连接的线程. 如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过 poolMaximumIdleConnections + 与 poolMaximumLocalBadConnectionTolerance 之和。 默认值:3 (新增于 3.4.5)
      • -
      • poolPingQuery – 发送到数据库的侦测查询,用来检验连接是否处在正常工作秩序中并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动失败时带有一个恰当的错误消息。 +
      • poolPingQuery – 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动失败时带有一个恰当的错误消息。
      • -
      • poolPingEnabled – 是否启用侦测查询。若开启,也必须使用一个可执行的 SQL 语句设置 poolPingQuery 属性(最好是一个非常快的 SQL),默认值:false。 +
      • poolPingEnabled – 是否启用侦测查询。若开启,需要设置 poolPingQuery 属性为一个可执行的 SQL 语句(最好是一个速度非常快的 SQL 语句),默认值:false。
      • -
      • poolPingConnectionsNotUsedFor – 配置 poolPingQuery 的使用频度。这可以被设置成匹配具体的数据库连接超时时间,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。 +
      • poolPingConnectionsNotUsedFor – 配置 poolPingQuery 的频率。可以被设置为和数据库连接超时时间一样,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。

      - JNDI– 这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。这种数据源配置只需要两个属性: + JNDI – 这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。这种数据源配置只需要两个属性:

      • initial_context – 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么 data_source 属性将会直接从 InitialContext 中寻找。 @@ -1667,7 +1666,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader,properti

        - 通过需要实现接口 org.apache.ibatis.datasource.DataSourceFactory , 也可使用任何第三方数据源,: + 你可以通过实现接口 org.apache.ibatis.datasource.DataSourceFactory 来使用第三方数据源:

        -

        为了令其工作,为每个需要 MyBatis 调用的 setter 方法中增加一个属性。下面是一个可以连接至 PostgreSQL 数据库的例子:

        +

        为了令其工作,记得为每个希望 MyBatis 调用的 setter 方法在配置文件中增加对应的属性。下面是一个可以连接至 PostgreSQL 数据库的例子:

        @@ -1719,7 +1718,7 @@ public class C3P0DataSourceFactory extends UnpooledDataSourceFactory { ]]> -

        在有 properties 时,DB_VENDOR databaseIdProvider 的将被设置为第一个能匹配数据库产品名称的属性键对应的值,如果没有匹配的属性将会设置为 “null”。 +

        在提供了属性别名时,DB_VENDOR databaseIdProvider 将被设置为第一个能匹配数据库产品名称的属性键对应的值,如果没有匹配的属性将会设置为 “null”。 在这个例子中,如果 getDatabaseProductName() 返回“Oracle (DataDirect)”,databaseId 将被设置为“oracle”。

        你可以通过实现接口 org.apache.ibatis.mapping.DatabaseIdProvider 并在 mybatis-config.xml 中注册来构建自己的 DatabaseIdProvider:

        @@ -1735,28 +1734,28 @@ public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {

        既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要定义 SQL 映射语句了。但是首先我们需要告诉 MyBatis 到哪里去找到这些语句。 Java 在自动查找这方面没有提供一个很好的方法,所以最佳的方式是告诉 MyBatis 到哪里去找映射文件。你可以使用相对于类路径的资源引用, 或完全限定资源定位符(包括 file:/// 的 URL),或类名和包名等。例如:

        - + ]]> - + ]]> - + ]]> - + ]]> From 0633239cf42b0e99a4007b97e90527d1e355183a Mon Sep 17 00:00:00 2001 From: CodeingBoy Date: Fri, 17 Nov 2017 11:20:14 +0800 Subject: [PATCH 0027/2062] Replace "NOTE" to corresponding Chinese text --- src/site/zh/xdoc/configuration.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/site/zh/xdoc/configuration.xml b/src/site/zh/xdoc/configuration.xml index 176a996a9f6..d3d65ea8b85 100644 --- a/src/site/zh/xdoc/configuration.xml +++ b/src/site/zh/xdoc/configuration.xml @@ -111,7 +111,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ ]]>

        - NOTE 你可以使用 ":" 作为属性键(e.g. db:username) + 提示 你可以使用 ":" 作为属性键(e.g. db:username) 或者你也可以在sql定义中使用 OGNL 表达式的三元运算符(e.g. ${tableName != null ? tableName : 'global_constants'}), 你应该通过增加一个指定的属性来改变分隔键和默认值的字符。例如:

        @@ -825,7 +825,7 @@ public class Author {

        无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器。

        - NOTE + 提示 从 3.4.5 开始,MyBatis 默认支持 JSR-310(日期和时间 API) 。

        @@ -1488,7 +1488,7 @@ public class ExamplePlugin implements Interceptor { ]]>

        上面的插件将会拦截在 Executor 实例中所有的 “update” 方法调用, 这里的 Executor 是负责执行低层映射语句的内部对象。

        -

        NOTE +

        提示 覆盖配置类

        除了用插件来修改 MyBatis 核心行为之外,还可以通过完全覆盖配置类来达到目的。只需继承后覆盖其中的每个方法,再把它传递到 SqlSessionFactoryBuilder.build(myConfig) 方法即可。再次重申,这可能会严重影响 MyBatis 的行为,务请慎之又慎。

        @@ -1571,7 +1571,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, propert

        - NOTE如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器, + 提示如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器, 因为 Spring 模块会使用自带的管理器来覆盖前面的配置。

        From 2d62dc129ab05b4163e6271cd71d6e2741c13c0c Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Thu, 23 Nov 2017 10:05:59 +0900 Subject: [PATCH 0028/2062] Upgrade to postgresql-embedded 2.5 Fixes gh-1133 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e0605232183..cbf1df3a8da 100644 --- a/pom.xml +++ b/pom.xml @@ -261,7 +261,7 @@ ru.yandex.qatools.embed postgresql-embedded - 2.3 + 2.5 test From 199ad1c80caf7c7672a087ed31ff9f2c01e989b0 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Thu, 23 Nov 2017 10:17:16 +0900 Subject: [PATCH 0029/2062] Change cached directory for postgresql-embedded (Workaround for yandex-qatools/postgresql-embedded#110) See gh-1133 --- .../apache/ibatis/submitted/usesjava8/keycolumn/InsertTest.java | 2 +- .../usesjava8/multiple_resultsets/MultipleResultTest.java | 2 +- .../usesjava8/postgres_genkeys/PostgresGeneratedKeysTest.java | 2 +- .../ibatis/submitted/usesjava8/refcursor/RefCursorTest.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/apache/ibatis/submitted/usesjava8/keycolumn/InsertTest.java b/src/test/java/org/apache/ibatis/submitted/usesjava8/keycolumn/InsertTest.java index c590cedb35b..031129c63ea 100644 --- a/src/test/java/org/apache/ibatis/submitted/usesjava8/keycolumn/InsertTest.java +++ b/src/test/java/org/apache/ibatis/submitted/usesjava8/keycolumn/InsertTest.java @@ -57,7 +57,7 @@ public class InsertTest { @BeforeClass public static void setUp() throws Exception { // Launch PostgreSQL server. Download / unarchive if necessary. - String url = postgres.start(EmbeddedPostgres.cachedRuntimeConfig(Paths.get("target/postgres")), "localhost", SocketUtil.findFreePort(), "keycolumn", "postgres", "root", Collections.emptyList()); + String url = postgres.start(EmbeddedPostgres.cachedRuntimeConfig(Paths.get(System.getProperty("java.io.tmpdir"), "pgembed")), "localhost", SocketUtil.findFreePort(), "keycolumn", "postgres", "root", Collections.emptyList()); Configuration configuration = new Configuration(); Environment environment = new Environment("development", new JdbcTransactionFactory(), new UnpooledDataSource( diff --git a/src/test/java/org/apache/ibatis/submitted/usesjava8/multiple_resultsets/MultipleResultTest.java b/src/test/java/org/apache/ibatis/submitted/usesjava8/multiple_resultsets/MultipleResultTest.java index 5f16e4d4fba..91047b9bfa8 100644 --- a/src/test/java/org/apache/ibatis/submitted/usesjava8/multiple_resultsets/MultipleResultTest.java +++ b/src/test/java/org/apache/ibatis/submitted/usesjava8/multiple_resultsets/MultipleResultTest.java @@ -55,7 +55,7 @@ public class MultipleResultTest { @BeforeClass public static void setUp() throws Exception { // Launch PostgreSQL server. Download / unarchive if necessary. - String url = postgres.start(EmbeddedPostgres.cachedRuntimeConfig(Paths.get("target/postgres")), "localhost", SocketUtil.findFreePort(), "multiple_resultsets", "postgres", "root", Collections.emptyList()); + String url = postgres.start(EmbeddedPostgres.cachedRuntimeConfig(Paths.get(System.getProperty("java.io.tmpdir"), "pgembed")), "localhost", SocketUtil.findFreePort(), "multiple_resultsets", "postgres", "root", Collections.emptyList()); Configuration configuration = new Configuration(); Environment environment = new Environment("development", new JdbcTransactionFactory(), new UnpooledDataSource( diff --git a/src/test/java/org/apache/ibatis/submitted/usesjava8/postgres_genkeys/PostgresGeneratedKeysTest.java b/src/test/java/org/apache/ibatis/submitted/usesjava8/postgres_genkeys/PostgresGeneratedKeysTest.java index ffa64a95da7..7e3c14b0dfa 100644 --- a/src/test/java/org/apache/ibatis/submitted/usesjava8/postgres_genkeys/PostgresGeneratedKeysTest.java +++ b/src/test/java/org/apache/ibatis/submitted/usesjava8/postgres_genkeys/PostgresGeneratedKeysTest.java @@ -48,7 +48,7 @@ public class PostgresGeneratedKeysTest { @BeforeClass public static void setUp() throws Exception { // Launch PostgreSQL server. Download / unarchive if necessary. - String url = postgres.start(EmbeddedPostgres.cachedRuntimeConfig(Paths.get("target/postgres")), "localhost", SocketUtil.findFreePort(), "postgres_genkeys", "postgres", "root", Collections.emptyList()); + String url = postgres.start(EmbeddedPostgres.cachedRuntimeConfig(Paths.get(System.getProperty("java.io.tmpdir"), "pgembed")), "localhost", SocketUtil.findFreePort(), "postgres_genkeys", "postgres", "root", Collections.emptyList()); Configuration configuration = new Configuration(); Environment environment = new Environment("development", new JdbcTransactionFactory(), new UnpooledDataSource( diff --git a/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/RefCursorTest.java b/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/RefCursorTest.java index 631e813e688..c250ea4ffdb 100644 --- a/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/RefCursorTest.java +++ b/src/test/java/org/apache/ibatis/submitted/usesjava8/refcursor/RefCursorTest.java @@ -60,7 +60,7 @@ public class RefCursorTest { @BeforeClass public static void setUp() throws Exception { // Launch PostgreSQL server. Download / unarchive if necessary. - String url = postgres.start(EmbeddedPostgres.cachedRuntimeConfig(Paths.get("target/postgres")), "localhost", SocketUtil.findFreePort(), "refcursor", "postgres", "root", Collections.emptyList()); + String url = postgres.start(EmbeddedPostgres.cachedRuntimeConfig(Paths.get(System.getProperty("java.io.tmpdir"), "pgembed")), "localhost", SocketUtil.findFreePort(), "refcursor", "postgres", "root", Collections.emptyList()); Configuration configuration = new Configuration(); Environment environment = new Environment("development", new JdbcTransactionFactory(), new UnpooledDataSource( From aec7fb515766d52ad055bdba5b1dc156977573ca Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Fri, 24 Nov 2017 00:33:44 +0900 Subject: [PATCH 0030/2062] fixes #1146 A double value 0.0d should be evaluated as false. BigDecimal#equals() is too strict. --- .../scripting/xmltags/ExpressionEvaluator.java | 2 +- .../xml/dynamic/ExpressionEvaluatorTest.java | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/ExpressionEvaluator.java b/src/main/java/org/apache/ibatis/scripting/xmltags/ExpressionEvaluator.java index 08c3b8d1b17..be6d4167f74 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/ExpressionEvaluator.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/ExpressionEvaluator.java @@ -34,7 +34,7 @@ public boolean evaluateBoolean(String expression, Object parameterObject) { return (Boolean) value; } if (value instanceof Number) { - return !new BigDecimal(String.valueOf(value)).equals(BigDecimal.ZERO); + return new BigDecimal(String.valueOf(value)).compareTo(BigDecimal.ZERO) != 0; } return value != null; } diff --git a/src/test/java/org/apache/ibatis/builder/xml/dynamic/ExpressionEvaluatorTest.java b/src/test/java/org/apache/ibatis/builder/xml/dynamic/ExpressionEvaluatorTest.java index 324e40df5c4..6df59332b25 100644 --- a/src/test/java/org/apache/ibatis/builder/xml/dynamic/ExpressionEvaluatorTest.java +++ b/src/test/java/org/apache/ibatis/builder/xml/dynamic/ExpressionEvaluatorTest.java @@ -15,14 +15,13 @@ */ package org.apache.ibatis.builder.xml.dynamic; +import static org.junit.Assert.*; + import java.util.HashMap; import org.apache.ibatis.domain.blog.Author; import org.apache.ibatis.domain.blog.Section; import org.apache.ibatis.scripting.xmltags.ExpressionEvaluator; - -import static org.junit.Assert.assertEquals; - import org.junit.Test; public class ExpressionEvaluatorTest { @@ -65,6 +64,15 @@ public void shouldReturnFalseIfZero() { assertEquals(false, value); } + @Test + public void shouldReturnFalseIfZeroWithScale() { + class Bean { + @SuppressWarnings("unused") + public double d = 0.0d; + } + assertFalse(evaluator.evaluateBoolean("d", new Bean())); + } + @Test public void shouldIterateOverIterable() { final HashMap parameterObject = new HashMap() {{ From 5a4a6539d9d803b64c5bc098a1fea7632460030c Mon Sep 17 00:00:00 2001 From: "wei.zw" Date: Mon, 27 Nov 2017 16:54:46 +0800 Subject: [PATCH 0031/2062] misc:enhance nodeHandlerMap ,using a global Map instead of local variables --- .../scripting/xmltags/XMLScriptBuilder.java | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/XMLScriptBuilder.java b/src/main/java/org/apache/ibatis/scripting/xmltags/XMLScriptBuilder.java index 8496b8476c6..e36e077c417 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/XMLScriptBuilder.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/XMLScriptBuilder.java @@ -37,6 +37,7 @@ public class XMLScriptBuilder extends BaseBuilder { private final XNode context; private boolean isDynamic; private final Class parameterType; + private final Map nodeHandlerMap = new HashMap(); public XMLScriptBuilder(Configuration configuration, XNode context) { this(configuration, context, null); @@ -48,6 +49,19 @@ public XMLScriptBuilder(Configuration configuration, XNode context, Class par this.parameterType = parameterType; } + + private void initNodeHandlerMap(){ + nodeHandlerMap.put("trim", new TrimHandler()); + nodeHandlerMap.put("where", new WhereHandler()); + nodeHandlerMap.put("set", new SetHandler()); + nodeHandlerMap.put("foreach", new ForEachHandler()); + nodeHandlerMap.put("if", new IfHandler()); + nodeHandlerMap.put("choose", new ChooseHandler()); + nodeHandlerMap.put("when", new IfHandler()); + nodeHandlerMap.put("otherwise", new OtherwiseHandler()); + nodeHandlerMap.put("bind", new BindHandler()); + } + public SqlSource parseScriptNode() { List contents = parseDynamicTags(context); MixedSqlNode rootSqlNode = new MixedSqlNode(contents); @@ -88,17 +102,7 @@ List parseDynamicTags(XNode node) { } NodeHandler nodeHandlers(String nodeName) { - Map map = new HashMap(); - map.put("trim", new TrimHandler()); - map.put("where", new WhereHandler()); - map.put("set", new SetHandler()); - map.put("foreach", new ForEachHandler()); - map.put("if", new IfHandler()); - map.put("choose", new ChooseHandler()); - map.put("when", new IfHandler()); - map.put("otherwise", new OtherwiseHandler()); - map.put("bind", new BindHandler()); - return map.get(nodeName); + return nodeHandlerMap.get(nodeName); } private interface NodeHandler { From c6a7878bb6677dee70427114ce89f95ccc48036f Mon Sep 17 00:00:00 2001 From: "wei.zw" Date: Mon, 27 Nov 2017 16:55:20 +0800 Subject: [PATCH 0032/2062] misc:enhance nodeHandlerMap ,using a global Map instead of local variables --- .../org/apache/ibatis/scripting/xmltags/XMLScriptBuilder.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/XMLScriptBuilder.java b/src/main/java/org/apache/ibatis/scripting/xmltags/XMLScriptBuilder.java index e36e077c417..736a9021a67 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/XMLScriptBuilder.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/XMLScriptBuilder.java @@ -47,6 +47,7 @@ public XMLScriptBuilder(Configuration configuration, XNode context, Class par super(configuration); this.context = context; this.parameterType = parameterType; + initNodeHandlerMap(); } From 5a7d43146efdd014ce83912a5b59a43949ac171d Mon Sep 17 00:00:00 2001 From: "wei.zw" Date: Wed, 29 Nov 2017 19:58:25 +0800 Subject: [PATCH 0033/2062] change the method of instantiateClass from default to private and add test cases --- .../factory/DefaultObjectFactory.java | 2 +- .../factory/DefaultObjectFactoryTest.java | 53 +++++++++++++++++-- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/apache/ibatis/reflection/factory/DefaultObjectFactory.java b/src/main/java/org/apache/ibatis/reflection/factory/DefaultObjectFactory.java index cb7cdf9c8be..4c794a8bd18 100644 --- a/src/main/java/org/apache/ibatis/reflection/factory/DefaultObjectFactory.java +++ b/src/main/java/org/apache/ibatis/reflection/factory/DefaultObjectFactory.java @@ -55,7 +55,7 @@ public void setProperties(Properties properties) { // no props for default } - T instantiateClass(Class type, List> constructorArgTypes, List constructorArgs) { + private T instantiateClass(Class type, List> constructorArgTypes, List constructorArgs) { try { Constructor constructor; if (constructorArgTypes == null || constructorArgs == null) { diff --git a/src/test/java/org/apache/ibatis/reflection/factory/DefaultObjectFactoryTest.java b/src/test/java/org/apache/ibatis/reflection/factory/DefaultObjectFactoryTest.java index 1c5388dcd82..290e472601e 100644 --- a/src/test/java/org/apache/ibatis/reflection/factory/DefaultObjectFactoryTest.java +++ b/src/test/java/org/apache/ibatis/reflection/factory/DefaultObjectFactoryTest.java @@ -15,8 +15,18 @@ */ package org.apache.ibatis.reflection.factory; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; import org.apache.ibatis.reflection.ReflectionException; import org.junit.Assert; @@ -30,9 +40,9 @@ public class DefaultObjectFactoryTest { @Test - public void instantiateClass() throws Exception { + public void createClass() throws Exception { DefaultObjectFactory defaultObjectFactory = new DefaultObjectFactory(); - TestClass testClass = defaultObjectFactory.instantiateClass(TestClass.class, + TestClass testClass = defaultObjectFactory.create(TestClass.class, Arrays.>asList(String.class, Integer.class), Arrays.asList("foo", 0)); Assert.assertEquals("myInteger didn't match expected", (Integer) 0, testClass.myInteger); @@ -40,10 +50,10 @@ public void instantiateClass() throws Exception { } @Test - public void instantiateClassThrowsProperErrorMsg() { + public void createClassThrowsProperErrorMsg() { DefaultObjectFactory defaultObjectFactory = new DefaultObjectFactory(); try { - defaultObjectFactory.instantiateClass(TestClass.class, Collections.>singletonList(String.class), Collections.singletonList("foo")); + defaultObjectFactory.create(TestClass.class, Collections.>singletonList(String.class), Collections.singletonList("foo")); Assert.fail("Should have thrown ReflectionException"); } catch (Exception e) { Assert.assertTrue("Should be ReflectionException", e instanceof ReflectionException); @@ -52,4 +62,39 @@ public void instantiateClassThrowsProperErrorMsg() { } } + @Test + public void creatHashMap() throws Exception{ + DefaultObjectFactory defaultObjectFactory=new DefaultObjectFactory(); + Map map= defaultObjectFactory.create(Map.class,null,null); + Assert.assertTrue("Should be HashMap",map instanceof HashMap); + } + + @Test + public void createArrayList() throws Exception { + DefaultObjectFactory defaultObjectFactory = new DefaultObjectFactory(); + List list = defaultObjectFactory.create(List.class); + Assert.assertTrue(" list should be ArrayList", list instanceof ArrayList); + + Collection collection = defaultObjectFactory.create(Collection.class); + Assert.assertTrue(" collection should be ArrayList", collection instanceof ArrayList); + + Iterable iterable = defaultObjectFactory.create(Iterable.class); + Assert.assertTrue(" iterable should be ArrayList", iterable instanceof ArrayList); + } + + + @Test + public void createTreeSet() throws Exception { + DefaultObjectFactory defaultObjectFactory = new DefaultObjectFactory(); + SortedSet sortedSet = defaultObjectFactory.create(SortedSet.class); + Assert.assertTrue(" sortedSet should be TreeSet", sortedSet instanceof TreeSet); + } + + + @Test + public void createHashSet() throws Exception { + DefaultObjectFactory defaultObjectFactory = new DefaultObjectFactory(); + Set set = defaultObjectFactory.create(Set.class); + Assert.assertTrue(" set should be HashSet", set instanceof HashSet); + } } From 284379d5badb0accc8e18a5e00009391833f9df8 Mon Sep 17 00:00:00 2001 From: "wei.zw" Date: Wed, 29 Nov 2017 20:00:01 +0800 Subject: [PATCH 0034/2062] change the method of instantiateClass from default to private and add test cases --- .../ibatis/reflection/factory/DefaultObjectFactoryTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/org/apache/ibatis/reflection/factory/DefaultObjectFactoryTest.java b/src/test/java/org/apache/ibatis/reflection/factory/DefaultObjectFactoryTest.java index 290e472601e..e17b89893f2 100644 --- a/src/test/java/org/apache/ibatis/reflection/factory/DefaultObjectFactoryTest.java +++ b/src/test/java/org/apache/ibatis/reflection/factory/DefaultObjectFactoryTest.java @@ -21,7 +21,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; From 317fa72a4050ed59522826d5643dd6232e6af7b2 Mon Sep 17 00:00:00 2001 From: CodeingBoy Date: Thu, 30 Nov 2017 01:30:47 +0800 Subject: [PATCH 0035/2062] Improve Chinese documentation quality --- src/site/zh/xdoc/sqlmap-xml.xml | 202 +++++++++++++++----------------- 1 file changed, 96 insertions(+), 106 deletions(-) diff --git a/src/site/zh/xdoc/sqlmap-xml.xml b/src/site/zh/xdoc/sqlmap-xml.xml index 72d1c2206c0..97f0b65cceb 100644 --- a/src/site/zh/xdoc/sqlmap-xml.xml +++ b/src/site/zh/xdoc/sqlmap-xml.xml @@ -474,7 +474,7 @@ ps.setInt(1,id);]]>

        像 MyBatis 的剩余部分一样,javaType 通常可以由参数对象确定,除非该对象是一个 HashMap。这时所使用的 TypeHandler 应该明确指明 javaType。

        -

        NOTE 如果 null 被当作值来传递,对于所有可能为空的列,JDBC Type 是必需的。你可以通过自己阅读预处理语句的 setNull() 方法的 JavaDocs 文档来研究这种情况。 +

        NOTE 如果一个列允许 null 值,并且会传递值 null 的参数,就必须要指定 JDBC Type。阅读 PreparedStatement.setNull() 的 JavaDocs 文档来获取更多信息。

        为了以后定制类型处理方式,你也可以指定一个特殊的类型处理器类(或别名),比如: @@ -482,7 +482,7 @@ ps.setInt(1,id);]]> -

        尽管看起来配置变得越来越繁琐,但实际上是很少去设置它们。 +

        尽管看起来配置变得越来越繁琐,但实际上,很少需要去设置它们。

        对于数值类型,还有一个小数保留位数的设置,来确定小数点后保留的位数。 @@ -490,7 +490,7 @@ ps.setInt(1,id);]]> -

        最后,mode 属性允许你指定 IN,OUT 或 INOUT 参数。如果参数为 OUT 或 INOUT,参数对象属性的真实值将会被改变,就像你在获取输出参数时所期望的那样。如果 mode 为 OUT(或 INOUT),而且 jdbcType 为 CURSOR(也就是 Oracle 的 REFCURSOR),你必须指定一个 resultMap 来映射结果集到参数类型。要注意这里的 javaType 属性是可选的,如果左边的空白是 jdbcType 的 CURSOR 类型,它会自动地被设置为结果集。 +

        最后,mode 属性允许你指定 IN,OUT 或 INOUT 参数。如果参数为 OUT 或 INOUT,参数对象属性的真实值将会被改变,就像你在获取输出参数时所期望的那样。如果 mode 为 OUT(或 INOUT),而且 jdbcType 为 CURSOR(也就是 Oracle 的 REFCURSOR),你必须指定一个 resultMap 来映射结果集 ResultMap 到参数类型。要注意这里的 javaType 属性是可选的,如果留空并且 jdbcType 是 CURSOR,它会被自动地被设为 ResultMap

        @@ -500,7 +500,7 @@ ps.setInt(1,id);]]> -

        尽管所有这些选项很强大,但大多时候你只须简单地指定属性名,其他的事情 MyBatis 会自己去推断,你最多需要为可能为空的列名指定 jdbcType。 +

        尽管所有这些选项很强大,但大多时候你只须简单地指定属性名,其他的事情 MyBatis 会自己去推断,顶多要为可能为空的列指定 jdbcType

        字符串替换 -

        默认情况下,使用#{}格式的语法会导致 MyBatis 创建预处理语句属性并安全地设置值(比如?)。这样做更安全,更迅速,通常也是首选做法,不过有时你只是想直接在 SQL 语句中插入一个不改变的字符串。比如,像 ORDER BY,你可以这样来使用: +

        默认情况下,使用 #{} 格式的语法会导致 MyBatis 创建 PreparedStatement 参数并安全地设置参数(就像使用 ? 一样)。这样做更安全,更迅速,通常也是首选做法,不过有时你就是想直接在 SQL 语句中插入一个不转义的字符串。比如,像 ORDER BY,你可以这样来使用:

        @@ -520,23 +520,20 @@ ps.setInt(1,id);]]>

        - NOTE 以这种方式接受从用户输出的内容并提供给语句中不变的字符串是不安全的,会导致潜在的 SQL 注入攻击,因此要么不允许用户输入这些字段,要么自行转义并检验。 + NOTE 用这种方式接受用户的输入,并将其用于语句中的参数是不安全的,会导致潜在的 SQL 注入攻击,因此要么不允许用户输入这些字段,要么自行转义并检验。

        -resultMap 元素是 MyBatis 中最重要最强大的元素。它就是让你远离 90%的需要从结果集中取出数据的 JDBC 代码的那个东西, -而且在一些情形下允许你做一些 JDBC 不支持的事情。 -事实上, -编写相似于对复杂语句联合映射这些等同的代码, -也许可以跨过上千行的代码。 -ResultMap 的设计就是简单语句不需要明确的结果映射,而很多复杂语句确实需要描述它们 -的关系。 + resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来, +并在一些情形下允许你做一些 JDBC 不支持的事情。 +实际上,在对复杂语句进行联合映射的时候,它很可能可以代替数千行的同等功能的代码。 +ResultMap 的设计思想是,简单的语句不需要明确的结果映射,而复杂一点的语句只需要描述它们的关系就行了。

        -你已经看到简单映射语句的示例了,但没有明确的 resultMap。比如: +你已经见过简单映射语句的示例了,但没有明确的 resultMap。比如:

        @@ -546,10 +543,10 @@ ResultMap 的设计就是简单语句不需要明确的结果映射,而很多复 ]]>

        -这样一个语句只是简单地将结果自动映射到 HashMap 的键上,这由 resultType 属性指定。 -这在很多情况下是有用的,但是 HashMap 不能很好描述一个领域模型。 -那样你的应用程序将会使用 JavaBeans 或 POJOs(Plain Old Java Objects,普通 Java 对象)来作为领域模型。 -MyBatis 对两者都支持。看看下面这个 JavaBean: + 上述语句只是简单地将所有的列映射到 HashMap 的键上,这由 resultType 属性指定。 + 虽然在大部分情况下都够用,但是 HashMap 不是一个很好的领域模型。 + 你的程序更可能会使用 JavaBean 或 POJO(Plain Old Java Objects,普通 Java 对象)作为领域模型。 + MyBatis 对两者都支持。看看下面这个 JavaBean:

        -基于 JavaBean 的规范,上面这个类有 3 个属性:id,username 和 hashedPassword。这些 -在 select 语句中会精确匹配到列名。 +基于 JavaBean 的规范,上面这个类有 3 个属性:id,username 和 hashedPassword。这些属性会对应到 select 语句中的列名。

        -这样的一个 JavaBean 可以被映射到结果集,就像映射到 HashMap 一样简单。 +这样的一个 JavaBean 可以被映射到 ResultSet,就像映射到 HashMap 一样简单。

        @@ -593,7 +589,7 @@ public class User { ]]>

        -要记住类型别名是你的伙伴。使用它们你可以不用输入类的全路径。比如: +类型别名是你的好帮手。使用它们,你就可以不用输入类的完全限定名称了。比如:

        @@ -607,9 +603,9 @@ public class User { ]]>

        -这些情况下,MyBatis 会在幕后自动创建一个 ResultMap,基于属性名来映射列到 -JavaBean 的属性上。如果列名没有精确匹配,你可以在列名上使用 select 字句的别名(一个 -基本的 SQL 特性)来匹配标签。比如: +这些情况下,MyBatis 会在幕后自动创建一个 ResultMap,再基于属性名来映射列到 +JavaBean 的属性上。如果列名和属性名没有精确匹配,可以在 SELECT 语句中对列使用别名(这是一个 +基本的 SQL 特性)来匹配标签。比如:

        @@ -622,10 +618,9 @@ JavaBean 的属性上。如果列名没有精确匹配,你可以在列名上使 ]]>

        -ResultMap 最优秀的地方你已经了解了很多了,但是你还没有真正的看到一个。 -这些简单的示例不需要比你看到的更多东西。 -只是出于示例的原因, -让我们来看看最后一个示例中外部的 resultMap 是什么样子的,这也是解决列名不匹配的另外一种方式。 +ResultMap 最优秀的地方在于,虽然你已经对它相当了解了,但是根本就不需要显式地用到他们。 +上面这些简单的示例根本不需要下面这些繁琐的配置。 +出于示范的原因,让我们来看看最后一个示例中,如果使用外部的 resultMap 会怎样,这也是解决列名不匹配的另外一种方式。

        @@ -635,7 +630,7 @@ ResultMap 最优秀的地方你已经了解了很多了,但是你还没有真正 ]]>

        -引用它的语句使用 resultMap 属性就行了(注意我们去掉了 resultType 属性)。比如: +引用它的语句使用 resultMap 属性就行了(注意我们去掉了 resultType 属性)。比如:

        @@ -651,14 +646,14 @@ ResultMap 最优秀的地方你已经了解了很多了,但是你还没有真正

        高级结果映射

        -MyBatis MyBatis 创建的一个想法:数据库不用永远是你所想或所需的。 -而我们最喜欢的数据库最好是第三范式或 BCNF 模式,但它们有时不是。 -如果可能有一个单独的数据库映射,所有应用程序都可以使用它,这是非常好的,但有时也不是。 -结果映射就是结果映射就是 MyBatis 提供处理这个问题的答案。 +MyBatis 创建的一个想法是:数据库不可能永远是你所想或所需的那个样子。 +我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们不总都是这样。 +如果有一个独立且完美的数据库映射模式,所有应用程序都可以使用它,那就太好了,但可惜也没有。 +ResultMap 就是 MyBatis 对这个问题的答案。

        -比如,我们如何映射下面这个语句? +比如,我们如何映射下面这个语句?

        @@ -697,15 +692,15 @@ MyBatis MyBatis 创建的一个想法:数据库不用永远是你所想或所需 ]]>

        -你可能想把它映射到一个智能的对象模型,包含一个作者写的博客, -有很多的博文,每篇博文有零条或多条的评论和标签。 -下面是一个完整的复杂结果映射例子 -(假设作者,博客,博文,评论和标签都是类型的别名) 我们来看看。 -但是不用紧张,我们会一步一步来说明。 -虽然它可能看起来令人望而生畏,但实际上非常简单。 +你可能想把它映射到一个智能的对象模型,这个对象表示了一篇博客,它由某位作者所写, +有很多的博文,每篇博文有零或多条的评论和标签。 +我们来看看下面这个完整的例子,它是一个非常复杂的 ResultMap +(假设作者,博客,博文,评论和标签都是类型的别名)。 +不用紧张,我们会一步一步来说明。 +虽然它看起来令人望而生畏,但其实非常简单。

        - + @@ -736,42 +731,42 @@ MyBatis MyBatis 创建的一个想法:数据库不用永远是你所想或所需 ]]>

        -resultMap 元素有很多子元素和一个值得讨论的结构。 -下面是 resultMap 元素的概念视图 +resultMap 元素有很多子元素和一个值得讨论的结构。 +下面是 resultMap 元素的概念视图。

        resultMap

        • - constructor - 类在实例化时,用来注入结果到构造方法中 + constructor - 用于在实例化类时,注入结果到构造方法中
            -
          • idArg - ID 参数;标记结果作为 ID 可以帮助提高整体效能
          • -
          • arg - 注入到构造方法的一个普通结果
          • +
          • idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
          • +
          • arg - 将被注入到构造方法的一个普通结果
        • -
        • id – 一个 ID 结果;标记结果作为 ID 可以帮助提高整体效能
        • +
        • id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
        • result – 注入到字段或 JavaBean 属性的普通结果
        • - association – 一个复杂的类型关联;许多结果将包成这种类型 + association – 一个复杂类型的关联;许多结果将包装成这种类型
            -
          • 嵌入结果映射 – 结果映射自身的关联,或者参考一个 +
          • 嵌套结果映射 – 关联可以指定为一个 resultMap 元素,或者引用一个
        • - collection – 复杂类型的集 + collection – 一个复杂类型的集合
            -
          • 嵌入结果映射 – 结果映射自身的集,或者参考一个
          • +
          • 嵌套结果映射 – 集合可以指定为一个 resultMap 元素,或者引用一个
        • - discriminator – 使用结果值来决定使用哪个结果映射 + discriminator – 使用结果值来决定使用哪个 resultMap
          • case – 基于某些值的结果映射
              -
            • 嵌入结果映射 – 这种情形结果也映射它本身,因此可以包含很多相 - 同的元素,或者它可以参照一个外部的结果映射。 +
            • 嵌套结果映射 – 一个 case 也是一个映射它本身的结果,因此可以包含很多相 + 同的元素,或者它可以参照一个外部的 resultMap
          • @@ -780,7 +775,7 @@ resultMap 元素有很多子元素和一个值得讨论的结构。

        - + @@ -794,12 +789,12 @@ resultMap 元素有很多子元素和一个值得讨论的结构。 - - @@ -807,14 +802,14 @@ resultMap 元素有很多子元素和一个值得讨论的结构。

        最佳实践 -通常逐步建立结果映射。单元测试的真正帮助在这里。如果你尝试一次创建一个像上面示例那样的巨大的结果映射, -那么可能会有错误而且很难去控制它来工作。 -开始时简单一些,一步一步的发展。而且要进行单元测试!使用该框架的缺点是它们有时是黑盒(是否可见源代码)。 -你确定你实现想要的行为的最好选择是编写单元测试。它也可以你帮助得到提交时的错误。 +最好一步步地建立结果映射。单元测试可以在这个过程中起到很大帮助。如果你尝试一次创建一个像上面示例那样的巨大的结果映射, +那么很可能会出现错误而且很难去使用它来完成工作。 +从最简单的形态开始,逐步进化。而且别忘了单元测试!使用框架的缺点是有时候它们看上去像黑盒子(无论源代码是否可见)。 +为了确保你实现的行为和想要的一致,最好的选择是编写单元测试。提交 bug 的时候它也能起到很大的作用。

        -下面一部分将详细说明每个元素。 +下一部分将详细说明每个元素。

        id & result

        @@ -823,17 +818,16 @@ resultMap 元素有很多子元素和一个值得讨论的结构。 ]]>

        -这些是结果映射最基本内容。id 和 result 都映射一个单独列的值到简单数据类型(字符 -串,整型,双精度浮点数,日期等)的单独属性或字段。 +这些是结果映射最基本的内容。id 和 result 都将一个列的值映射到一个简单数据类型(字符串,整型,双精度浮点数,日期等)的属性或字段。

        -这两者之间的唯一不同是 id 表示的结果将是当比较对象实例时用到的标识属性。这帮 -助来改进整体表现,特别是缓存和嵌入结果映射(也就是联合映射)。 +这两者之间的唯一不同是, id 表示的结果将是对象的标识属性,这会在比较对象实例时用到。 +这样可以提高整体的性能,尤其是缓存和嵌套结果映射(也就是联合映射)的时候。

        -每个都有一些属性: +两个元素都有一些属性:

        ResultMap AttributesResultMap 的属性列表
        属性
        type类的全限定名, 或者一个类型别名 (内置的别名可以参考上面的表格). + 类的完全限定名, 或者一个类型别名 (内置的别名可以参考上面的表格).
        autoMapping如果设置这个属性,MyBatis将会为这个ResultMap开启或者关闭自动映射。这个属性会覆盖全局的属性autoMappingBehavior。默认值为:unset。 + 如果设置这个属性,MyBatis将会为这个ResultMap开启或者关闭自动映射。这个属性会覆盖全局的属性 autoMappingBehavior。默认值为:unset。
        @@ -849,7 +843,7 @@ resultMap 元素有很多子元素和一个值得讨论的结构。 @@ -870,25 +864,23 @@ resultMap 元素有很多子元素和一个值得讨论的结构。 的列表) 。如果你映射到一个 JavaBean,MyBatis 通常可以断定类型。 然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType -来保证所需的行为。 +来保证期望的行为。 @@ -946,14 +938,13 @@ jdbcType

        构造方法

        -对于大多数数据传输对象(Data Transfer Object,DTO)类型,属性可以起作用,而且像 -你绝大多数的领域模型, -可能有些情况下你想使用不可变类。 -通常包含引用或查询数 -据的表很少或基本不变的话对不可变类来说是合适的。 +通过修改对象属性的方式,可以满足大多数的数据传输对象(Data Transfer Object,DTO)以及绝大部分领域模型的要求。 +但有些情况下你想使用不可变类。 +通常来说,很少或基本不变的、包含引用或查询数 +据的表,很适合使用不可变类。 构造方法注入允许你在初始化时 -为类设置属性的值,而不用暴露出公有方法。MyBatis 也支持私有属性和私有 JavaBeans 属 -性来达到这个目的,但是一些人更青睐构造方法注入。构造方法元素支持这个。 +为类设置属性的值,而不用暴露出公有方法。MyBatis 也支持私有属性和私有 JavaBeans 属 +性来达到这个目的,但有一些人更青睐于构造方法注入。constructor 元素就是为此而生的。

        @@ -980,7 +971,9 @@ jdbcType ]]>

        -当你在处理一个带有多个形参的构造方法时,对arg元素顺序的维持是很容易出错的。为了能利用构造方法形参的name来对形参进行引用,你可以添加 @Param 注解或者使用'-parameters'编译选项和启用 useActualParamName (此选项默认开启)来编译工程。 +当你在处理一个带有多个形参的构造方法时,很容易在保证 arg 元素的正确顺序上出错。 +从版本 3.4.3 开始,可以在指定参数名称的前提下,以任意顺序编写 arg 元素。 +为了通过名称来引用构造方法参数,你可以添加 @Param 注解,或者使用 '-parameters' 编译选项并启用 useActualParamName 选项(默认开启)来编译项目。 下面的例子对于同一个构造方法依然是有效的,尽管第二和第三个形参顺序与构造方法中声明的顺序不匹配。

        @@ -991,11 +984,11 @@ jdbcType ]]>

        - 如果存在具有同名和相同类型的属性,那么它的 javaType 可以省略。 + 如果类中存在名称和类型相同的属性,那么可以省略 javaType

        -剩余的属性和规则和固定的 id 和 result 元素是相同的。 +剩余的属性和规则和普通的 id 和 result 元素是一样的。

        property 映射到列结果的字段或属性。如果用来匹配的 JavaBeans 存在给定名字的属性,那么它将会被使用。否则 MyBatis 将会寻找给定名称 property 的字段。 -这两种情形你可以使用通常点式的复杂属性导航。比如,你可以这样映射一些东西: +无论是哪一种情形,你都可以使用通常的点式分隔形式进行复杂属性导航。比如,你可以这样映射一些简单的东西: “username” ,或者映射到一些复杂的东西: “address.street.number” @@ -859,8 +853,8 @@ resultMap 元素有很多子元素和一个值得讨论的结构。
        column -从数据库中得到的列名,或者是列名的重命名标签。这也是通常和会 -传递给 resultSet.getString(columnName)方法参数中相同的字符串。 +数据库中的列名,或者是列的别名。一般情况下,这和 +传递给 resultSet.getString(columnName) 方法的参数一样。
        jdbcType -在这个表格之后的所支持的 JDBC 类型列表中的类型。JDBC 类型是仅 -仅需要对插入,更新和删除操作可能为空的列进行处理。这是 JDBC -jdbcType -的需要,而不是 MyBatis 的。如果你直接使用 JDBC 编程,你需要指定 -这个类型-但仅仅对可能为空的值。 +JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 +只需要在可能执行插入、更新和删除的允许空值的列上指定 JDBC 类型。这是 JDBC +的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可能为 null 的值指定这个类型。
        typeHandler -我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默 -认的类型处理器。这个属性值是类的完全限定名或者是一个类型处理 -器的实现,或者是类型别名。 +我们在前面讨论过的默认类型处理器。使用这个属性,你可以覆盖默 +认的类型处理器。这个属性值是一个类型处理 +器实现类的完全限定名,或者是类型别名。
        @@ -1009,8 +1002,8 @@ jdbcType @@ -1018,51 +1011,48 @@ resultSet.getString(columnName)方法的字符串是相同的。
        column -来自数据库的列名,或重命名的列标签。这和通常传递给 -resultSet.getString(columnName)方法的字符串是相同的。 +数据库中的列名,或者是列的别名。一般情况下,这和 +传递给 resultSet.getString(columnName) 方法的参数一样。
        一个 Java 类的完全限定名,或一个类型别名(参考上面内建类型别名的列表)。 如果你映射到一个 JavaBean,MyBatis 通常可以断定类型。然而,如 -果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证所需的 +果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证期望的 行为。
        jdbcType -在这个表格之前的所支持的 JDBC 类型列表中的类型。JDBC 类型是仅仅 -需要对插入, -更新和删除操作可能为空的列进行处理。这是 JDBC 的需要, -而不是 MyBatis 的。如果你直接使用 JDBC 编程,你需要指定这个类型-但 -仅仅对可能为空的值。 +JDBC 类型,所支持的 JDBC 类型参见这个表格之前的“支持的 JDBC 类型”。 +只需要在可能执行插入、更新和删除的允许空值的列上指定 JDBC 类型。这是 JDBC +的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可能为 null 的值指定这个类型。
        typeHandler -我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的 -类型处理器。 -这个属性值是类的完全限定名或者是一个类型处理器的实现, -或者是类型别名。 +我们在前面讨论过的默认类型处理器。使用这个属性,你可以覆盖默 +认的类型处理器。这个属性值是一个类型处理 +器实现类的完全限定名,或者是类型别名。
        select - 用于加载复杂类型属性的映射语句的ID,从column中检索出来的数据,将作为此select语句的参数。具体请参考Association标签。 + 用于加载复杂类型属性的映射语句的 ID,它会从 column 属性中指定的列检索数据,作为参数传递给此 select 语句。具体请参考 Association 标签。
        resultMap - ResultMap的ID,可以将嵌套的结果集映射到一个合适的对象树中,功能和select属性相似,它可以实现将多表连接操作的结果映射成一个单一的ResultSet。这样的ResultSet将会将包含重复或部分数据重复的结果集正确的映射到嵌套的对象树中。为了实现它, MyBatis允许你 “串联” ResultMap,以便解决嵌套结果集的问题。想了解更多内容,请参考下面的Association元素。 + ResultMap 的 ID,可以将嵌套的结果集映射到一个合适的对象树中,功能和 select 属性相似,它可以实现将多表连接操作的结果映射成一个单一的ResultSet。这样的ResultSet将会将包含重复或部分数据重复的结果集正确的映射到嵌套的对象树中。为了实现它, MyBatis允许你 “串联” ResultMap,以便解决嵌套结果集的问题。想了解更多内容,请参考下面的Association元素。
        name - 构造方法形参的名字。通过指定具体的名字你可以以任意顺序写入arg元素。参看上面的解释。从3.4.3版本起。 + 构造方法形参的名字。从3.4.3版本开始,通过指定具体的名字,你可以以任意顺序写入arg元素。参看上面的解释。
        -

        关联

        +

        关联

        From f8e45dfbe1795ebf1826a5949a3a6fb09a5b07c2 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Thu, 14 Dec 2017 04:19:41 +0900 Subject: [PATCH 0036/2062] Inlined nodeHandlers() method. refs #1149 --- .../scripting/xmltags/XMLScriptBuilder.java | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/XMLScriptBuilder.java b/src/main/java/org/apache/ibatis/scripting/xmltags/XMLScriptBuilder.java index 736a9021a67..f78551b0d1c 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/XMLScriptBuilder.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/XMLScriptBuilder.java @@ -51,16 +51,16 @@ public XMLScriptBuilder(Configuration configuration, XNode context, Class par } - private void initNodeHandlerMap(){ - nodeHandlerMap.put("trim", new TrimHandler()); - nodeHandlerMap.put("where", new WhereHandler()); - nodeHandlerMap.put("set", new SetHandler()); - nodeHandlerMap.put("foreach", new ForEachHandler()); - nodeHandlerMap.put("if", new IfHandler()); - nodeHandlerMap.put("choose", new ChooseHandler()); - nodeHandlerMap.put("when", new IfHandler()); - nodeHandlerMap.put("otherwise", new OtherwiseHandler()); - nodeHandlerMap.put("bind", new BindHandler()); + private void initNodeHandlerMap() { + nodeHandlerMap.put("trim", new TrimHandler()); + nodeHandlerMap.put("where", new WhereHandler()); + nodeHandlerMap.put("set", new SetHandler()); + nodeHandlerMap.put("foreach", new ForEachHandler()); + nodeHandlerMap.put("if", new IfHandler()); + nodeHandlerMap.put("choose", new ChooseHandler()); + nodeHandlerMap.put("when", new IfHandler()); + nodeHandlerMap.put("otherwise", new OtherwiseHandler()); + nodeHandlerMap.put("bind", new BindHandler()); } public SqlSource parseScriptNode() { @@ -91,7 +91,7 @@ List parseDynamicTags(XNode node) { } } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628 String nodeName = child.getNode().getNodeName(); - NodeHandler handler = nodeHandlers(nodeName); + NodeHandler handler = nodeHandlerMap.get(nodeName); if (handler == null) { throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement."); } @@ -102,10 +102,6 @@ List parseDynamicTags(XNode node) { return contents; } - NodeHandler nodeHandlers(String nodeName) { - return nodeHandlerMap.get(nodeName); - } - private interface NodeHandler { void handleNode(XNode nodeToHandle, List targetContents); } @@ -237,7 +233,7 @@ private void handleWhenOtherwiseNodes(XNode chooseSqlNode, List ifSqlNo List children = chooseSqlNode.getChildren(); for (XNode child : children) { String nodeName = child.getNode().getNodeName(); - NodeHandler handler = nodeHandlers(nodeName); + NodeHandler handler = nodeHandlerMap.get(nodeName); if (handler instanceof IfHandler) { handler.handleNode(child, ifSqlNodes); } else if (handler instanceof OtherwiseHandler) { From 28ffd4ffe2cb9f774a2a7e288b114fefdb5d7901 Mon Sep 17 00:00:00 2001 From: Jason Bennett Date: Mon, 18 Dec 2017 15:05:02 -0800 Subject: [PATCH 0037/2062] Clarify how the tag interacts with the Java API. --- src/site/xdoc/java-api.xml | 3 ++- src/site/xdoc/sqlmap-xml.xml | 9 ++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/site/xdoc/java-api.xml b/src/site/xdoc/java-api.xml index 2bdf5709024..65b96d2face 100644 --- a/src/site/xdoc/java-api.xml +++ b/src/site/xdoc/java-api.xml @@ -344,7 +344,8 @@ try (SqlSession session = sqlSessionFactory.openSession()) {
    Class <cacheRef> - References the cache of another namespace to use. Attributes: value and name. + References the cache of another namespace to use. Note that caches declared in an XML mapper file are considered + a separate namespace, even if they share the same FQCN. Attributes: value and name. If you use this annotation, you should be specified either value or name attribute. For the value attribute specify a java type indicating the namespace(the namespace name become a FQCN of specified java type), and for the name attribute(this attribute is available since 3.4.2) specify a name indicating the namespace. diff --git a/src/site/xdoc/sqlmap-xml.xml b/src/site/xdoc/sqlmap-xml.xml index 9b38d5e4cc8..7b9b2621a37 100755 --- a/src/site/xdoc/sqlmap-xml.xml +++ b/src/site/xdoc/sqlmap-xml.xml @@ -2013,7 +2013,7 @@ SELECT * FROM POST WHERE BLOG_ID = #{id}]]>

    - By default, just local sessión caching is enabled that is used solely to cache data for the duration of a sessión. + By default, just local session caching is enabled that is used solely to cache data for the duration of a session. To enable a global second level of caching you simply need to add one line to your SQL Mapping file:

    @@ -2037,6 +2037,13 @@ SELECT * FROM POST WHERE BLOG_ID = #{id}]]> +

    + NOTE The cache will only apply to statements declared in the mapping file + where the cache tag is located. If you are using the Java API in conjunction with the XML mapping files, then + statements declared in the companion interface will not be cached by default. You will need to refer to the + cache region using the @CacheNamespaceRef annotation. +

    +

    All of these properties are modifiable through the attributes of the cache element. For example:

    From bacd59cb77db43de8c3941eba8229d490784a8db Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Thu, 21 Dec 2017 01:36:12 +0900 Subject: [PATCH 0038/2062] Apply the changes to the other languages. --- src/site/es/xdoc/java-api.xml | 2 +- src/site/es/xdoc/sqlmap-xml.xml | 7 +++++++ src/site/ja/xdoc/java-api.xml | 2 +- src/site/ja/xdoc/sqlmap-xml.xml | 4 ++++ src/site/ko/xdoc/java-api.xml | 2 +- src/site/ko/xdoc/sqlmap-xml.xml | 7 +++++++ src/site/zh/xdoc/java-api.xml | 2 +- src/site/zh/xdoc/sqlmap-xml.xml | 7 +++++++ 8 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/site/es/xdoc/java-api.xml b/src/site/es/xdoc/java-api.xml index fbe198014bd..7f3aac391cd 100644 --- a/src/site/es/xdoc/java-api.xml +++ b/src/site/es/xdoc/java-api.xml @@ -334,7 +334,7 @@ try (SqlSession session = sqlSessionFactory.openSession()) {
    Class <cacheRef> - Referencia una cache de otro namespace. Atributos: value and name. + Referencia una cache de otro namespace. Note that caches declared in an XML mapper file are considered a separate namespace, even if they share the same FQCN. Atributos: value and name. If you use this annotation, you should be specified either value or name attribute. For the value attribute specify a java type indicating the namespace(the namespace name become a FQCN of specified java type), and for the name attribute(this attribute is available since 3.4.2) specify a name indicating the namespace. diff --git a/src/site/es/xdoc/sqlmap-xml.xml b/src/site/es/xdoc/sqlmap-xml.xml index 837d705f982..56ae5ae279b 100644 --- a/src/site/es/xdoc/sqlmap-xml.xml +++ b/src/site/es/xdoc/sqlmap-xml.xml @@ -1659,6 +1659,13 @@ SELECT * FROM POST WHERE BLOG_ID = #{id}]]>
  • La caché puede tratarse como una cache de tipo lectura/escritura, lo cual significa que los objetos obtenidos no se comparten y pueden modificarse con seguridad por el llamante sin interferir en otras potenciales modificaciones realizadas por otros llamantes o hilos.
  • +

    + NOTE The cache will only apply to statements declared in the mapping file + where the cache tag is located. If you are using the Java API in conjunction with the XML mapping files, then + statements declared in the companion interface will not be cached by default. You will need to refer to the + cache region using the @CacheNamespaceRef annotation. +

    +

    Todas estas propiedades son modificables mediante atributos del elemento cache. Por ejemplo:

    diff --git a/src/site/ja/xdoc/java-api.xml b/src/site/ja/xdoc/java-api.xml index 869ab4fecbf..17ef66919cf 100644 --- a/src/site/ja/xdoc/java-api.xml +++ b/src/site/ja/xdoc/java-api.xml @@ -335,7 +335,7 @@ try (SqlSession session = sqlSessionFactory.openSession()) {
    Class <cacheRef> - 別のネームスペースに対して定義されているキャッシュの設定を参照します。 属性: value, name + 別のネームスペースに対して定義されているキャッシュの設定を参照します。XML マッパーで宣言されているキャッシュは、namespace に同一 FQCN が指定されていても独立したキャッシュとして扱われます。属性: value, name このアノテーションを使用する場合は、valueまたはname属性のどちらかを指定する必要があります。 value属性にはネームスペースを示すJava型(ネームスペース名は指定したJava型のFQCNになる)を、 name属性(この属性は3.4.2以降で利用可能)にはネームスペースを示す名前を指定します。 diff --git a/src/site/ja/xdoc/sqlmap-xml.xml b/src/site/ja/xdoc/sqlmap-xml.xml index 1036d7a9222..cd4d325fb0f 100644 --- a/src/site/ja/xdoc/sqlmap-xml.xml +++ b/src/site/ja/xdoc/sqlmap-xml.xml @@ -1876,6 +1876,10 @@ SELECT * FROM POST WHERE BLOG_ID = #{id}]]>
  • このキャッシュは読み書き可能なキャッシュとして扱われます。これはつまり、取得したオブジェクトは共有されず、呼び出した側で安全に変更することができる(別の呼び出し元や他スレッドでの変更の影響を受けない)ということを意味しています。
  • +

    + 重要 対応する Java マッパーのステートメントをキャッシュの適用対象に含めるためには @CacheNamespaceRef アノテーションで XML マッパーのネームスペースを指定する必要があります。 +

    +

    これらのプロパティは cache 要素の属性で変更することができます。

    diff --git a/src/site/ko/xdoc/java-api.xml b/src/site/ko/xdoc/java-api.xml index 9a685ed97d7..7901c73db01 100644 --- a/src/site/ko/xdoc/java-api.xml +++ b/src/site/ko/xdoc/java-api.xml @@ -431,7 +431,7 @@ try (SqlSession session = sqlSessionFactory.openSession()) {
    Class <cacheRef> - 다른 명명공간의 캐시에 대한 참조 사용가능한 속성들 : value, name. + 다른 명명공간의 캐시에 대한 참조 Note that caches declared in an XML mapper file are considered a separate namespace, even if they share the same FQCN. 사용가능한 속성들 : value, name. 이 annotation 을 사용하려면 value 또는 name 속성을 지정해야 한다. value 속성은 namespace(namespace 이름은 지정된 java type 의 FQCN 이 된다) 를 나타내는 java type 을 지정한다, 그리고 name 속성(이 속성은 3.4.2 부터 사용가능하다) 은 namespace 를 나타내는 이름을 지정한다. diff --git a/src/site/ko/xdoc/sqlmap-xml.xml b/src/site/ko/xdoc/sqlmap-xml.xml index eae52a97746..17d5b374167 100644 --- a/src/site/ko/xdoc/sqlmap-xml.xml +++ b/src/site/ko/xdoc/sqlmap-xml.xml @@ -1600,6 +1600,13 @@ public class User {
  • 캐시는 읽기/쓰기 캐시처럼 처리될 것이다. 이것은 가져올 객체는 공유되지 않고 호출자에 의해 안전하게 변경된다는 것을 의미한다.
  • +

    + NOTE The cache will only apply to statements declared in the mapping file + where the cache tag is located. If you are using the Java API in conjunction with the XML mapping files, then + statements declared in the companion interface will not be cached by default. You will need to refer to the + cache region using the @CacheNamespaceRef annotation. +

    +

    모든 프로퍼티는 cache 엘리먼트의 속성을 통해 변경가능하다. 예를들면

    diff --git a/src/site/zh/xdoc/java-api.xml b/src/site/zh/xdoc/java-api.xml index 7c56c89c99c..92534ae98b3 100644 --- a/src/site/zh/xdoc/java-api.xml +++ b/src/site/zh/xdoc/java-api.xml @@ -541,7 +541,7 @@ flushInterval,size,readWrite,blocking 和 properties。
    <cacheRef> - 参照另外一个命名空间的缓存来使用。属性:value, name。 + 参照另外一个命名空间的缓存来使用。Note that caches declared in an XML mapper file are considered a separate namespace, even if they share the same FQCN. 属性:value, name。 If you use this annotation, you should be specified either value or name attribute. For the value attribute specify a java type indicating the namespace(the namespace name become a FQCN of specified java type), and for the name attribute(this attribute is available since 3.4.2) specify a name indicating the namespace. diff --git a/src/site/zh/xdoc/sqlmap-xml.xml b/src/site/zh/xdoc/sqlmap-xml.xml index 97f0b65cceb..79c58d30e32 100644 --- a/src/site/zh/xdoc/sqlmap-xml.xml +++ b/src/site/zh/xdoc/sqlmap-xml.xml @@ -1769,6 +1769,13 @@ MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地 +

    + NOTE The cache will only apply to statements declared in the mapping file + where the cache tag is located. If you are using the Java API in conjunction with the XML mapping files, then + statements declared in the companion interface will not be cached by default. You will need to refer to the + cache region using the @CacheNamespaceRef annotation. +

    +

    所有的这些属性都可以通过缓存元素的属性来修改。比如:

    From 097239ad6cff48c8e877e64ec53c2cf9cb346788 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Thu, 21 Dec 2017 01:45:40 +0900 Subject: [PATCH 0039/2062] Added a missing close tag. --- src/site/zh/xdoc/sqlmap-xml.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/site/zh/xdoc/sqlmap-xml.xml b/src/site/zh/xdoc/sqlmap-xml.xml index 79c58d30e32..447f4b48045 100644 --- a/src/site/zh/xdoc/sqlmap-xml.xml +++ b/src/site/zh/xdoc/sqlmap-xml.xml @@ -756,7 +756,7 @@ ResultMap 就是 MyBatis 对这个问题的答案。
  • collection – 一个复杂类型的集合
      -
    • 嵌套结果映射 – 集合可以指定为一个 resultMap 元素,或者引用一个 +
    • 嵌套结果映射 – 集合可以指定为一个 resultMap 元素,或者引用一个
  • From 036cc5c618a03e7091ab4aca91d51c7e4a4448f9 Mon Sep 17 00:00:00 2001 From: Jeremy Landis Date: Tue, 26 Dec 2017 19:16:26 -0500 Subject: [PATCH 0040/2062] [mvn] Upgrade maven wrapper to 0.3.0 --- .mvn/wrapper/maven-wrapper.jar | Bin 47610 -> 47774 bytes mvnw | 6 ++++-- mvnw.cmd | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar index 9cc84ea9b4d95453115d0c26488d6a78694e0bc6..41c70a7e0b7da7ecbbcf53b62aacdf8bc81e10b0 100644 GIT binary patch delta 3716 zcmYM$byO5@7YA^50EGovN$HSYTImu5mTsh#Qc76brDQ=mqy|MwkZxGIL?o3|>F!Qx zDPQEg=ltfc&;8Cb_c_nZnRD*h0r2q%i1kKS6Rh_)6f)vb)RM$=5;=nWZ^@mUOte3h zPTlzn+x-oju(yPSoF}u-(X@-HeNOEi?T|l%s zvyHcqO{P2IbGH7D}aR;ySFygE8GYQn-cfJ>R)*c%|zduf^ zgj#U7WU)g`y656%B z#W1p^TN2au`SrQC#M!M77|Mf?fZL(6Hz+HqKbAs$L;4gKZu%t|#Ggh=+jqDp_m2Mj z`H-ddygYW{;RRZ&O-|}I5zOOodt!8UUasEXNs-0=LrA4=Mt%)aDWk5B`X@Q}rybvP z6zkiyFywi;UIzDK5JQi4R)#k<%cTuTG`%hc;KHSdTGuFZB$ir@YU!LvG9T(Q-M12Y z107K_*lgBy5g37Tu(R@?BM$Vu$2p2CNH(81v>*b0(V^G#i=g!nm*jXp;R$usbgLi1 zsegP^j|W!5WCoiol@^yL9QBD?trxZpXH)}Nt8sm~16%aTSvg)@HQdqNRql*lJNAFY zQm9IAM9d?eJs#rw>fR+st1#7&rT7-D5D9Q+`XKj#ogwYw6LY)mPmg8rP9-!H59_RA z-$F~t@_W%LFJ?}i-&G;mp9)tMju%ayQBL~)ikxn&&vb)5-dOObn6+`uOfKu8 z5sC8;P1@u9wf1M;vB!R8*1BzE_95gGj`BwooZ}_;qv!)V?7j<55~kS}m!|QWnfsJQ zT)H+|n!F!Gj=7eYb@;0pdKoXrBH1bJs1|8-T(Cyb73Re(Ma}T6vczVID49`99%P|frJ?PD{ZdelmiYh!u z@SMv?lV|I%h!3b~?3ExrdNS{09aN8H`l~x`7cZn$gezt>l*}Ol&Wx5Pj=!J=U)z$!mLz zP1d^aRMhXf_?$a>22}aB#teGKHThr&=C>@KUpOT_wN3UI9ZxLJ>xVZ-K1p&uWt7nW zk~JQE_=m^=#PO<+FqnJCO*yTO_t1gN@^GvFIf-DX8WU`4#jvfq^Fn6*m{=8k zEc(@2OF9i@8n&wKrfrGP=H@Y-yAV!I){xVAd6lrd+Bi%3P>9`baBIfP-JkBjVp?OQ z)da`b<|r@=!SQ8pve;1Vq2f|rG&YRb4fGMI;q*+AIY@1AJwEQ{?RtBnq(Ai*mG8;E zW0gwWLo3udCCZl+Sc6+#&$C9~WYU55*?Tr#lc~Kql;JhS(sklxChu4La&?V7cvvN8 z*WfqAS1Y64aR8tT2Kn=>4W^Z%uPCU_F5&bofzQp_19dYl(Kyn&n$x{rDqlm#>2l** z14mVpWG1H&DaGHDh4n6w11bsfk%$D_}?}QDXHbgkhrL^~r zENb&)?h0?=e0TeEAuSr5yN!0M>xg0Bdx|@2(bf&EQmDqU3E7=Lr)LamW$G~0+ve3+WtKuW!}nVUJga02xcQRq5shRjF_I z+#gEA8E>D=aj^HBql5P7=7$)js+ceZNh|UgxC+jIcuqo$-O?mE?&+(1|J%WMD99#N z6^CvxI^xWAC|1q14DVx@!4mKj-RWL6NTuc_9BEBrBrU~&6O>>yEY)>Pk)BUBMgc_; zRtb9?Z)w!xw%Na$lJh^A5*%h*(04GtlwvB>!{~7zz%w<`nsqEM)jdOjfQE6+)Gg{X zHpSfnFmz74DYpySa4XP=SxxyDt=;=d$uDssh6NE%EQNIWR;#&V_J@9RbHAD42Az{^ zGCJ8u1oV=X_-?76#8RuZAB73nuQAKE5z@!y&1RG+x-;+WZ72+F-J@w|Q*h`NyIlI9 zt572O?Y(>^IyJ>N^W!Qg%%vtT=lxsT~@GyUY>L{qn<_|8C<@A`e_OK5o352+qi{cFs?U{_*E*aW!hxuk+&U^=K( z{KST$jO061FPOd&VI>U3ELLpQ=%57FklIoZN$kV};}EX$G#wtKJRp%xO`IM2YtjT; z0-Kz)PCU4Vj=NYyA(t2z%c8_0Z!WFmdct${lpPX zbC%#N^l*Ym3Wqqvr%|V5NOR$Ej|E9dG^O|SL39ec%j6!;M~Udwn8jeQF0lJqa%)co zu|wPAs}a(1#Qm`#R$Df_6wECXOf<rMKSUs8`cSeF86s`eo{**iOSma~GM?Eh^f5)>TC+iNE4mwp3F>%*x{K=>D} zTXThD2%gwxF>jY1$ZEcr@M4%&u}JObYkiHbXmj9x1=9xAoapA$+r`4|=CT&i zo}K`M6~(W}BQaH@pq{64KDJGy)nh4b@5JvBD>q-RDbqA?t5@MhtqSa#3Dd_Q=V{3a zOfzyKoDm8yjgd*4y3m+|zpL10P+!*IvbT#Se>RR+^G)7sk1A_Qd7M1ps-Qi-B@dm| zn7_?=!5uv7J{=mY)*d_`;&SmvQeBb?eQE&xTTd|TiqQ#M2-Ola)ti096ww*%I^%{o z?d*6{Sy}?c2}(ZB?kqXMHf^MMN7<CrTJT-gP7D} zqJ32iq|5L{7bf@}2Aj>Jy?%BHNie$C9GO7SX8nK^xpWkjL%5x36l6-9+-|KOl@V?K z3e+*)3{WAgsWWPn0)-;!RT64dak|I;G;A;gZ&=H;GH8u^hcvn-F7;uLEwJ4ljCW1` zV#D}%PA8lO0~N7gM)qsvlHC!6F9(gmQ4y+F1plV&MLGO^H~|)x0wp*vOIvx$JLiRa zGkJs4`34YXP^dr>u!MlHLK-l-01g!WBPW-ZDg+?IBE!PN!@C~HnXvvF&9ShAu9N>R z03<53grtKmg#wU4Fso1$Ai@Tj1W3WXLO7uLkF=m@5hw8BA8A0pqU+xh|Aywp#Nb|$ zF3?8s|56r+CB0#Du{e-OcEdh07-)yNOZ(q3u(0_4+a2o^a?X6S@MVb%@P_Z29N=)t zW1v9bn!NuW1r186fyZJui=s;vfoSO)?w85~p^tA!{g@oIErSE@svstt5v(fX1iq?* z+U5-4Rhb&VrTKs2f!!J`AY=JmFt3~%(9*k(;ox?;EI_P(!>w%C&0Q}HHPC8s6Mex5 z142))DGc&dJOakeuPFzjitmD7E2x1)|Rlexj8N2Lz$eJy(lbOm$_UzJR&mKQ}_GOZNEMq4^%917f z`cTO(*^=t@x}NKL&Oi6}^S#e?pL6c>&z*AsocsV@1mK&Kw#&l#4RoizU7u~s!B|q^$ zp_fPz9Gp(wL}=x?c@&8)dqw;9So*&edA}$AHb{L*R^ev#RNimBEn7~@CN+Db_jM63 zYT1KzmYA;wwB0Y*Gixg{*tS)unlITbc-(eaIq7~#ZlnjLs!gX&epQQvXhI;L!Fzxh zjgh$>Ql5;AOpXlXf*@;~fFv&b6Sgmizxb$ZFr`1q1IY(txYC^(seL(I_A(N_?6-`# zmG@OB*M(xuKrVZ;C??g_s+n)Tm~NhKSXV?OOvG`mAdnEabCa+5H{Q1<^}*!t>CyS1 zqxPePBmCd9u$IWT9Z2>@nW)!^4z?Q0q;V1#maKpl&cwQsPn z>Nld^^sdarGRHdhnpV4=Hlk`M(6ciNRuL_cy#ZF5GlB0SptI0k*wH~9?QwJMXebFG zO~@oKrS0xLfu5YqLL)TaHP2{KOw?y;&P`kl)QI8ng0})}$PQLUk9nso=|XvZ=5@y% z_QWwI3$5=rpZdWf@K-V1&N8&+m9Nkd9{(+?a1K zg^bp)cWu7#ZkiHlTPP-wx2l>8(iON-Hro6laU;JyZ^uPj;mKunDCg4Y9W6#!NB!h{ zHttB~CHl=0e%)Sb&Ah6ddSmG-qS)fCqjw}-n+yZ{WCfRtVu9X7I+w6SfhFeW=EVRa zYg*q;m%O)!Ua}%alWo}LB(H16b`;W+NI}Z5XAQsaYbQ)&JPjdV`fG1z$k+u06-PVR z-27Qr!JXMNC5$f@ccxV6VrbLRC6&mAnFZ@Vd@m`}B_E{H!F#v8Z{G*O?%9mZAj~t9 zcp&hyZ`}+8?Nq1uW?|YxMjzQr1MMD6-euozRJ}P&cj)m>tFcUk^~aJdOiXK9_$B26 z0_!d#4)3XU*2g{)4@FaoYf$!F^3mhz(w<-%sA5kPiNlx3MsHBnNQ)bPqlXBRR7RS6Lsf<7BTY~^QcgN(iN5#uy@_Ex!r1F)}gJ@z$CVILGMHT8fbMD+mKPK+5IUi zT1wbo`5t~iH#VWH(UP!yQg`~?wTB(H@%)1ZR$@~)X|bn9{YLCKPDkrRDzNz)YO=7q zGl@X$Wb?I8UduK8+J3dk4Sk-XFB`2?7_)_JuVLGO@Qh+9Vc}%lyvLzHxs6^l2Ufbr zMY*q8%<)mGaFBAi9*=KMAx?s5tw~6S8c?H0vJ{0xv5A-g zJ}xMhLUI1`-DI7YG3FE#bJI@@l5m2nq76NryUF>})G-bT<$2CX8|sgl8J9(|(>9}U z*MB;@oJO293p2&J8t6%M*PGdfX5FJg(x@aw$^>z)9YS$gel4FnUHqD&&6iSaaD9@z zZ}BEW@}hPTB{%Ek>bHGbt$H5qT3K#fWq9l})i}*r_G>O%(;h!45nkA6^?LsRz5Rxb zTsJ#&Xt2PKK6QMvsS7+34?e}f zmtsTUqs7;h&^D`>=3dW&W!E`%n8)JX*1OyM3%5RRPQUujh2Zeft=U9M>d80@pcjiw z2i^x22>sBEUwDPJi$^23#tSr-p1N+xNTMdo=TTV?8eT#*q?8HEE63y>wfhztwD>i4 zjG)1uO?!2}Za0J3sUUdJ?cV>TF&5=nOux!ZYvc|{eqK|djw+s*}*7p6JYl0cyX|7*uvM|V4V3YG+JfZP*Uq>iDtX4ahTh?Xg{M^_tk0>krQzQ-Zrn@ zS%int7kwQ0hGfG@X?TU?BM~a_Q8Dor=j7@;b=4OVvn^>E3!gjjJfEUK%OU{fr%9J-&Oe6=So`ToM@d{0w;PjvEP-JBc~ zuG30RQfMPAihujaiqXE*t5K*(=AN+0g1VDToinI+KQi@;aj<-j_}UdXk#-)ZLh>Vr zd!&tM1Pr^$MylWy36WhOCa8-;`gCQ_bgg0k1uA~JwOP<#^17hPwXCVGC-iqVLsLa9nyz#W)G~9~8Z$+N=Zbmu z``nD55rX>-Fe;e-lh(msjuG;{7ZIu7;Mf>sck`b$lJR;cg>$R^LBNy zdruJ`j$5TyEyL4%@f*ZoZZj0~l)>mg7SnqD)kCAVceHMq|RF zo!t%4nh5=sZEQ4H8C%0y6*4)hO2dMCRpCMTdx&Wq@us*jc2Yu-I?ZIAW+82N5TZ3$ zxNlT>-w_6F+4$=24eyu_vX+#`e6}B zT=A~mfagTvOX@ju26N>02h-5{hrQH0W8kBB&|M*DriRz^)z2!Lgas^Hz-i@F}uMF>; z)jmd3Et;t{EJCJ@GkiXUvim7)dli^O`7ULDT1vs~dbP$IgBP`g^RwSQj7rLN@rF7)KYu>oDQt%(`H6)aGMBdSYS})}w7uTuJwE?lC~<#lKZ-{=r5gtx z-@df?t(kV?^7F@q-U3hl3hAHr9sS^W>JKIL-B@(f+kUq`G9KJ1cd8t&lB@q~FOBJs zPp^1A1h0ImwAE)8DXE0qxF&F-UH*uAoUD>3kr`VboQq$+`=pfjV6Me;blu~0R>v~u z%NR^WQ|*0Y!b#XpRFSIr5~e)OASLF~WXhkBWQ#Fd7Hyj1N}F>1Xm6#1D;Wp334c_n z7hTKIa$|y3bhwteG(>Q6iK3pl4}HGRD-=>q1}`u^LM>=G^&_LNxHEK!vQ6s}}5-takq_v~5z~`-&pZp5hecrR>lz z<%?(7V4Q-*^t>6Occ2@S-tMFJ2+k7l`NQ)62{r+duO3N5MyALDcG4nivqYEtDbA-_ zF!;4OCT-ckj~7@$#d0(tPkoLjv zi=bN#8c4J`Lp^Y?mL5E;VTDM647IKh8Sq)HFA$46ONoO)j@LlLIy4~bbdKe9n!sO= zbGhru2CCMh0S0ey#9JTCBhiEJ>sbLwzcZ8pktBJ*EAU(-pw6w}Gwa~_nI#4eHi`k%nP(jk14Wy} iAZDO_6AnlzI&aBZlPLfa&bD!YcFppXi6#I4f&T%J|7$A% diff --git a/mvnw b/mvnw index 5bf251c0774..e96ccd5fbbb 100755 --- a/mvnw +++ b/mvnw @@ -108,7 +108,7 @@ if $cygwin ; then CLASSPATH=`cygpath --path --unix "$CLASSPATH"` fi -# For Migwn, ensure paths are in UNIX format before anything is touched +# For Mingw, ensure paths are in UNIX format before anything is touched if $mingw ; then [ -n "$M2_HOME" ] && M2_HOME="`(cd "$M2_HOME"; pwd)`" @@ -201,7 +201,9 @@ if [ -z "$BASE_DIR" ]; then fi export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -echo $MAVEN_PROJECTBASEDIR +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" # For Cygwin, switch paths to Windows format before running java diff --git a/mvnw.cmd b/mvnw.cmd index 019bd74d766..6a6eec39baf 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -35,6 +35,8 @@ @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' @echo off +@REM set title of command window +title %0 @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% From 214824dca6a659492f19e3c9bfada7830eb75f84 Mon Sep 17 00:00:00 2001 From: Jeremy Landis Date: Tue, 26 Dec 2017 19:17:06 -0500 Subject: [PATCH 0041/2062] [ci] Add oraclejdk9 to build --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 1af60c7e4af..7473fa4bd69 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ addons: - openjdk-6-jdk jdk: + - oraclejdk9 - oraclejdk8 - openjdk8 - openjdk7 From 5b3c2eb68e2d38f133eac9cd60084725811fee02 Mon Sep 17 00:00:00 2001 From: Jeremy Landis Date: Tue, 26 Dec 2017 19:40:38 -0500 Subject: [PATCH 0042/2062] [pom] Update junit to vintage 4.12.2 --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index cbf1df3a8da..f8081fe6748 100644 --- a/pom.xml +++ b/pom.xml @@ -197,9 +197,9 @@ - junit - junit - 4.12 + org.junit.vintage + junit-vintage-engine + 4.12.2 test From a8197e178ea2fcfa5eea03fa01947ad90554f72d Mon Sep 17 00:00:00 2001 From: Jeremy Landis Date: Tue, 26 Dec 2017 19:40:55 -0500 Subject: [PATCH 0043/2062] [pom] Add patch to support java 9 builds --- pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pom.xml b/pom.xml index f8081fe6748..f37f7d89f83 100644 --- a/pom.xml +++ b/pom.xml @@ -346,6 +346,12 @@ + + + org.codehaus.mojo + animal-sniffer-maven-plugin + 1.16 + From 037cc035fa3e59a39aa16d266b4c182de5be4439 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 28 Dec 2017 03:01:51 +0800 Subject: [PATCH 0044/2062] Refactor XMLScriptBuilder#parseDynamicTags() (#1167) * Modify the parseDynamicTags's return type as MixedSqlNode and visibility to protected --- .../scripting/xmltags/XMLScriptBuilder.java | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/XMLScriptBuilder.java b/src/main/java/org/apache/ibatis/scripting/xmltags/XMLScriptBuilder.java index f78551b0d1c..955a9cd8a7b 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/XMLScriptBuilder.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/XMLScriptBuilder.java @@ -64,8 +64,7 @@ private void initNodeHandlerMap() { } public SqlSource parseScriptNode() { - List contents = parseDynamicTags(context); - MixedSqlNode rootSqlNode = new MixedSqlNode(contents); + MixedSqlNode rootSqlNode = parseDynamicTags(context); SqlSource sqlSource = null; if (isDynamic) { sqlSource = new DynamicSqlSource(configuration, rootSqlNode); @@ -75,7 +74,7 @@ public SqlSource parseScriptNode() { return sqlSource; } - List parseDynamicTags(XNode node) { + protected MixedSqlNode parseDynamicTags(XNode node) { List contents = new ArrayList(); NodeList children = node.getNode().getChildNodes(); for (int i = 0; i < children.getLength(); i++) { @@ -99,7 +98,7 @@ List parseDynamicTags(XNode node) { isDynamic = true; } } - return contents; + return new MixedSqlNode(contents); } private interface NodeHandler { @@ -127,8 +126,7 @@ public TrimHandler() { @Override public void handleNode(XNode nodeToHandle, List targetContents) { - List contents = parseDynamicTags(nodeToHandle); - MixedSqlNode mixedSqlNode = new MixedSqlNode(contents); + MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle); String prefix = nodeToHandle.getStringAttribute("prefix"); String prefixOverrides = nodeToHandle.getStringAttribute("prefixOverrides"); String suffix = nodeToHandle.getStringAttribute("suffix"); @@ -145,8 +143,7 @@ public WhereHandler() { @Override public void handleNode(XNode nodeToHandle, List targetContents) { - List contents = parseDynamicTags(nodeToHandle); - MixedSqlNode mixedSqlNode = new MixedSqlNode(contents); + MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle); WhereSqlNode where = new WhereSqlNode(configuration, mixedSqlNode); targetContents.add(where); } @@ -159,8 +156,7 @@ public SetHandler() { @Override public void handleNode(XNode nodeToHandle, List targetContents) { - List contents = parseDynamicTags(nodeToHandle); - MixedSqlNode mixedSqlNode = new MixedSqlNode(contents); + MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle); SetSqlNode set = new SetSqlNode(configuration, mixedSqlNode); targetContents.add(set); } @@ -173,8 +169,7 @@ public ForEachHandler() { @Override public void handleNode(XNode nodeToHandle, List targetContents) { - List contents = parseDynamicTags(nodeToHandle); - MixedSqlNode mixedSqlNode = new MixedSqlNode(contents); + MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle); String collection = nodeToHandle.getStringAttribute("collection"); String item = nodeToHandle.getStringAttribute("item"); String index = nodeToHandle.getStringAttribute("index"); @@ -193,8 +188,7 @@ public IfHandler() { @Override public void handleNode(XNode nodeToHandle, List targetContents) { - List contents = parseDynamicTags(nodeToHandle); - MixedSqlNode mixedSqlNode = new MixedSqlNode(contents); + MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle); String test = nodeToHandle.getStringAttribute("test"); IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test); targetContents.add(ifSqlNode); @@ -208,8 +202,7 @@ public OtherwiseHandler() { @Override public void handleNode(XNode nodeToHandle, List targetContents) { - List contents = parseDynamicTags(nodeToHandle); - MixedSqlNode mixedSqlNode = new MixedSqlNode(contents); + MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle); targetContents.add(mixedSqlNode); } } From e26588ccda616c82dcf1be888403b98a88be495f Mon Sep 17 00:00:00 2001 From: Jeremy Landis Date: Tue, 23 May 2017 21:58:56 -0400 Subject: [PATCH 0045/2062] [ci] Update install.sh to use maven wrapper --- travis/install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/travis/install.sh b/travis/install.sh index 79415a10953..551be74ae81 100644 --- a/travis/install.sh +++ b/travis/install.sh @@ -17,11 +17,11 @@ if [ $TRAVIS_JDK_VERSION == "openjdk6" ]; then # Java 1.6 - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -Pjava16 + ./mvnw install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -Pjava16 --settings ./travis/settings.xml elif [ $TRAVIS_JDK_VERSION == "oraclejdk7" ] || [ $TRAVIS_JDK_VERSION == "openjdk7" ]; then # Java 1.7 - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -Pjava17 + ./mvnw install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -Pjava17 --settings ./travis/settings.xml else # Java 1.8 - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V + ./mvnw install -DskipTests=true -Dmaven.javadoc.skip=true -B -V --settings ./travis/settings.xml fi From 1262b2bbba6a380debdef53edf6eaca2bc7efdfc Mon Sep 17 00:00:00 2001 From: Jeremy Landis Date: Sun, 31 Dec 2017 19:55:23 -0500 Subject: [PATCH 0046/2062] [ci] Remove oraclejdk7 from install.sh and update else to note for java 9 too --- travis/install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/travis/install.sh b/travis/install.sh index 551be74ae81..64e6b0355f0 100644 --- a/travis/install.sh +++ b/travis/install.sh @@ -18,10 +18,10 @@ if [ $TRAVIS_JDK_VERSION == "openjdk6" ]; then # Java 1.6 ./mvnw install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -Pjava16 --settings ./travis/settings.xml -elif [ $TRAVIS_JDK_VERSION == "oraclejdk7" ] || [ $TRAVIS_JDK_VERSION == "openjdk7" ]; then +elif [ $TRAVIS_JDK_VERSION == "openjdk7" ]; then # Java 1.7 ./mvnw install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -Pjava17 --settings ./travis/settings.xml else - # Java 1.8 + # Java 1.8 and 9 ./mvnw install -DskipTests=true -Dmaven.javadoc.skip=true -B -V --settings ./travis/settings.xml fi From f6788c7a100eb6cb53b0b7401a31c469198ec017 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Thu, 4 Jan 2018 23:55:40 +0900 Subject: [PATCH 0047/2062] Updated license year. --- .../apache/ibatis/reflection/factory/DefaultObjectFactory.java | 2 +- .../apache/ibatis/scripting/xmltags/ExpressionEvaluator.java | 2 +- .../ibatis/builder/xml/dynamic/ExpressionEvaluatorTest.java | 2 +- .../ibatis/reflection/factory/DefaultObjectFactoryTest.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/apache/ibatis/reflection/factory/DefaultObjectFactory.java b/src/main/java/org/apache/ibatis/reflection/factory/DefaultObjectFactory.java index 4c794a8bd18..4b5702e266e 100644 --- a/src/main/java/org/apache/ibatis/reflection/factory/DefaultObjectFactory.java +++ b/src/main/java/org/apache/ibatis/reflection/factory/DefaultObjectFactory.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2015 the original author or authors. + * Copyright 2009-2017 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. diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/ExpressionEvaluator.java b/src/main/java/org/apache/ibatis/scripting/xmltags/ExpressionEvaluator.java index be6d4167f74..44138df9675 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/ExpressionEvaluator.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/ExpressionEvaluator.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2015 the original author or authors. + * Copyright 2009-2017 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. diff --git a/src/test/java/org/apache/ibatis/builder/xml/dynamic/ExpressionEvaluatorTest.java b/src/test/java/org/apache/ibatis/builder/xml/dynamic/ExpressionEvaluatorTest.java index 6df59332b25..9a8525747cb 100644 --- a/src/test/java/org/apache/ibatis/builder/xml/dynamic/ExpressionEvaluatorTest.java +++ b/src/test/java/org/apache/ibatis/builder/xml/dynamic/ExpressionEvaluatorTest.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2015 the original author or authors. + * Copyright 2009-2017 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. diff --git a/src/test/java/org/apache/ibatis/reflection/factory/DefaultObjectFactoryTest.java b/src/test/java/org/apache/ibatis/reflection/factory/DefaultObjectFactoryTest.java index e17b89893f2..2fd800e364e 100644 --- a/src/test/java/org/apache/ibatis/reflection/factory/DefaultObjectFactoryTest.java +++ b/src/test/java/org/apache/ibatis/reflection/factory/DefaultObjectFactoryTest.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2015 the original author or authors. + * Copyright 2009-2017 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. From 2caa67b3f8162b75fa2c85f90024ac5c1c45b2bb Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Thu, 4 Jan 2018 23:57:25 +0900 Subject: [PATCH 0048/2062] Updated license year. --- .../apache/ibatis/reflection/factory/DefaultObjectFactory.java | 2 +- .../apache/ibatis/scripting/xmltags/ExpressionEvaluator.java | 2 +- .../ibatis/builder/xml/dynamic/ExpressionEvaluatorTest.java | 2 +- .../ibatis/reflection/factory/DefaultObjectFactoryTest.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/apache/ibatis/reflection/factory/DefaultObjectFactory.java b/src/main/java/org/apache/ibatis/reflection/factory/DefaultObjectFactory.java index 4b5702e266e..b512cd3f8d1 100644 --- a/src/main/java/org/apache/ibatis/reflection/factory/DefaultObjectFactory.java +++ b/src/main/java/org/apache/ibatis/reflection/factory/DefaultObjectFactory.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 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. diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/ExpressionEvaluator.java b/src/main/java/org/apache/ibatis/scripting/xmltags/ExpressionEvaluator.java index 44138df9675..22c5d3578b3 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/ExpressionEvaluator.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/ExpressionEvaluator.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 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. diff --git a/src/test/java/org/apache/ibatis/builder/xml/dynamic/ExpressionEvaluatorTest.java b/src/test/java/org/apache/ibatis/builder/xml/dynamic/ExpressionEvaluatorTest.java index 9a8525747cb..94810a3ed76 100644 --- a/src/test/java/org/apache/ibatis/builder/xml/dynamic/ExpressionEvaluatorTest.java +++ b/src/test/java/org/apache/ibatis/builder/xml/dynamic/ExpressionEvaluatorTest.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 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. diff --git a/src/test/java/org/apache/ibatis/reflection/factory/DefaultObjectFactoryTest.java b/src/test/java/org/apache/ibatis/reflection/factory/DefaultObjectFactoryTest.java index 2fd800e364e..514452169fd 100644 --- a/src/test/java/org/apache/ibatis/reflection/factory/DefaultObjectFactoryTest.java +++ b/src/test/java/org/apache/ibatis/reflection/factory/DefaultObjectFactoryTest.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 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. From f9fabc335d0e987d36d0edb363de0c83e3a9d0d6 Mon Sep 17 00:00:00 2001 From: iMouseWu <2159201@qq.com> Date: Fri, 5 Jan 2018 21:36:44 +0800 Subject: [PATCH 0049/2062] Add resource path to the exception message when parsing XML mapper failed (#1172) * print error detail when xmlConfigBuilder parse error * modify get location from context to variables --- .../ibatis/builder/xml/XMLMapperBuilder.java | 2 +- .../apache/ibatis/builder/ProblemMapper.xml | 28 +++++++++++++++++++ .../ibatis/builder/XmlMapperBuilderTest.java | 16 +++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/apache/ibatis/builder/ProblemMapper.xml diff --git a/src/main/java/org/apache/ibatis/builder/xml/XMLMapperBuilder.java b/src/main/java/org/apache/ibatis/builder/xml/XMLMapperBuilder.java index 790bfa9de52..2b544f41b61 100644 --- a/src/main/java/org/apache/ibatis/builder/xml/XMLMapperBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/xml/XMLMapperBuilder.java @@ -117,7 +117,7 @@ private void configurationElement(XNode context) { sqlElement(context.evalNodes("/mapper/sql")); buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception e) { - throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e); + throw new BuilderException("Error parsing Mapper XML.The XML location is " + resource + " Cause: " + e, e); } } diff --git a/src/test/java/org/apache/ibatis/builder/ProblemMapper.xml b/src/test/java/org/apache/ibatis/builder/ProblemMapper.xml new file mode 100644 index 00000000000..a7f7145dacc --- /dev/null +++ b/src/test/java/org/apache/ibatis/builder/ProblemMapper.xml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/src/test/java/org/apache/ibatis/builder/XmlMapperBuilderTest.java b/src/test/java/org/apache/ibatis/builder/XmlMapperBuilderTest.java index 5719eda30aa..b1e8b4b4aa4 100644 --- a/src/test/java/org/apache/ibatis/builder/XmlMapperBuilderTest.java +++ b/src/test/java/org/apache/ibatis/builder/XmlMapperBuilderTest.java @@ -24,8 +24,10 @@ import org.apache.ibatis.mapping.ResultSetType; import org.apache.ibatis.mapping.StatementType; import org.apache.ibatis.session.Configuration; +import org.junit.Rule; import org.apache.ibatis.type.TypeHandler; import org.junit.Test; +import org.junit.rules.ExpectedException; import static com.googlecode.catchexception.apis.BDDCatchException.*; import static org.assertj.core.api.BDDAssertions.then; @@ -34,6 +36,9 @@ public class XmlMapperBuilderTest { + @Rule + public ExpectedException expectedEx = ExpectedException.none(); + @Test public void shouldSuccessfullyLoadXMLMapperFile() throws Exception { Configuration configuration = new Configuration(); @@ -167,6 +172,17 @@ public void useCacheRefNamespaceIsUndefined() { .hasMessage("No cache for namespace 'eee' could be found."); } + @Test + public void shouldFailedLoadXMLMapperFile() throws Exception { + expectedEx.expect(BuilderException.class); + expectedEx.expectMessage("Error parsing Mapper XML.The XML location is org/apache/ibatis/builder/ProblemMapper.xml"); + Configuration configuration = new Configuration(); + String resource = "org/apache/ibatis/builder/ProblemMapper.xml"; + InputStream inputStream = Resources.getResourceAsStream(resource); + XMLMapperBuilder builder = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); + builder.parse(); + } + // @Test // public void shouldNotLoadTheSameNamespaceFromTwoResourcesWithDifferentNames() throws Exception { // Configuration configuration = new Configuration(); From 21377c9e6b375938ab5d282bba071ef32da8621f Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Fri, 5 Jan 2018 22:50:11 +0900 Subject: [PATCH 0050/2062] Trivial adjustment and license year updates. --- .../java/org/apache/ibatis/builder/xml/XMLMapperBuilder.java | 4 ++-- src/test/java/org/apache/ibatis/builder/ProblemMapper.xml | 2 +- .../java/org/apache/ibatis/builder/XmlMapperBuilderTest.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/apache/ibatis/builder/xml/XMLMapperBuilder.java b/src/main/java/org/apache/ibatis/builder/xml/XMLMapperBuilder.java index 2b544f41b61..c7021b2c4fb 100644 --- a/src/main/java/org/apache/ibatis/builder/xml/XMLMapperBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/xml/XMLMapperBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 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. @@ -117,7 +117,7 @@ private void configurationElement(XNode context) { sqlElement(context.evalNodes("/mapper/sql")); buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception e) { - throw new BuilderException("Error parsing Mapper XML.The XML location is " + resource + " Cause: " + e, e); + throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e); } } diff --git a/src/test/java/org/apache/ibatis/builder/ProblemMapper.xml b/src/test/java/org/apache/ibatis/builder/ProblemMapper.xml index a7f7145dacc..db5ab6807fa 100644 --- a/src/test/java/org/apache/ibatis/builder/ProblemMapper.xml +++ b/src/test/java/org/apache/ibatis/builder/ProblemMapper.xml @@ -1,7 +1,7 @@ - - - - - - org.apache.felix - maven-bundle-plugin - 2.5.4 - - - - java17 From e3c73bc665d3646122b7a17af3dccd0dda866e27 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Sat, 17 Mar 2018 04:27:45 +0900 Subject: [PATCH 0077/2062] Refine build definition for droping JDK6 #1207 --- pom.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index 62ad5f7563b..6239e32498b 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ org.mybatis mybatis-parent - 29 + 30 @@ -131,6 +131,8 @@ + 1.7 + 1.7 1.8 1.8 -parameters @@ -140,6 +142,10 @@ *;resolution:=optional * org.apache.ibatis.test.SlowTests + + + java17 + 1.0 @@ -346,12 +352,6 @@ - - - org.codehaus.mojo - animal-sniffer-maven-plugin - 1.16 - From 4cf9c5a7f05585dd91d1c10ce8a8063cdfb536c8 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Sat, 17 Mar 2018 04:33:44 +0900 Subject: [PATCH 0078/2062] Drop JDK7 on travis build #1207 --- .travis.yml | 1 - travis/install.sh | 10 ++-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index d771033e6f7..75802ac5b3a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,6 @@ jdk: - oraclejdk9 - oraclejdk8 - openjdk8 - - openjdk7 before_install: - echo "MAVEN_OPTS='-Dlicense.skip=true'" > ~/.mavenrc diff --git a/travis/install.sh b/travis/install.sh index a4f014beeb3..8a2a02e3c16 100644 --- a/travis/install.sh +++ b/travis/install.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright 2009-2017 the original author or authors. +# Copyright 2009-2018 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. @@ -15,10 +15,4 @@ # limitations under the License. # -if [ $TRAVIS_JDK_VERSION == "openjdk7" ]; then - # Java 1.7 - ./mvnw install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -Pjava17 --settings ./travis/settings.xml -else - # Java 1.8 and 9 - ./mvnw install -DskipTests=true -Dmaven.javadoc.skip=true -B -V --settings ./travis/settings.xml -fi +./mvnw install -DskipTests=true -Dmaven.javadoc.skip=true -B -V --settings ./travis/settings.xml From 59229e3fbb01fac74d5f8b1c62dbd5003f3a1ffd Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Sat, 17 Mar 2018 04:36:29 +0900 Subject: [PATCH 0079/2062] Drop maven profile for JDK7 #1207 --- pom.xml | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/pom.xml b/pom.xml index 6239e32498b..356b32f0a9d 100644 --- a/pom.xml +++ b/pom.xml @@ -380,32 +380,4 @@ - - - java17 - - 1.7 - - - 1.7 - 1.7 - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - **/usesjava8/**/*.java - - - - - - - - From 490ac006b05f41af409e11f1161ad0668ded08c9 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Sat, 17 Mar 2018 04:39:36 +0900 Subject: [PATCH 0080/2062] Refine build definition for droping JDK7 #1207 --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 356b32f0a9d..1a978a72368 100644 --- a/pom.xml +++ b/pom.xml @@ -131,8 +131,8 @@ - 1.7 - 1.7 + 1.8 + 1.8 1.8 1.8 -parameters @@ -144,7 +144,7 @@ org.apache.ibatis.test.SlowTests - java17 + java18 1.0 From bc7ed622be0ab77b421878308580d8077fa0951c Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Sat, 17 Mar 2018 08:36:44 +0900 Subject: [PATCH 0081/2062] Upgrade dependency artifacts to latest version #1207 * log4j-core 2.3 -> 2.5 * cglib 3.2.5 -> 3.2.6 * junit-vintage-engine 4.12.2 -> 5.1.0 * hsqldb 2.3.5 -> 2.4.0 * derby 10.12.1.1 -> 10.14.1.0 * mockito-core 2.12.0 -> 2.16.0 * commons-dbcp 1.x -> commons-dbcp2 2.2.0 * jboss-transaction-api_1.2_spec 1.0.1 -> 1.1.1 * postgresql 42.1.4.jre6 -> 42.2.1 * assertj-core 1.7.1 -> 3.9.1 * catch-exception 1.4.4 -> 1.4.6 * postgresql-embedded 2.5 -> 2.8 --- pom.xml | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/pom.xml b/pom.xml index 1a978a72368..d1bd7ff4894 100644 --- a/pom.xml +++ b/pom.xml @@ -152,7 +152,7 @@ ognl ognl - 3.1.16 + 3.1.16 compile true @@ -181,11 +181,10 @@ 1.2.17 true - org.apache.logging.log4j log4j-core - 2.3 + 2.5 true @@ -197,7 +196,7 @@ cglib cglib - 3.2.5 + 3.2.6 true @@ -205,38 +204,37 @@ org.junit.vintage junit-vintage-engine - 4.12.2 + 5.1.0 test org.hsqldb hsqldb - 2.3.5 + 2.4.0 test org.apache.derby derby - 10.12.1.1 + 10.14.1.0 test org.mockito mockito-core - 2.12.0 + 2.16.0 test - - commons-dbcp - commons-dbcp - 1.4 + org.apache.commons + commons-dbcp2 + 2.2.0 test org.jboss.spec.javax.transaction jboss-transaction-api_1.2_spec - 1.0.1.Final + 1.1.1.Final test @@ -249,25 +247,25 @@ org.postgresql postgresql - 42.1.4.jre6 + 42.2.1 test org.assertj assertj-core - 1.7.1 + 3.9.1 test eu.codearte.catch-exception catch-exception - 1.4.4 + 1.4.6 test ru.yandex.qatools.embed postgresql-embedded - 2.5 + 2.8 test From adcbfbd58c92d9495fb611a76c793a2316c8af1c Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Mon, 19 Mar 2018 21:22:43 +0900 Subject: [PATCH 0082/2062] Update to maven 3.5.2 #1207 --- .mvn/wrapper/maven-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index a380338b9b8..7095a8882f3 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,2 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.2.5/apache-maven-3.2.5-bin.zip +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.2/apache-maven-3.5.2-bin.zip wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.0/maven-wrapper-0.4.0.jar From f471b3204b134e4f73bb4254093e75182b8fc4c6 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Mon, 19 Mar 2018 21:28:21 +0900 Subject: [PATCH 0083/2062] Change artifact to javax.transaction:javax.transaction-api #1207 --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index d1bd7ff4894..f812fd1f3b9 100644 --- a/pom.xml +++ b/pom.xml @@ -232,9 +232,9 @@ test - org.jboss.spec.javax.transaction - jboss-transaction-api_1.2_spec - 1.1.1.Final + javax.transaction + javax.transaction-api + 1.3 test From b1f9387540c66ffc2188a35fda99c02784a95184 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Mon, 19 Mar 2018 21:36:37 +0900 Subject: [PATCH 0084/2062] Upgrade to mybatis-parent to 31-SNAPSHOT #1207 --- pom.xml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f812fd1f3b9..6b6542523a1 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ org.mybatis mybatis-parent - 30 + 31-SNAPSHOT @@ -378,4 +378,13 @@ + + + + sonatype-oss-snapshots + Sonatype OSS Snapshots Repository + https://oss.sonatype.org/content/repositories/snapshots + + + From 8f100840a86d04cd00914373f3b393e09a43d0ea Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Mon, 19 Mar 2018 23:56:01 +0900 Subject: [PATCH 0085/2062] Update Copyright year --- .../org/apache/ibatis/transaction/jdbc/JdbcTransaction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/apache/ibatis/transaction/jdbc/JdbcTransaction.java b/src/main/java/org/apache/ibatis/transaction/jdbc/JdbcTransaction.java index f67f12660fb..6cd7492b11d 100644 --- a/src/main/java/org/apache/ibatis/transaction/jdbc/JdbcTransaction.java +++ b/src/main/java/org/apache/ibatis/transaction/jdbc/JdbcTransaction.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 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. From c54ced15ee12b4fca0d7c438b82bc2b898acd94f Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Tue, 20 Mar 2018 00:41:56 +0900 Subject: [PATCH 0086/2062] Casting explicitly to the Message #1210 --- .../logging/log4j2/Log4j2AbstractLoggerImpl.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/apache/ibatis/logging/log4j2/Log4j2AbstractLoggerImpl.java b/src/main/java/org/apache/ibatis/logging/log4j2/Log4j2AbstractLoggerImpl.java index 52bde61a0fb..e8ac38d0d47 100644 --- a/src/main/java/org/apache/ibatis/logging/log4j2/Log4j2AbstractLoggerImpl.java +++ b/src/main/java/org/apache/ibatis/logging/log4j2/Log4j2AbstractLoggerImpl.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 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. @@ -20,6 +20,7 @@ import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.MarkerManager; +import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.SimpleMessage; import org.apache.logging.log4j.spi.AbstractLogger; import org.apache.logging.log4j.spi.ExtendedLoggerWrapper; @@ -51,27 +52,27 @@ public boolean isTraceEnabled() { @Override public void error(String s, Throwable e) { - log.logIfEnabled(FQCN, Level.ERROR, MARKER, new SimpleMessage(s), e); + log.logIfEnabled(FQCN, Level.ERROR, MARKER, (Message) new SimpleMessage(s), e); } @Override public void error(String s) { - log.logIfEnabled(FQCN, Level.ERROR, MARKER, new SimpleMessage(s), null); + log.logIfEnabled(FQCN, Level.ERROR, MARKER, (Message) new SimpleMessage(s), null); } @Override public void debug(String s) { - log.logIfEnabled(FQCN, Level.DEBUG, MARKER, new SimpleMessage(s), null); + log.logIfEnabled(FQCN, Level.DEBUG, MARKER, (Message) new SimpleMessage(s), null); } @Override public void trace(String s) { - log.logIfEnabled(FQCN, Level.TRACE, MARKER, new SimpleMessage(s), null); + log.logIfEnabled(FQCN, Level.TRACE, MARKER, (Message) new SimpleMessage(s), null); } @Override public void warn(String s) { - log.logIfEnabled(FQCN, Level.WARN, MARKER, new SimpleMessage(s), null); + log.logIfEnabled(FQCN, Level.WARN, MARKER, (Message) new SimpleMessage(s), null); } } From 99967dd701bc52b74a2ac5c21a7046706caee677 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Tue, 20 Mar 2018 00:48:33 +0900 Subject: [PATCH 0087/2062] Upgrade to log4j-core 2.11.0 #1210 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6b6542523a1..677aa197fe5 100644 --- a/pom.xml +++ b/pom.xml @@ -184,7 +184,7 @@ org.apache.logging.log4j log4j-core - 2.5 + 2.11.0 true From 24e9cb47c789891281303f9c78de19cf6468643a Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Tue, 20 Mar 2018 07:37:30 +0900 Subject: [PATCH 0088/2062] Upgrade to ognl 3.2.x #1211 * Upgrade to ognl 3.2.5 * Add OgnlMemberAccess * Avoid calling the deprecated method on OgnlCache --- pom.xml | 2 +- .../ibatis/scripting/xmltags/OgnlCache.java | 5 +- .../scripting/xmltags/OgnlMemberAccess.java | 63 +++++++++++++++++++ 3 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/apache/ibatis/scripting/xmltags/OgnlMemberAccess.java diff --git a/pom.xml b/pom.xml index 677aa197fe5..fb36782d3ff 100644 --- a/pom.xml +++ b/pom.xml @@ -152,7 +152,7 @@ ognl ognl - 3.1.16 + 3.2.5 compile true diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/OgnlCache.java b/src/main/java/org/apache/ibatis/scripting/xmltags/OgnlCache.java index 64c64b5c9cc..9c1ad89dffd 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/OgnlCache.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/OgnlCache.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2016 the original author or authors. + * Copyright 2009-2018 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. @@ -32,6 +32,7 @@ */ public final class OgnlCache { + private static final OgnlMemberAccess MEMBER_ACCESS = new OgnlMemberAccess(); private static final Map expressionCache = new ConcurrentHashMap(); private OgnlCache() { @@ -40,7 +41,7 @@ private OgnlCache() { public static Object getValue(String expression, Object root) { try { - Map context = Ognl.createDefaultContext(root, new OgnlClassResolver()); + Map context = Ognl.createDefaultContext(root, MEMBER_ACCESS, new OgnlClassResolver(), null); return Ognl.getValue(parseExpression(expression), context, root); } catch (OgnlException e) { throw new BuilderException("Error evaluating expression '" + expression + "'. Cause: " + e, e); diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/OgnlMemberAccess.java b/src/main/java/org/apache/ibatis/scripting/xmltags/OgnlMemberAccess.java new file mode 100644 index 00000000000..33a8eeec5ce --- /dev/null +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/OgnlMemberAccess.java @@ -0,0 +1,63 @@ +/** + * Copyright 2009-2018 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.scripting.xmltags; + +import ognl.MemberAccess; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Member; +import java.lang.reflect.Modifier; +import java.util.Map; + +/** + * The {@link MemberAccess} class that based on DefaultMemberAccess. + * + * @author Kazuki Shimizu + * @since 3.5.0 + * + * @see DefaultMemberAccess + */ +class OgnlMemberAccess implements MemberAccess { + + @Override + public Object setup(Map context, Object target, Member member, String propertyName) { + Object result = null; + if (isAccessible(context, target, member, propertyName)) { + AccessibleObject accessible = (AccessibleObject) member; + if (!accessible.isAccessible()) { + result = Boolean.FALSE; + accessible.setAccessible(true); + } + } + return result; + } + + @Override + public void restore(Map context, Object target, Member member, String propertyName, + Object state) { + if (state != null) { + ((AccessibleObject) member).setAccessible(((Boolean) state)); + } + } + + @Override + public boolean isAccessible(Map context, Object target, Member member, String propertyName) { + return Modifier.isPublic(member.getModifiers()); + } + +} From 1050ff58e23225ca8016bd5fab7ffcfd6d51217d Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Wed, 21 Mar 2018 05:07:27 +0900 Subject: [PATCH 0089/2062] Add related link #1211 --- .../org/apache/ibatis/scripting/xmltags/OgnlMemberAccess.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/OgnlMemberAccess.java b/src/main/java/org/apache/ibatis/scripting/xmltags/OgnlMemberAccess.java index 33a8eeec5ce..dd3cb8cfd26 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/OgnlMemberAccess.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/OgnlMemberAccess.java @@ -31,6 +31,7 @@ * * @see DefaultMemberAccess + * @see #47 of ognl */ class OgnlMemberAccess implements MemberAccess { From 2e0a40a91c6afe6da2eec01afa50d408d810ced2 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Wed, 21 Mar 2018 16:10:56 +0900 Subject: [PATCH 0090/2062] Polishing the error message that notifies duplication of MappedStatement #1104 --- .../apache/ibatis/session/Configuration.java | 24 ++++++++++++-- .../apache/ibatis/session/SqlSessionTest.java | 31 ++++++++++++++++--- 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/apache/ibatis/session/Configuration.java b/src/main/java/org/apache/ibatis/session/Configuration.java index f2a60fe03f7..47d4ac6a6a7 100644 --- a/src/main/java/org/apache/ibatis/session/Configuration.java +++ b/src/main/java/org/apache/ibatis/session/Configuration.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 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. @@ -24,6 +24,7 @@ import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.function.BiFunction; import org.apache.ibatis.binding.MapperRegistry; import org.apache.ibatis.builder.CacheRefResolver; @@ -145,7 +146,9 @@ public class Configuration { protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry(); protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry(); - protected final Map mappedStatements = new StrictMap("Mapped Statements collection"); + protected final Map mappedStatements = new StrictMap("Mapped Statements collection") + .additionalErrorMessageForDuplication((savedValue, currentValue) -> + ". please check " + savedValue.getResource() + " and " + currentValue.getResource()); protected final Map caches = new StrictMap("Caches collection"); protected final Map resultMaps = new StrictMap("Result Maps collection"); protected final Map parameterMaps = new StrictMap("Parameter Maps collection"); @@ -845,6 +848,7 @@ protected static class StrictMap extends HashMap { private static final long serialVersionUID = -4950446264854982944L; private final String name; + private BiFunction additionalErrorMessageForDuplication; public StrictMap(String name, int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); @@ -866,10 +870,24 @@ public StrictMap(String name, Map m) { this.name = name; } + /** + * Assign a function for providing an additional error message when contains value with the same key. + *

    + * function arguments are 1st is saved value and 2nd is current value. + * @param additionalErrorMessage A function for providing an additional error message + * @return an additional error message + * @since 3.5.0 + */ + public StrictMap additionalErrorMessageForDuplication(BiFunction additionalErrorMessage) { + this.additionalErrorMessageForDuplication = additionalErrorMessage; + return this; + } + @SuppressWarnings("unchecked") public V put(String key, V value) { if (containsKey(key)) { - throw new IllegalArgumentException(name + " already contains value for " + key); + throw new IllegalArgumentException(name + " already contains value for " + key + + (additionalErrorMessageForDuplication == null ? "" : additionalErrorMessageForDuplication.apply(super.get(key), value))); } if (key.contains(".")) { final String shortKey = getShortName(key); diff --git a/src/test/java/org/apache/ibatis/session/SqlSessionTest.java b/src/test/java/org/apache/ibatis/session/SqlSessionTest.java index 7214c1cdc16..dd14765e248 100644 --- a/src/test/java/org/apache/ibatis/session/SqlSessionTest.java +++ b/src/test/java/org/apache/ibatis/session/SqlSessionTest.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2016 the original author or authors. + * Copyright 2009-2018 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. @@ -49,10 +49,14 @@ import org.apache.ibatis.exceptions.TooManyResultsException; import org.apache.ibatis.executor.result.DefaultResultHandler; import org.apache.ibatis.io.Resources; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.SqlCommandType; +import org.apache.ibatis.mapping.SqlSource; import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; +import org.mockito.Mockito; public class SqlSessionTest extends BaseDataTest { private static SqlSessionFactory sqlMapper; @@ -489,13 +493,32 @@ public void shouldThrowExceptionIfMappedStatementDoesNotExist() throws Exception } @Test - public void shouldThrowExceptionIfTryingToAddStatementWithSameName() throws Exception { + public void shouldThrowExceptionIfTryingToAddStatementWithSameNameInXml() throws Exception { Configuration config = sqlMapper.getConfiguration(); try { - config.addMappedStatement(config.getMappedStatement("org.apache.ibatis.domain.blog.mappers.BlogMapper.selectBlogWithPostsUsingSubSelect")); + MappedStatement ms = new MappedStatement.Builder(config, + "org.apache.ibatis.domain.blog.mappers.BlogMapper.selectBlogWithPostsUsingSubSelect", + Mockito.mock(SqlSource.class), SqlCommandType.SELECT) + .resource("org/mybatis/TestMapper.xml").build(); + config.addMappedStatement(ms); fail("Expected exception to be thrown due to statement that already exists."); } catch (Exception e) { - assertTrue(e.getMessage().contains("already contains value for org.apache.ibatis.domain.blog.mappers.BlogMapper.selectBlogWithPostsUsingSubSelect")); + assertTrue(e.getMessage().contains("already contains value for org.apache.ibatis.domain.blog.mappers.BlogMapper.selectBlogWithPostsUsingSubSelect. please check org/apache/ibatis/builder/BlogMapper.xml and org/mybatis/TestMapper.xml")); + } + } + + @Test + public void shouldThrowExceptionIfTryingToAddStatementWithSameNameInAnnotation() throws Exception { + Configuration config = sqlMapper.getConfiguration(); + try { + MappedStatement ms = new MappedStatement.Builder(config, + "org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor2", + Mockito.mock(SqlSource.class), SqlCommandType.SELECT) + .resource("org/mybatis/TestMapper.xml").build(); + config.addMappedStatement(ms); + fail("Expected exception to be thrown due to statement that already exists."); + } catch (Exception e) { + assertTrue(e.getMessage().contains("already contains value for org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor2. please check org/apache/ibatis/domain/blog/mappers/AuthorMapper.java (best guess) and org/mybatis/TestMapper.xml")); } } From 43f7cec634957d03706113b1d06bf269a61ba409 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Wed, 21 Mar 2018 17:53:28 +0900 Subject: [PATCH 0091/2062] Upgrade to ognl 3.2.6 #1211 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fb36782d3ff..95adffbd09b 100644 --- a/pom.xml +++ b/pom.xml @@ -152,7 +152,7 @@ ognl ognl - 3.2.5 + 3.2.6 compile true From 25ce2db9d18e10c798db1e13694eb1ace0baf1d4 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Sat, 24 Mar 2018 09:50:39 +0900 Subject: [PATCH 0092/2062] Support concurrency on the OgnlClassResolver #1216 --- .../ibatis/scripting/xmltags/OgnlCache.java | 3 +- .../scripting/xmltags/OgnlClassResolver.java | 28 ++++--------------- 2 files changed, 7 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/OgnlCache.java b/src/main/java/org/apache/ibatis/scripting/xmltags/OgnlCache.java index 9c1ad89dffd..1999526b6f8 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/OgnlCache.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/OgnlCache.java @@ -33,6 +33,7 @@ public final class OgnlCache { private static final OgnlMemberAccess MEMBER_ACCESS = new OgnlMemberAccess(); + private static final OgnlClassResolver CLASS_RESOLVER = new OgnlClassResolver(); private static final Map expressionCache = new ConcurrentHashMap(); private OgnlCache() { @@ -41,7 +42,7 @@ private OgnlCache() { public static Object getValue(String expression, Object root) { try { - Map context = Ognl.createDefaultContext(root, MEMBER_ACCESS, new OgnlClassResolver(), null); + Map context = Ognl.createDefaultContext(root, MEMBER_ACCESS, CLASS_RESOLVER, null); return Ognl.getValue(parseExpression(expression), context, root); } catch (OgnlException e) { throw new BuilderException("Error evaluating expression '" + expression + "'. Cause: " + e, e); diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/OgnlClassResolver.java b/src/main/java/org/apache/ibatis/scripting/xmltags/OgnlClassResolver.java index 6761f7635fb..6cb6ff07133 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/OgnlClassResolver.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/OgnlClassResolver.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 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. @@ -15,11 +15,7 @@ */ package org.apache.ibatis.scripting.xmltags; -import java.util.HashMap; -import java.util.Map; - -import ognl.ClassResolver; - +import ognl.DefaultClassResolver; import org.apache.ibatis.io.Resources; /** @@ -31,25 +27,11 @@ * * @see Issue 161 */ -public class OgnlClassResolver implements ClassResolver { - - private final Map> classes = new HashMap>(101); +public class OgnlClassResolver extends DefaultClassResolver { @Override - public Class classForName(String className, Map context) throws ClassNotFoundException { - Class result = null; - if ((result = classes.get(className)) == null) { - try { - result = Resources.classForName(className); - } catch (ClassNotFoundException e1) { - if (className.indexOf('.') == -1) { - result = Resources.classForName("java.lang." + className); - classes.put("java.lang." + className, result); - } - } - classes.put(className, result); - } - return result; + protected Class toClassForName(String className) throws ClassNotFoundException { + return Resources.classForName(className); } } From 70bc45f8988de988a74000ae180608033f84b623 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Sat, 24 Mar 2018 18:25:09 +0900 Subject: [PATCH 0093/2062] Fix property name for spotbugs on pom.xml #1224 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 95adffbd09b..90494a9f5cd 100644 --- a/pom.xml +++ b/pom.xml @@ -137,7 +137,7 @@ 1.8 -parameters 3.3.1 - org.apache.ibatis.* + org.apache.ibatis.* org.apache.ibatis.*;version=${project.version};-noimport:=true *;resolution:=optional * From 81534457cb737b3a089fc5a17b23a76d24be0731 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Sat, 24 Mar 2018 18:26:21 +0900 Subject: [PATCH 0094/2062] Fix javadoc error using maven-javadoc-plugin 3.0 #1224 --- src/main/java/org/apache/ibatis/mapping/BoundSql.java | 4 ++-- .../java/org/apache/ibatis/reflection/ParamNameResolver.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/apache/ibatis/mapping/BoundSql.java b/src/main/java/org/apache/ibatis/mapping/BoundSql.java index 3939e63f5bd..7bfa322b193 100644 --- a/src/main/java/org/apache/ibatis/mapping/BoundSql.java +++ b/src/main/java/org/apache/ibatis/mapping/BoundSql.java @@ -27,8 +27,8 @@ * An actual SQL String got from an {@link SqlSource} after having processed any dynamic content. * The SQL may have SQL placeholders "?" and an list (ordered) of an parameter mappings * with the additional information for each parameter (at least the property name of the input object to read - * the value from). - *
    + * the value from). + *

    * Can also have additional parameters that are created by the dynamic language (for loops, bind...). * * @author Clinton Begin diff --git a/src/main/java/org/apache/ibatis/reflection/ParamNameResolver.java b/src/main/java/org/apache/ibatis/reflection/ParamNameResolver.java index 4993322a766..4e623a5c39f 100644 --- a/src/main/java/org/apache/ibatis/reflection/ParamNameResolver.java +++ b/src/main/java/org/apache/ibatis/reflection/ParamNameResolver.java @@ -105,8 +105,8 @@ public String[] getNames() { /** *

    - * A single non-special parameter is returned without a name.
    - * Multiple parameters are named using the naming rule.
    + * A single non-special parameter is returned without a name. + * Multiple parameters are named using the naming rule. * In addition to the default names, this method also adds the generic names (param1, param2, * ...). *

    From 879d24b1d9822e214cdd4d6ef8c98f8a2a39bc21 Mon Sep 17 00:00:00 2001 From: Jeremy Landis Date: Sun, 18 Mar 2018 20:40:19 -0400 Subject: [PATCH 0095/2062] [mvn] Upgrade wrapper maven version to 3.5.3 --- .mvn/wrapper/maven-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 7095a8882f3..20297d0f281 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,2 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.2/apache-maven-3.5.2-bin.zip +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.0/maven-wrapper-0.4.0.jar From 9e9d968f030de1b74a27cd329e0690b064e4a8d0 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Sun, 25 Mar 2018 14:15:27 +0900 Subject: [PATCH 0096/2062] Fix to refer the line separator from system property on ScriptRunnerTest #1230 --- .../apache/ibatis/jdbc/ScriptRunnerTest.java | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/test/java/org/apache/ibatis/jdbc/ScriptRunnerTest.java b/src/test/java/org/apache/ibatis/jdbc/ScriptRunnerTest.java index 017981b230b..2df2cf16b07 100644 --- a/src/test/java/org/apache/ibatis/jdbc/ScriptRunnerTest.java +++ b/src/test/java/org/apache/ibatis/jdbc/ScriptRunnerTest.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 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. @@ -41,6 +41,8 @@ public class ScriptRunnerTest extends BaseDataTest { + private static final String LINE_SEPARATOR = System.getProperty("line.separator", "\n"); + @Test @Ignore("This fails with HSQLDB 2.0 due to the create index statements in the schema script") public void shouldRunScriptsBySendingFullScriptAtOnce() throws Exception { @@ -198,9 +200,9 @@ public void testLogging() throws Exception { conn.close(); assertEquals( - "select userid from account where userid = 'j2ee'" + System.getProperty("line.separator") - + System.getProperty("line.separator") + "USERID\t" + System.getProperty("line.separator") - + "j2ee\t" + System.getProperty("line.separator"), sw.toString()); + "select userid from account where userid = 'j2ee'" + LINE_SEPARATOR + + LINE_SEPARATOR + "USERID\t" + LINE_SEPARATOR + + "j2ee\t" + LINE_SEPARATOR, sw.toString()); } @Test @@ -221,9 +223,9 @@ public void testLoggingFullScipt() throws Exception { conn.close(); assertEquals( - "select userid from account where userid = 'j2ee';" + System.getProperty("line.separator") - + System.getProperty("line.separator") + "USERID\t" + System.getProperty("line.separator") - + "j2ee\t" + System.getProperty("line.separator"), sw.toString()); + "select userid from account where userid = 'j2ee';" + LINE_SEPARATOR + + LINE_SEPARATOR + "USERID\t" + LINE_SEPARATOR + + "j2ee\t" + LINE_SEPARATOR, sw.toString()); } private void runJPetStoreScripts(ScriptRunner runner) throws IOException, SQLException { @@ -264,10 +266,10 @@ public void shouldAcceptDelimiterVariations() throws Exception { Reader reader = new StringReader(sql); runner.runScript(reader); - verify(stmt, Mockito.times(1)).execute(eq("line 1;\n" + "line 2;\n\n")); - verify(stmt, Mockito.times(1)).execute(eq("line 3\n")); - verify(stmt, Mockito.times(1)).execute(eq("line 4\n")); - verify(stmt, Mockito.times(1)).execute(eq("line 5\n")); + verify(stmt, Mockito.times(1)).execute(eq("line 1;" + LINE_SEPARATOR + "line 2;" + LINE_SEPARATOR + LINE_SEPARATOR)); + verify(stmt, Mockito.times(1)).execute(eq("line 3" + LINE_SEPARATOR)); + verify(stmt, Mockito.times(1)).execute(eq("line 4" + LINE_SEPARATOR)); + verify(stmt, Mockito.times(1)).execute(eq("line 5" + LINE_SEPARATOR)); } @Test @@ -298,7 +300,7 @@ public void shouldAcceptMultiCharDelimiter() throws Exception { Reader reader = new StringReader(sql); runner.runScript(reader); - verify(stmt, Mockito.times(1)).execute(eq("line 1;\n" + "line 2;\n\n")); - verify(stmt, Mockito.times(1)).execute(eq("line 3\n")); + verify(stmt, Mockito.times(1)).execute(eq("line 1;" + LINE_SEPARATOR + "line 2;" + LINE_SEPARATOR + LINE_SEPARATOR)); + verify(stmt, Mockito.times(1)).execute(eq("line 3" + LINE_SEPARATOR)); } } From d30f6478153169dc57aa9f308d0c6f021a5bf7a2 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Sun, 25 Mar 2018 15:37:23 +0900 Subject: [PATCH 0097/2062] Update copyright year --- src/main/java/org/apache/ibatis/mapping/BoundSql.java | 2 +- .../java/org/apache/ibatis/reflection/ParamNameResolver.java | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/apache/ibatis/mapping/BoundSql.java b/src/main/java/org/apache/ibatis/mapping/BoundSql.java index 7bfa322b193..402983fab23 100644 --- a/src/main/java/org/apache/ibatis/mapping/BoundSql.java +++ b/src/main/java/org/apache/ibatis/mapping/BoundSql.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 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. diff --git a/src/main/java/org/apache/ibatis/reflection/ParamNameResolver.java b/src/main/java/org/apache/ibatis/reflection/ParamNameResolver.java index 4e623a5c39f..1a5d4408be3 100644 --- a/src/main/java/org/apache/ibatis/reflection/ParamNameResolver.java +++ b/src/main/java/org/apache/ibatis/reflection/ParamNameResolver.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 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. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.ibatis.reflection; import java.lang.annotation.Annotation; From adbcd98d17b993af6d19997aa81da82ae507669e Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Sat, 7 Apr 2018 07:25:48 +0900 Subject: [PATCH 0098/2062] Support type-safe on @Lang#value --- .../org/apache/ibatis/annotations/Lang.java | 6 ++++-- .../org/apache/ibatis/builder/BaseBuilder.java | 6 +++--- .../ibatis/builder/MapperBuilderAssistant.java | 4 ++-- .../annotation/MapperAnnotationBuilder.java | 2 +- .../builder/xml/XMLStatementBuilder.java | 4 ++-- .../scripting/LanguageDriverRegistry.java | 18 +++++++++--------- .../apache/ibatis/session/Configuration.java | 4 ++-- .../scripting/LanguageDriverRegistryTest.java | 4 ++-- 8 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/apache/ibatis/annotations/Lang.java b/src/main/java/org/apache/ibatis/annotations/Lang.java index b23c1c7c430..696bb520ee4 100644 --- a/src/main/java/org/apache/ibatis/annotations/Lang.java +++ b/src/main/java/org/apache/ibatis/annotations/Lang.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2016 the original author or authors. + * Copyright 2009-2018 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. @@ -15,6 +15,8 @@ */ package org.apache.ibatis.annotations; +import org.apache.ibatis.scripting.LanguageDriver; + import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -28,5 +30,5 @@ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Lang { - Class value(); + Class value(); } diff --git a/src/main/java/org/apache/ibatis/builder/BaseBuilder.java b/src/main/java/org/apache/ibatis/builder/BaseBuilder.java index 76c50cae94e..8bcf7109f5b 100644 --- a/src/main/java/org/apache/ibatis/builder/BaseBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/BaseBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2015 the original author or authors. + * Copyright 2009-2018 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. @@ -108,7 +108,7 @@ protected Object createInstance(String alias) { } } - protected Class resolveClass(String alias) { + protected Class resolveClass(String alias) { if (alias == null) { return null; } @@ -145,7 +145,7 @@ protected TypeHandler resolveTypeHandler(Class javaType, Class resolveAlias(String alias) { + protected Class resolveAlias(String alias) { return typeAliasRegistry.resolveAlias(alias); } } diff --git a/src/main/java/org/apache/ibatis/builder/MapperBuilderAssistant.java b/src/main/java/org/apache/ibatis/builder/MapperBuilderAssistant.java index f30bdc421f4..43121881eb4 100644 --- a/src/main/java/org/apache/ibatis/builder/MapperBuilderAssistant.java +++ b/src/main/java/org/apache/ibatis/builder/MapperBuilderAssistant.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 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. @@ -472,7 +472,7 @@ public ResultMapping buildResultMapping( nestedResultMap, notNullColumn, columnPrefix, typeHandler, flags, null, null, configuration.isLazyLoadingEnabled()); } - public LanguageDriver getLanguageDriver(Class langClass) { + public LanguageDriver getLanguageDriver(Class langClass) { if (langClass != null) { configuration.getLanguageRegistry().register(langClass); } else { 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 303854afe30..f52b9ced4f3 100644 --- a/src/main/java/org/apache/ibatis/builder/annotation/MapperAnnotationBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/annotation/MapperAnnotationBuilder.java @@ -381,7 +381,7 @@ void parseStatement(Method method) { private LanguageDriver getLanguageDriver(Method method) { Lang lang = method.getAnnotation(Lang.class); - Class langClass = null; + Class langClass = null; if (lang != null) { langClass = lang.value(); } diff --git a/src/main/java/org/apache/ibatis/builder/xml/XMLStatementBuilder.java b/src/main/java/org/apache/ibatis/builder/xml/XMLStatementBuilder.java index ddc7663a643..623eca94e16 100644 --- a/src/main/java/org/apache/ibatis/builder/xml/XMLStatementBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/xml/XMLStatementBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 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. @@ -192,7 +192,7 @@ private boolean databaseIdMatchesCurrent(String id, String databaseId, String re } private LanguageDriver getLanguageDriver(String lang) { - Class langClass = null; + Class langClass = null; if (lang != null) { langClass = resolveClass(lang); } diff --git a/src/main/java/org/apache/ibatis/scripting/LanguageDriverRegistry.java b/src/main/java/org/apache/ibatis/scripting/LanguageDriverRegistry.java index f981e55fde7..a8196cd0138 100644 --- a/src/main/java/org/apache/ibatis/scripting/LanguageDriverRegistry.java +++ b/src/main/java/org/apache/ibatis/scripting/LanguageDriverRegistry.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 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. @@ -23,17 +23,17 @@ */ public class LanguageDriverRegistry { - private final Map, LanguageDriver> LANGUAGE_DRIVER_MAP = new HashMap, LanguageDriver>(); + private final Map, LanguageDriver> LANGUAGE_DRIVER_MAP = new HashMap<>(); - private Class defaultDriverClass; + private Class defaultDriverClass; - public void register(Class cls) { + public void register(Class cls) { if (cls == null) { throw new IllegalArgumentException("null is not a valid Language Driver"); } if (!LANGUAGE_DRIVER_MAP.containsKey(cls)) { try { - LANGUAGE_DRIVER_MAP.put(cls, (LanguageDriver) cls.newInstance()); + LANGUAGE_DRIVER_MAP.put(cls, cls.newInstance()); } catch (Exception ex) { throw new ScriptingException("Failed to load language driver for " + cls.getName(), ex); } @@ -44,13 +44,13 @@ public void register(LanguageDriver instance) { if (instance == null) { throw new IllegalArgumentException("null is not a valid Language Driver"); } - Class cls = instance.getClass(); + Class cls = instance.getClass(); if (!LANGUAGE_DRIVER_MAP.containsKey(cls)) { LANGUAGE_DRIVER_MAP.put(cls, instance); } } - public LanguageDriver getDriver(Class cls) { + public LanguageDriver getDriver(Class cls) { return LANGUAGE_DRIVER_MAP.get(cls); } @@ -58,11 +58,11 @@ public LanguageDriver getDefaultDriver() { return getDriver(getDefaultDriverClass()); } - public Class getDefaultDriverClass() { + public Class getDefaultDriverClass() { return defaultDriverClass; } - public void setDefaultDriverClass(Class defaultDriverClass) { + public void setDefaultDriverClass(Class defaultDriverClass) { register(defaultDriverClass); this.defaultDriverClass = defaultDriverClass; } diff --git a/src/main/java/org/apache/ibatis/session/Configuration.java b/src/main/java/org/apache/ibatis/session/Configuration.java index f2a60fe03f7..bdc4d2d4042 100644 --- a/src/main/java/org/apache/ibatis/session/Configuration.java +++ b/src/main/java/org/apache/ibatis/session/Configuration.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 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. @@ -520,7 +520,7 @@ public LanguageDriverRegistry getLanguageRegistry() { return languageRegistry; } - public void setDefaultScriptingLanguage(Class driver) { + public void setDefaultScriptingLanguage(Class driver) { if (driver == null) { driver = XMLLanguageDriver.class; } diff --git a/src/test/java/org/apache/ibatis/scripting/LanguageDriverRegistryTest.java b/src/test/java/org/apache/ibatis/scripting/LanguageDriverRegistryTest.java index bbec82c36d8..1ec1f6f4ca1 100644 --- a/src/test/java/org/apache/ibatis/scripting/LanguageDriverRegistryTest.java +++ b/src/test/java/org/apache/ibatis/scripting/LanguageDriverRegistryTest.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 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. @@ -56,7 +56,7 @@ public void registerByTypeSameType() { @Test public void registerByTypeNull() { - when(registry).register((Class) null); + when(registry).register((Class) null); then(caughtException()).isInstanceOf(IllegalArgumentException.class) .hasMessage("null is not a valid Language Driver"); } From 44f324f8ee4cb316b2793414da2acc129aa82fdc Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Sat, 7 Apr 2018 12:09:57 +0900 Subject: [PATCH 0099/2062] Change the variable scope of annotation sets in MapperAnnotationBuilder --- .../annotation/MapperAnnotationBuilder.java | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) 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 303854afe30..c8a19cc0e34 100644 --- a/src/main/java/org/apache/ibatis/builder/annotation/MapperAnnotationBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/annotation/MapperAnnotationBuilder.java @@ -97,28 +97,30 @@ */ public class MapperAnnotationBuilder { - private final Set> sqlAnnotationTypes = new HashSet>(); - private final Set> sqlProviderAnnotationTypes = new HashSet>(); + private static final Set> SQL_ANNOTATION_TYPES = new HashSet<>(); + private static final Set> SQL_PROVIDER_ANNOTATION_TYPES = new HashSet<>(); private final Configuration configuration; private final MapperBuilderAssistant assistant; private final Class type; + static { + SQL_ANNOTATION_TYPES.add(Select.class); + SQL_ANNOTATION_TYPES.add(Insert.class); + SQL_ANNOTATION_TYPES.add(Update.class); + SQL_ANNOTATION_TYPES.add(Delete.class); + + SQL_PROVIDER_ANNOTATION_TYPES.add(SelectProvider.class); + SQL_PROVIDER_ANNOTATION_TYPES.add(InsertProvider.class); + SQL_PROVIDER_ANNOTATION_TYPES.add(UpdateProvider.class); + SQL_PROVIDER_ANNOTATION_TYPES.add(DeleteProvider.class); + } + public MapperAnnotationBuilder(Configuration configuration, Class type) { String resource = type.getName().replace('.', '/') + ".java (best guess)"; this.assistant = new MapperBuilderAssistant(configuration, resource); this.configuration = configuration; this.type = type; - - sqlAnnotationTypes.add(Select.class); - sqlAnnotationTypes.add(Insert.class); - sqlAnnotationTypes.add(Update.class); - sqlAnnotationTypes.add(Delete.class); - - sqlProviderAnnotationTypes.add(SelectProvider.class); - sqlProviderAnnotationTypes.add(InsertProvider.class); - sqlProviderAnnotationTypes.add(UpdateProvider.class); - sqlProviderAnnotationTypes.add(DeleteProvider.class); } public void parse() { @@ -516,11 +518,11 @@ private SqlCommandType getSqlCommandType(Method method) { } private Class getSqlAnnotationType(Method method) { - return chooseAnnotationType(method, sqlAnnotationTypes); + return chooseAnnotationType(method, SQL_ANNOTATION_TYPES); } private Class getSqlProviderAnnotationType(Method method) { - return chooseAnnotationType(method, sqlProviderAnnotationTypes); + return chooseAnnotationType(method, SQL_PROVIDER_ANNOTATION_TYPES); } private Class chooseAnnotationType(Method method, Set> types) { From 1845f0b08da9c0312d84e7bc1dd3df4e2eb58044 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Sat, 7 Apr 2018 21:34:32 +0900 Subject: [PATCH 0100/2062] Remove the parameter name restriction when assigning keys generated by multi-row insert. --- .../executor/keygen/Jdbc3KeyGenerator.java | 144 +++++-- .../submitted/keygen/CountryMapper.java | 67 +++- .../ibatis/submitted/keygen/CountryMapper.xml | 124 +++++- .../ibatis/submitted/keygen/CreateDB.sql | 7 + .../keygen/Jdbc3KeyGeneratorTest.java | 374 +++++++++++++++++- .../ibatis/submitted/keygen/Planet.java | 48 +++ 6 files changed, 714 insertions(+), 50 deletions(-) create mode 100644 src/test/java/org/apache/ibatis/submitted/keygen/Planet.java 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 fd6ccdf3745..35273c15672 100644 --- a/src/main/java/org/apache/ibatis/executor/keygen/Jdbc3KeyGenerator.java +++ b/src/main/java/org/apache/ibatis/executor/keygen/Jdbc3KeyGenerator.java @@ -19,18 +19,20 @@ import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import org.apache.ibatis.binding.BindingException; +import org.apache.ibatis.binding.MapperMethod.ParamMap; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.executor.ExecutorException; import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.reflection.ArrayUtil; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.session.defaults.DefaultSqlSession.StrictMap; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.TypeHandler; import org.apache.ibatis.type.TypeHandlerRegistry; @@ -43,6 +45,7 @@ public class Jdbc3KeyGenerator implements KeyGenerator { /** * A shared instance. + * * @since 3.4.3 */ public static final Jdbc3KeyGenerator INSTANCE = new Jdbc3KeyGenerator(); @@ -54,29 +57,24 @@ public void processBefore(Executor executor, MappedStatement ms, Statement stmt, @Override public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) { - processBatch(ms, stmt, getParameters(parameter)); + processBatch(ms, stmt, parameter); } - public void processBatch(MappedStatement ms, Statement stmt, Collection parameters) { + public void processBatch(MappedStatement ms, Statement stmt, Object parameter) { + final String[] keyProperties = ms.getKeyProperties(); + if (keyProperties == null || keyProperties.length == 0) { + return; + } ResultSet rs = null; try { rs = stmt.getGeneratedKeys(); final Configuration configuration = ms.getConfiguration(); - final TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); - final String[] keyProperties = ms.getKeyProperties(); - final ResultSetMetaData rsmd = rs.getMetaData(); - TypeHandler[] typeHandlers = null; - if (keyProperties != null && rsmd.getColumnCount() >= keyProperties.length) { - for (Object parameter : parameters) { - // there should be one row for each statement (also one for each parameter) - if (!rs.next()) { - break; - } - final MetaObject metaParam = configuration.newMetaObject(parameter); - if (typeHandlers == null) { - typeHandlers = getTypeHandlers(typeHandlerRegistry, metaParam, keyProperties, rsmd); - } - populateKeys(rs, metaParam, keyProperties, typeHandlers); + if (rs.getMetaData().getColumnCount() >= keyProperties.length) { + Object soleParam = getSoleParameter(parameter); + if (soleParam != null) { + assignKeysToParam(configuration, rs, keyProperties, soleParam); + } else { + assignKeysToOneOfParams(configuration, rs, keyProperties, (Map) parameter); } } } catch (Exception e) { @@ -92,25 +90,103 @@ public void processBatch(MappedStatement ms, Statement stmt, Collection } } - 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")); + protected void assignKeysToOneOfParams(final Configuration configuration, ResultSet rs, final String[] keyProperties, + Map paramMap) throws SQLException { + // For backward compatibility, search parameter with special name first. + if (paramMap.containsKey("collection")) { + Object param = paramMap.get("collection"); + if (param instanceof Collection) { + assignKeysToParam(configuration, rs, keyProperties, param); + return; + } + } else if (paramMap.containsKey("list")) { + Object param = paramMap.get("list"); + if (param instanceof List) { + assignKeysToParam(configuration, rs, keyProperties, param); + return; + } + } else if (paramMap.containsKey("array")) { + Object param = paramMap.get("array"); + if (param instanceof Object[]) { + assignKeysToParam(configuration, rs, keyProperties, param); + return; + } + } + // Assuming 'keyProperty' includes the parameter name. e.g. 'param.id'. + int firstDot = keyProperties[0].indexOf('.'); + if (firstDot == -1) { + throw new ExecutorException( + "Could not determine which parameter to assign generated keys to. " + + "Note that when there are multiple parameters, 'keyProperty' must include the parameter name (e.g. 'param.id'). " + + "Specified key properties are " + ArrayUtil.toString(keyProperties) + " and available parameters are " + + paramMap.keySet()); + } + String paramName = keyProperties[0].substring(0, firstDot); + Object param; + if (paramMap.containsKey(paramName)) { + param = paramMap.get(paramName); + } else { + throw new ExecutorException("Could not find parameter '" + paramName + "'. " + + "Note that when there are multiple parameters, 'keyProperty' must include the parameter name (e.g. 'param.id'). " + + "Specified key properties are " + ArrayUtil.toString(keyProperties) + " and available parameters are " + + paramMap.keySet()); + } + // Remove param name from 'keyProperty' string. e.g. 'param.id' -> 'id' + String[] modifiedKeyProperties = new String[keyProperties.length]; + for (int i = 0; i < keyProperties.length; i++) { + if (keyProperties[i].charAt(firstDot) == '.' && keyProperties[i].startsWith(paramName)) { + modifiedKeyProperties[i] = keyProperties[i].substring(firstDot + 1); + } else { + throw new ExecutorException("Assigning generated keys to multiple parameters is not supported. " + + "Note that when there are multiple parameters, 'keyProperty' must include the parameter name (e.g. 'param.id'). " + + "Specified key properties are " + ArrayUtil.toString(keyProperties) + " and available parameters are " + + paramMap.keySet()); + } + } + assignKeysToParam(configuration, rs, modifiedKeyProperties, param); + } + + private void assignKeysToParam(final Configuration configuration, ResultSet rs, final String[] keyProperties, + Object param) + throws SQLException { + final TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); + final ResultSetMetaData rsmd = rs.getMetaData(); + // Wrap the parameter in Collection to normalize the logic. + Collection paramAsCollection = null; + if (param instanceof Object[]) { + paramAsCollection = Arrays.asList((Object[]) param); + } else if (!(param instanceof Collection)) { + paramAsCollection = Arrays.asList(param); + } else { + paramAsCollection = (Collection) param; + } + TypeHandler[] typeHandlers = null; + for (Object obj : paramAsCollection) { + if (!rs.next()) { + break; + } + MetaObject metaParam = configuration.newMetaObject(obj); + if (typeHandlers == null) { + typeHandlers = getTypeHandlers(typeHandlerRegistry, metaParam, keyProperties, rsmd); } + populateKeys(rs, metaParam, keyProperties, typeHandlers); } - if (parameters == null) { - parameters = new ArrayList(); - parameters.add(parameter); + } + + private Object getSoleParameter(Object parameter) { + if (!(parameter instanceof ParamMap || parameter instanceof StrictMap)) { + return parameter; + } + Object soleParam = null; + for (Object paramValue : ((Map) parameter).values()) { + if (soleParam == null) { + soleParam = paramValue; + } else if (soleParam != paramValue) { + soleParam = null; + break; + } } - return parameters; + return soleParam; } private TypeHandler[] getTypeHandlers(TypeHandlerRegistry typeHandlerRegistry, MetaObject metaParam, String[] keyProperties, ResultSetMetaData rsmd) throws SQLException { diff --git a/src/test/java/org/apache/ibatis/submitted/keygen/CountryMapper.java b/src/test/java/org/apache/ibatis/submitted/keygen/CountryMapper.java index 3943882d581..e40ceec6e4f 100644 --- a/src/test/java/org/apache/ibatis/submitted/keygen/CountryMapper.java +++ b/src/test/java/org/apache/ibatis/submitted/keygen/CountryMapper.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2016 the original author or authors. + * Copyright 2009-2017 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. @@ -16,10 +16,75 @@ package org.apache.ibatis.submitted.keygen; import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Options; +import org.apache.ibatis.annotations.Param; public interface CountryMapper { + @Options(useGeneratedKeys = true, keyProperty = "id") + @Insert({ "insert into country (countryname,countrycode) values (#{countryname},#{countrycode})" }) + int insertBean(Country country); + + @Options(useGeneratedKeys = true, keyProperty = "id") + @Insert({ "insert into country (countryname,countrycode) values (#{country.countryname},#{country.countrycode})" }) + int insertNamedBean(@Param("country") Country country); + int insertList(List countries); + + int insertNamedList(@Param("countries") List countries); + + int insertSet(Set countries); + + int insertNamedSet(@Param("countries") Set countries); + + int insertArray(Country[] countries); + + int insertNamedArray(@Param("countries") Country[] countries); + + @Options(useGeneratedKeys = true, keyProperty = "country.id") + @Insert({ "insert into country (countryname,countrycode) values (#{country.countryname},#{country.countrycode})" }) + int insertMultiParams(@Param("country") Country country, @Param("someId") Integer someId); + + @Options(useGeneratedKeys = true, keyProperty = "id") + @Insert({ "insert into country (countryname,countrycode) values (#{country.countryname},#{country.countrycode})" }) + int insertMultiParams_keyPropertyWithoutParamName(@Param("country") Country country, @Param("someId") Integer someId); + + @Options(useGeneratedKeys = true, keyProperty = "bogus.id") + @Insert({ "insert into country (countryname,countrycode) values (#{country.countryname},#{country.countrycode})" }) + int insertMultiParams_keyPropertyWithWrongParamName(@Param("country") Country country, + @Param("someId") Integer someId); + + int insertListAndSomeId(@Param("list") List countries, @Param("someId") Integer someId); + + int insertSetAndSomeId(@Param("collection") Set countries, @Param("someId") Integer someId); + + int insertArrayAndSomeId(@Param("array") Country[] countries, @Param("someId") Integer someId); + + int insertList_MultiParams(@Param("countries") List countries, @Param("someId") Integer someId); + + int insertSet_MultiParams(@Param("countries") Set countries, @Param("someId") Integer someId); + + int insertArray_MultiParams(@Param("countries") Country[] countries, @Param("someId") Integer someId); + int insertUndefineKeyProperty(Country country); + @Options(useGeneratedKeys = true, keyProperty = "id,code") + @Insert({ "insert into planet (name) values (#{name})" }) + int insertPlanet(Planet planet); + + int insertPlanets(List planets); + + @Options(useGeneratedKeys = true, keyProperty = "planet.id,planet.code") + @Insert({ "insert into planet (name) values (#{planet.name})" }) + int insertPlanet_MultiParams(@Param("planet") Planet planet, @Param("someId") Integer someId); + + int insertPlanets_MultiParams(@Param("planets") List planets, @Param("someId") Integer someId); + + @Options(useGeneratedKeys = true, keyProperty = "planet.id,map.code") + @Insert({ "insert into planet (name) values (#{planet.name})" }) + int insertAssignKeysToTwoParams(@Param("planet") Planet planet, @Param("map") Map map); } diff --git a/src/test/java/org/apache/ibatis/submitted/keygen/CountryMapper.xml b/src/test/java/org/apache/ibatis/submitted/keygen/CountryMapper.xml index 1d08851f68f..9bd78d78cc8 100644 --- a/src/test/java/org/apache/ibatis/submitted/keygen/CountryMapper.xml +++ b/src/test/java/org/apache/ibatis/submitted/keygen/CountryMapper.xml @@ -1,7 +1,7 @@ - + ]]>

    @@ -107,19 +107,17 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ - + ]]>

    - 提示 你可以使用 ":" 作为属性键(e.g. db:username) - 或者你也可以在sql定义中使用 OGNL 表达式的三元运算符(e.g. ${tableName != null ? tableName : 'global_constants'}), - 你应该通过增加一个指定的属性来改变分隔键和默认值的字符。例如: + 提示 如果你已经使用 ":" 作为属性的键(如:db:username) ,或者你已经在 SQL 定义中使用 OGNL 表达式的三元运算符(如: ${tableName != null ? tableName : 'global_constants'}),你应该通过设置特定的属性来修改分隔键名和默认值的字符。例如:

    - + ]]> @@ -128,13 +126,13 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ ]]> - +

    这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。下表描述了设置中各项的意图、默认值等。

    - + @@ -189,7 +187,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ multipleResultSetsEnabled @@ -305,7 +303,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ 任意正整数 @@ -313,7 +311,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ safeRowBoundsEnabled @@ -478,7 +476,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING @@ -492,7 +490,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ CGLIB | JAVASSIST @@ -500,13 +498,13 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ vfsImpl @@ -515,7 +513,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ @@ -564,8 +562,8 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ ]]> - -

    类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。例如:

    + +

    类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。例如:

    @@ -575,21 +573,21 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ ]]> -

    当这样配置时,Blog可以用在任何使用domain.blog.Blog的地方。

    -

    也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如: +

    当这样配置时,Blog 可以用在任何使用 domain.blog.Blog 的地方。

    +

    也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:

    ]]>

    每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 - 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。看下面的例子:

    + 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。见下面的例子:

    -

    这是一些为常见的 Java 类型内建的相应的类型别名。它们都是大小写不敏感的,需要注意的是由基本类型名称重复导致的特殊处理。

    +

    这是一些为常见的 Java 类型内建的相应的类型别名。它们都是不区分大小写的,注意对基本类型名称重复采取的特殊命名风格。

    设置参数设置名 描述 有效值 默认值 - 是否允许单一语句返回多结果集(需要兼容驱动)。 + 是否允许单一语句返回多结果集(需要驱动支持)。 true | false @@ -218,8 +216,8 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ useGeneratedKeys - 允许 JDBC 支持自动生成主键,需要驱动兼容。 - 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)。 + 允许 JDBC 支持自动生成主键,需要驱动支持。 + 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能支持但仍可正常工作(比如 Derby)。 true | false @@ -291,7 +289,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ 任意正整数 - Not Set (null) + 未设置 (null)
    - Not Set (null) + 未设置 (null)
    - 允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为false。 + 允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。 true | false @@ -327,7 +325,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ safeResultHandlerEnabled - 允许在嵌套语句中使用分页(ResultHandler)。如果允许使用则设置为false。 + 允许在嵌套语句中使用分页(ResultHandler)。如果允许使用则设置为 false。 true | false @@ -375,7 +373,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 - JdbcType 常量. 大多都为: NULL, VARCHAR and OTHER + JdbcType 常量,常用值:NULL, VARCHAR 或 OTHER。 OTHER @@ -414,7 +412,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ defaultEnumTypeHandler - 指定 Enum 使用的默认 TypeHandler 。 (从3.4.5开始) + 指定 Enum 使用的默认 TypeHandler 。 (从 3.4.5 开始) 一个类型别名或完全限定类名。 @@ -428,7 +426,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ callSettersOnNulls - 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。注意基本类型(int、boolean等)是不能设置成 null 的。 + 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值初始化的时候比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。 true | false @@ -444,7 +442,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ 当返回行的所有列都是空时,MyBatis默认返回null。 当开启这个设置时,MyBatis会返回一个空实例。 - 请注意,它也适用于嵌套的结果集 (i.e. collectioin and association)。(从3.4.2开始) + 请注意,它也适用于嵌套的结果集 (如集合或关联)。(从 3.4.2 开始) true | false @@ -464,7 +462,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ 任何字符串 - Not set + 未设置
    - Not set + 未设置
    - JAVASSIST (MyBatis 3.3 or above) + JAVASSIST (MyBatis 3.3 以上)
    - 指定VFS的实现 + 指定 VFS 的实现 - 自定义VFS的实现的类全限定名,以逗号分隔。 + 自定义 VFS 的实现的类全限定名,以逗号分隔。 - Not set + 未设置
    允许使用方法签名中的名称作为语句参数名称。 - 为了使用该特性,你的工程必须采用Java 8编译,并且加上-parameters选项。(从3.4.1开始) + 为了使用该特性,你的工程必须采用 Java 8 编译,并且加上-parameters选项。(从 3.4.1 开始) true | false @@ -530,14 +528,14 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environ 指定一个提供Configuration实例的类。 - 这个被返回的Configuration实例用来加载被反序列化对象的懒加载属性值。 - 这个类必须包含一个签名方法static Configuration getConfiguration(). (从 3.2.3 版本开始) + 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 + 这个类必须包含一个签名为static Configuration getConfiguration()的方法。 (从 3.2.3 开始) 类型别名或者全类名. - Not set + 未设置
    @@ -821,12 +819,12 @@ public class Author {
    - +

    无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器。

    提示 - 从 3.4.5 开始,MyBatis 默认支持 JSR-310(日期和时间 API) 。 + 从 3.4.5 开始,MyBatis 默认支持 JSR-310(日期和时间 API) 。

    @@ -1104,7 +1102,7 @@ public class Author { Enumeration Type @@ -1115,7 +1113,7 @@ public class Author { Enumeration Type @@ -1225,7 +1223,7 @@ public class Author { java.time.YearMonth @@ -1280,16 +1278,15 @@ public class ExampleTypeHandler extends BaseTypeHandler { ]]>

    - 使用这个的类型处理器将会覆盖已经存在的处理 Java 的 String 类型属性和 VARCHAR 参数及结果的类型处理器。 - 要注意 MyBatis 不会窥探数据库元信息来决定使用哪种类型,所以你必须在参数和结果映射中指明那是 VARCHAR 类型的字段, - 以使其能够绑定到正确的类型处理器上。 - 这是因为:MyBatis 直到语句被执行才清楚数据类型。 + 使用上述的类型处理器将会覆盖已经存在的处理 Java 的 String 类型属性和 VARCHAR 参数及结果的类型处理器。 + 要注意 MyBatis 不会通过窥探数据库元信息来决定使用哪种类型,所以你必须在参数和结果映射中指明那是 VARCHAR 类型的字段, + 以使其能够绑定到正确的类型处理器上。这是因为 MyBatis 直到语句被执行时才清楚数据类型。

    通过类型处理器的泛型,MyBatis 可以得知该类型处理器处理的 Java 类型,不过这种行为可以通过两种方法改变:

      -
    • 在类型处理器的配置元素(typeHandler element)上增加一个 javaType 属性(比如:javaType="String"); +
    • 在类型处理器的配置元素(typeHandler 元素)上增加一个 javaType 属性(比如:javaType="String");
    • 在类型处理器的类上(TypeHandler class)增加一个 @MappedTypes 注解来指定与其关联的 Java 类型列表。 如果在 javaType 属性中也同时指定,则注解方式将被忽略。 @@ -1307,11 +1304,11 @@ public class ExampleTypeHandler extends BaseTypeHandler {

    - 当决定在ResultMap中使用某一TypeHandler时,此时java类型是已知的(从结果类型中获得),但是JDBC类型是未知的。 - 因此Mybatis使用javaType=[TheJavaType], jdbcType=null的组合来选择一个TypeHandler。 - 这意味着使用@MappedJdbcTypes注解可以限制TypeHandler的范围,同时除非显式的设置,否则TypeHandler在ResultMap中将是无效的。 - 如果希望在ResultMap中使用TypeHandler,那么设置@MappedJdbcTypes注解的includeNullJdbcType=true即可。 - 然而从Mybatis 3.4.0开始,如果只有一个注册的TypeHandler来处理Java类型,那么它将是ResultMap使用Java类型时的默认值(即使没有includeNullJdbcType=true)。 + 当在 ResultMap 中决定使用哪种类型处理器时,此时 Java 类型是已知的(从结果类型中获得),但是 JDBC 类型是未知的。 + 因此 Mybatis 使用 javaType=[Java 类型], jdbcType=null 的组合来选择一个类型处理器。 + 这意味着使用 @MappedJdbcTypes 注解可以限制类型处理器的范围,同时除非显式的设置,否则类型处理器在 ResultMap 中将是无效的。 + 如果希望在 ResultMap 中使用类型处理器,那么设置 @MappedJdbcTypes 注解的 includeNullJdbcType=true 即可。 + 然而从 Mybatis 3.4.0 开始,如果只有一个注册的类型处理器来处理 Java 类型,那么它将是 ResultMap使用 Java 类型时的默认值(即使没有 includeNullJdbcType=true)。

    最后,可以让 MyBatis 为你查找类型处理器:

    @@ -1321,7 +1318,7 @@ public class ExampleTypeHandler extends BaseTypeHandler { ]]> -

    注意在使用自动检索(autodiscovery)功能的时候,只能通过注解方式来指定 JDBC 的类型。

    +

    注意在使用自动发现(autodiscovery)功能的时候,只能通过注解方式来指定 JDBC 的类型。

    你可以创建一个能够处理多个类的泛型类型处理器。为了使用泛型类型处理器, 需要增加一个接受该类的 class 作为参数的构造器,这样在构造一个类型处理器的时候 MyBatis 就会传入一个具体的类。

    @@ -1351,7 +1348,7 @@ public class GenericTypeHandler extends BaseTypeHandler {

    不过,我们可能不想存储名字,相反我们的 DBA 会坚持使用整形值代码。那也一样轻而易举: 在配置文件中把 EnumOrdinalTypeHandler 加到 typeHandlers 中即可, - 这样每个 RoundingMode 将通过他们的序数值来映射成对应的整形。 + 这样每个 RoundingMode 将通过他们的序数值来映射成对应的整形数值。

    @@ -1493,12 +1490,12 @@ public class ExamplePlugin implements Interceptor {

    除了用插件来修改 MyBatis 核心行为之外,还可以通过完全覆盖配置类来达到目的。只需继承后覆盖其中的每个方法,再把它传递到 SqlSessionFactoryBuilder.build(myConfig) 方法即可。再次重申,这可能会严重影响 MyBatis 的行为,务请慎之又慎。

    - +

    MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, - 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者共享相同 Schema 的多个生产数据库, - 想使用相同的 SQL 映射。许多类似的用例。

    + 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中 + 使用相同的 SQL 映射。有许多类似的使用场景。

    - 不过要记住:尽管可以配置多个环境,每个 SqlSessionFactory 实例只能选择其一。 + 不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

    所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推,记起来很简单: @@ -1542,16 +1539,16 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, propert

    • - 默认的环境 ID(比如:default="development")。 + 默认使用的环境 ID(比如:default="development")。
    • - 每个 environment 元素定义的环境 ID(比如:id="development")。 + 每个 environment 元素定义的环境 ID(比如:id="development")。
    • - 事务管理器的配置(比如:type="JDBC")。 + 事务管理器的配置(比如:type="JDBC")。
    • - 数据源的配置(比如:type="POOLED")。 + 数据源的配置(比如:type="POOLED")。

    默认的环境和环境 ID 是自解释的,因此一目了然。你可以对环境随意命名,但一定要保证默认的环境 ID 要匹配其中一个环境 ID。 @@ -1575,7 +1572,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, propert 因为 Spring 模块会使用自带的管理器来覆盖前面的配置。

    - 这两种事务管理器类型都不需要任何属性。它们不过是类型别名,换句话说,你可以使用 TransactionFactory 接口的实现类的完全限定名或类型别名代替它们。 + 这两种事务管理器类型都不需要设置任何属性。它们其实是类型别名,换句话说,你可以使用 TransactionFactory 接口的实现类的完全限定名或类型别名代替它们。

    defaultTransactionIsolationLevel – 默认的连接事务隔离级别。 -

    作为可选项,你也可以传递属性给数据库驱动。要这样做,属性的前缀为“driver.”,例如: +

    作为可选项,你也可以传递属性给数据库驱动。只需在属性名加上“driver.”前缀即可,例如:

    • driver.encoding=UTF8
    • @@ -1638,8 +1635,8 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, propert
    • poolTimeToWait – 这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直安静的失败),默认值:20000 毫秒(即 20 秒)。
    • poolMaximumLocalBadConnectionTolerance – 这是一个关于坏连接容忍度的底层设置, - 作用于每一个尝试从缓存池获取连接的线程. 如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过 poolMaximumIdleConnections - 与 poolMaximumLocalBadConnectionTolerance 之和。 默认值:3 (新增于 3.4.5) + 作用于每一个尝试从缓存池获取连接的线程。 如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过 poolMaximumIdleConnections + 与 poolMaximumLocalBadConnectionTolerance 之和。 默认值:3 (新增于 3.4.5)
    • poolPingQuery – 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动失败时带有一个恰当的错误消息。
    • @@ -1652,7 +1649,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, propert JNDI – 这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。这种数据源配置只需要两个属性:

        -
      • initial_context – 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么 data_source 属性将会直接从 InitialContext 中寻找。 +
      • initial_context – 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么将会直接从 InitialContext 中寻找 data_source 属性。
      • data_source – 这是引用数据源实例位置的上下文的路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。
      • @@ -1700,7 +1697,7 @@ public class C3P0DataSourceFactory extends UnpooledDataSourceFactory { - +

        MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库 databaseId 属性的所有语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。 @@ -1709,8 +1706,8 @@ public class C3P0DataSourceFactory extends UnpooledDataSourceFactory { ]]> -

        这里的 DB_VENDOR 会通过 DatabaseMetaData#getDatabaseProductName() 返回的字符串进行设置。 - 由于通常情况下这个字符串都非常长而且相同产品的不同版本会返回不同的值,所以最好通过设置属性别名来使其变短,如下:

        +

        DB_VENDOR 对应的 databaseIdProvider 实现会将 databaseId 设置为 DatabaseMetaData#getDatabaseProductName() 返回的字符串。 + 由于通常情况下这些字符串都非常长而且相同产品的不同版本会返回不同的值,所以你可能想通过设置属性别名来使其变短,如下:

        @@ -1718,7 +1715,7 @@ public class C3P0DataSourceFactory extends UnpooledDataSourceFactory { ]]> -

        在提供了属性别名时,DB_VENDOR databaseIdProvider 将被设置为第一个能匹配数据库产品名称的属性键对应的值,如果没有匹配的属性将会设置为 “null”。 +

        在提供了属性别名时,DB_VENDOR 的 databaseIdProvider 实现会将 databaseId 设置为第一个数据库产品名与属性中的名称相匹配的值,如果没有匹配的属性将会设置为 “null”。 在这个例子中,如果 getDatabaseProductName() 返回“Oracle (DataDirect)”,databaseId 将被设置为“oracle”。

        你可以通过实现接口 org.apache.ibatis.mapping.DatabaseIdProvider 并在 mybatis-config.xml 中注册来构建自己的 DatabaseIdProvider:

        diff --git a/src/site/zh/xdoc/getting-started.xml b/src/site/zh/xdoc/getting-started.xml index 280cadd4904..7b396d53af9 100644 --- a/src/site/zh/xdoc/getting-started.xml +++ b/src/site/zh/xdoc/getting-started.xml @@ -1,7 +1,7 @@ + + + + + + + + + + + + + + + + From e155f8ee926347595cd1dbdfba2e04374f5efcce Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Sun, 22 Apr 2018 19:39:06 +0900 Subject: [PATCH 0112/2062] Change a logic for controlling accessible member on OGNL #1258 --- .../apache/ibatis/reflection/Reflector.java | 14 ++++-- .../scripting/xmltags/OgnlMemberAccess.java | 10 +++- .../submitted/member_access/CreateDB.sql | 24 ---------- .../member_access/MemberAccessTest.java | 48 +++++++++---------- 4 files changed, 40 insertions(+), 56 deletions(-) delete mode 100644 src/test/java/org/apache/ibatis/submitted/member_access/CreateDB.sql diff --git a/src/main/java/org/apache/ibatis/reflection/Reflector.java b/src/main/java/org/apache/ibatis/reflection/Reflector.java index c208cf23b3d..6e161cd222f 100644 --- a/src/main/java/org/apache/ibatis/reflection/Reflector.java +++ b/src/main/java/org/apache/ibatis/reflection/Reflector.java @@ -77,7 +77,7 @@ private void addDefaultConstructor(Class clazz) { Constructor[] consts = clazz.getDeclaredConstructors(); for (Constructor constructor : consts) { if (constructor.getParameterTypes().length == 0) { - if (canAccessPrivateMethods()) { + if (canControlMemberAccessible()) { try { constructor.setAccessible(true); } catch (Exception e) { @@ -254,7 +254,7 @@ private Class typeToClass(Type src) { private void addFields(Class clazz) { Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { - if (canAccessPrivateMethods()) { + if (canControlMemberAccessible()) { try { field.setAccessible(true); } catch (Exception e) { @@ -339,7 +339,7 @@ private void addUniqueMethods(Map uniqueMethods, Method[] method // if it is known, then an extended class must have // overridden a method if (!uniqueMethods.containsKey(signature)) { - if (canAccessPrivateMethods()) { + if (canControlMemberAccessible()) { try { currentMethod.setAccessible(true); } catch (Exception e) { @@ -372,7 +372,13 @@ private String getSignature(Method method) { return sb.toString(); } - private static boolean canAccessPrivateMethods() { + /** + * Checks whether can control member accessible. + * + * @return If can control member accessible, it return {@literal true} + * @since 3.5.0 + */ + public static boolean canControlMemberAccessible() { try { SecurityManager securityManager = System.getSecurityManager(); if (null != securityManager) { diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/OgnlMemberAccess.java b/src/main/java/org/apache/ibatis/scripting/xmltags/OgnlMemberAccess.java index dd3cb8cfd26..4029ffbadac 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/OgnlMemberAccess.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/OgnlMemberAccess.java @@ -16,10 +16,10 @@ package org.apache.ibatis.scripting.xmltags; import ognl.MemberAccess; +import org.apache.ibatis.reflection.Reflector; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Member; -import java.lang.reflect.Modifier; import java.util.Map; /** @@ -35,6 +35,12 @@ */ class OgnlMemberAccess implements MemberAccess { + private final boolean canControlMemberAccessible; + + OgnlMemberAccess() { + this.canControlMemberAccessible = Reflector.canControlMemberAccessible(); + } + @Override public Object setup(Map context, Object target, Member member, String propertyName) { Object result = null; @@ -58,7 +64,7 @@ public void restore(Map context, Object target, Member member, String propertyNa @Override public boolean isAccessible(Map context, Object target, Member member, String propertyName) { - return Modifier.isPublic(member.getModifiers()); + return canControlMemberAccessible; } } diff --git a/src/test/java/org/apache/ibatis/submitted/member_access/CreateDB.sql b/src/test/java/org/apache/ibatis/submitted/member_access/CreateDB.sql deleted file mode 100644 index cdace43886a..00000000000 --- a/src/test/java/org/apache/ibatis/submitted/member_access/CreateDB.sql +++ /dev/null @@ -1,24 +0,0 @@ --- --- Copyright 2009-2018 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 ( - id int, - name varchar(20) -); - -insert into users (id, name) values(1, 'User1'); diff --git a/src/test/java/org/apache/ibatis/submitted/member_access/MemberAccessTest.java b/src/test/java/org/apache/ibatis/submitted/member_access/MemberAccessTest.java index 4105c764380..95a5631578d 100644 --- a/src/test/java/org/apache/ibatis/submitted/member_access/MemberAccessTest.java +++ b/src/test/java/org/apache/ibatis/submitted/member_access/MemberAccessTest.java @@ -16,7 +16,6 @@ package org.apache.ibatis.submitted.member_access; import java.io.Reader; -import java.sql.Connection; import java.util.HashMap; import java.util.Map; @@ -26,7 +25,6 @@ import org.apache.ibatis.annotations.Results; import org.apache.ibatis.annotations.Select; 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; @@ -49,14 +47,6 @@ public static void setUp() throws Exception { sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); sqlSessionFactory.getConfiguration().addMapper(Mapper.class); } - try (SqlSession session = sqlSessionFactory.openSession(); - Connection conn = session.getConnection(); - Reader reader = Resources - .getResourceAsReader("org/apache/ibatis/submitted/member_access/CreateDB.sql")) { - ScriptRunner runner = new ScriptRunner(conn); - runner.setLogWriter(null); - runner.runScript(reader); - } } @Test @@ -87,15 +77,15 @@ public void parameterMappingAndResultAutoMappingUsingOgnl() { Params params = new Params(); Bean bean = mapper.resultAutoMappingUsingOgnl(params); - assertEquals(params.privateField, bean.privateField); - assertEquals(params.packagePrivateField, bean.packagePrivateField); - assertEquals(params.protectedField, bean.protectedField); - assertEquals(params.publicField, bean.publicField); - assertEquals(params.getPrivateProperty(), bean.properties.get("privateProperty")); - assertEquals(params.getPackagePrivateProperty(), + assertEquals(params.privateField + "%", bean.privateField); + assertEquals(params.packagePrivateField + "%", bean.packagePrivateField); + assertEquals(params.protectedField + "%", bean.protectedField); + assertEquals(params.publicField + "%", bean.publicField); + assertEquals(params.getPrivateProperty() + "%", bean.properties.get("privateProperty")); + assertEquals(params.getPackagePrivateProperty() + "%", bean.properties.get("packagePrivateProperty")); - assertEquals(params.getProtectedProperty(), bean.properties.get("protectedProperty")); - assertEquals(params.getPublicProperty(), bean.properties.get("publicProperty")); + assertEquals(params.getProtectedProperty() + "%", bean.properties.get("protectedProperty")); + assertEquals(params.getPublicProperty() + "%", bean.properties.get("publicProperty")); } } @@ -217,17 +207,23 @@ interface Mapper { // @formatter:off ""); + return sqlBuffer.toString(); + } + + public String buildUpdateSelective(ProviderContext context) { + final String tableName = context.getMapperType().getAnnotation(BaseMapper.Meta.class).tableName(); + Map columnMap = getColumnMap(context); + StringBuffer sqlBuffer = new StringBuffer(); + sqlBuffer.append(""); + return sqlBuffer.toString(); + } + + public String buildGetByEntityQuery(ProviderContext context) { + final String tableName = context.getMapperType().getAnnotation(BaseMapper.Meta.class).tableName(); + Map columnMap = getColumnMap(context); + StringBuffer sqlBuffer = new StringBuffer(); + sqlBuffer.append(""); + return sqlBuffer.toString(); + } + } diff --git a/src/test/java/org/apache/ibatis/submitted/sqlprovider/SqlProviderTest.java b/src/test/java/org/apache/ibatis/submitted/sqlprovider/SqlProviderTest.java index a85962f2a9c..41b5ef70ebc 100644 --- a/src/test/java/org/apache/ibatis/submitted/sqlprovider/SqlProviderTest.java +++ b/src/test/java/org/apache/ibatis/submitted/sqlprovider/SqlProviderTest.java @@ -538,4 +538,63 @@ public static String oneArgumentAndProviderContext(Integer value, ProviderContex } + @Test + public void shouldInsertUserSelective() { + SqlSession sqlSession = sqlSessionFactory.openSession(); + try { + Mapper mapper = sqlSession.getMapper(Mapper.class); + User user = new User(); + user.setId(999); + mapper.insertSelective(user); + + User loadedUser = mapper.getUser(999); + assertEquals(null, loadedUser.getName()); + + } finally { + sqlSession.close(); + } + } + + + @Test + public void shouldUpdateUserSelective() { + SqlSession sqlSession = sqlSessionFactory.openSession(); + try { + Mapper mapper = sqlSession.getMapper(Mapper.class); + User user = new User(); + user.setId(999); + user.setName("MyBatis"); + mapper.insert(user); + + user.setName(null); + mapper.updateSelective(user); + + User loadedUser = mapper.getUser(999); + assertEquals("MyBatis", loadedUser.getName()); + + } finally { + sqlSession.close(); + } + } + + @Test + public void mapperGetByEntity() { + SqlSession sqlSession = sqlSessionFactory.openSession(); + try { + Mapper mapper = sqlSession.getMapper(Mapper.class); + User query = new User(); + query.setName("User4"); + assertEquals(1, mapper.getByEntity(query).size()); + query = new User(); + query.setId(1); + assertEquals(1, mapper.getByEntity(query).size()); + query = new User(); + query.setId(1); + query.setName("User4"); + assertEquals(0, mapper.getByEntity(query).size()); + } finally { + sqlSession.close(); + } + } + } diff --git a/src/test/java/org/apache/ibatis/submitted/sqlprovider/User.java b/src/test/java/org/apache/ibatis/submitted/sqlprovider/User.java index 3b17cb33424..e44d259ba7c 100644 --- a/src/test/java/org/apache/ibatis/submitted/sqlprovider/User.java +++ b/src/test/java/org/apache/ibatis/submitted/sqlprovider/User.java @@ -16,8 +16,9 @@ package org.apache.ibatis.submitted.sqlprovider; public class User { - + @BaseMapper.Column private Integer id; + @BaseMapper.Column private String name; public Integer getId() { From 2a96a54d8cb1856175cf043ff79a84b5a1a3d074 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Mon, 3 Dec 2018 22:29:39 +0900 Subject: [PATCH 0210/2062] Use a system property as the switch. --- .../apache/ibatis/parsing/XPathParser.java | 9 +- .../builder/xsd/XmlConfigBuilderTest.java | 194 +++++++++--------- .../builder/xsd/XmlMapperBuilderTest.java | 31 ++- 3 files changed, 111 insertions(+), 123 deletions(-) diff --git a/src/main/java/org/apache/ibatis/parsing/XPathParser.java b/src/main/java/org/apache/ibatis/parsing/XPathParser.java index d803aa0485f..9c755d5df2f 100644 --- a/src/main/java/org/apache/ibatis/parsing/XPathParser.java +++ b/src/main/java/org/apache/ibatis/parsing/XPathParser.java @@ -31,7 +31,6 @@ import javax.xml.xpath.XPathFactory; import org.apache.ibatis.builder.BuilderException; -import org.apache.ibatis.io.Resources; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -55,7 +54,7 @@ public class XPathParser { *

        * @since 3.5.0 */ - public static final String KEY_USE_XSD = "org.apache.ibatis.parsing.XPathParser.use-xsd"; + public static final String KEY_USE_XSD = "org.mybatis.useXsd"; private static final String USE_XSD_DEFAULT_VALUE = "false"; @@ -252,7 +251,7 @@ private Document createDocument(InputSource inputSource) { factory.setCoalescing(false); factory.setExpandEntityReferences(true); - boolean useXsd = Boolean.parseBoolean(getPropertyValue(KEY_USE_XSD, USE_XSD_DEFAULT_VALUE)); + boolean useXsd = Boolean.parseBoolean(System.getProperty(KEY_USE_XSD, USE_XSD_DEFAULT_VALUE)); if (useXsd) { factory.setNamespaceAware(true); factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", XMLConstants.W3C_XML_SCHEMA_NS_URI); @@ -289,8 +288,4 @@ private void commonConstructor(boolean validation, Properties variables, EntityR this.xpath = factory.newXPath(); } - private String getPropertyValue(String key, String defaultValue) { - return (variables == null) ? defaultValue : variables.getProperty(key, defaultValue); - } - } diff --git a/src/test/java/org/apache/ibatis/builder/xsd/XmlConfigBuilderTest.java b/src/test/java/org/apache/ibatis/builder/xsd/XmlConfigBuilderTest.java index 2d6f2f3da4e..20cfc29da98 100644 --- a/src/test/java/org/apache/ibatis/builder/xsd/XmlConfigBuilderTest.java +++ b/src/test/java/org/apache/ibatis/builder/xsd/XmlConfigBuilderTest.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 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. @@ -49,7 +49,6 @@ import java.io.InputStream; import java.util.Arrays; import java.util.HashSet; -import java.util.Properties; import java.util.Set; import static org.hamcrest.core.Is.is; @@ -63,108 +62,105 @@ public class XmlConfigBuilderTest { @Test public void shouldSuccessfullyLoadMinimalXMLConfigFile() throws Exception { + System.setProperty(XPathParser.KEY_USE_XSD, "true"); String resource = "org/apache/ibatis/builder/xsd/MinimalMapperConfig.xml"; - InputStream inputStream = Resources.getResourceAsStream(resource); - Properties variables = new Properties(); - variables.setProperty(XPathParser.KEY_USE_XSD, "true"); - XMLConfigBuilder builder = new XMLConfigBuilder(inputStream, null, variables); - Configuration config = builder.parse(); - assertNotNull(config); - assertThat(config.getAutoMappingBehavior(), is(AutoMappingBehavior.PARTIAL)); - assertThat(config.getAutoMappingUnknownColumnBehavior(), is(AutoMappingUnknownColumnBehavior.NONE)); - assertThat(config.isCacheEnabled(), is(true)); - assertThat(config.getProxyFactory(), is(instanceOf(JavassistProxyFactory.class))); - assertThat(config.isLazyLoadingEnabled(), is(false)); - assertThat(config.isAggressiveLazyLoading(), is(false)); - assertThat(config.isMultipleResultSetsEnabled(), is(true)); - assertThat(config.isUseColumnLabel(), is(true)); - assertThat(config.isUseGeneratedKeys(), is(false)); - assertThat(config.getDefaultExecutorType(), is(ExecutorType.SIMPLE)); - assertNull(config.getDefaultStatementTimeout()); - assertNull(config.getDefaultFetchSize()); - assertThat(config.isMapUnderscoreToCamelCase(), is(false)); - assertThat(config.isSafeRowBoundsEnabled(), is(false)); - assertThat(config.getLocalCacheScope(), is(LocalCacheScope.SESSION)); - assertThat(config.getJdbcTypeForNull(), is(JdbcType.OTHER)); - assertThat(config.getLazyLoadTriggerMethods(), is((Set) new HashSet(Arrays.asList("equals", "clone", "hashCode", "toString")))); - assertThat(config.isSafeResultHandlerEnabled(), is(true)); - assertThat(config.getDefaultScriptingLanguageInstance(), is(instanceOf(XMLLanguageDriver.class))); - assertThat(config.isCallSettersOnNulls(), is(false)); - assertNull(config.getLogPrefix()); - assertNull(config.getLogImpl()); - assertNull(config.getConfigurationFactory()); + try (InputStream inputStream = Resources.getResourceAsStream(resource)) { + XMLConfigBuilder builder = new XMLConfigBuilder(inputStream); + Configuration config = builder.parse(); + assertNotNull(config); + assertThat(config.getAutoMappingBehavior(), is(AutoMappingBehavior.PARTIAL)); + assertThat(config.getAutoMappingUnknownColumnBehavior(), is(AutoMappingUnknownColumnBehavior.NONE)); + assertThat(config.isCacheEnabled(), is(true)); + assertThat(config.getProxyFactory(), is(instanceOf(JavassistProxyFactory.class))); + assertThat(config.isLazyLoadingEnabled(), is(false)); + assertThat(config.isAggressiveLazyLoading(), is(false)); + assertThat(config.isMultipleResultSetsEnabled(), is(true)); + assertThat(config.isUseColumnLabel(), is(true)); + assertThat(config.isUseGeneratedKeys(), is(false)); + assertThat(config.getDefaultExecutorType(), is(ExecutorType.SIMPLE)); + assertNull(config.getDefaultStatementTimeout()); + assertNull(config.getDefaultFetchSize()); + assertThat(config.isMapUnderscoreToCamelCase(), is(false)); + assertThat(config.isSafeRowBoundsEnabled(), is(false)); + assertThat(config.getLocalCacheScope(), is(LocalCacheScope.SESSION)); + assertThat(config.getJdbcTypeForNull(), is(JdbcType.OTHER)); + assertThat(config.getLazyLoadTriggerMethods(), is((Set) new HashSet(Arrays.asList("equals", "clone", "hashCode", "toString")))); + assertThat(config.isSafeResultHandlerEnabled(), is(true)); + assertThat(config.getDefaultScriptingLanguageInstance(), is(instanceOf(XMLLanguageDriver.class))); + assertThat(config.isCallSettersOnNulls(), is(false)); + assertNull(config.getLogPrefix()); + assertNull(config.getLogImpl()); + assertNull(config.getConfigurationFactory()); + } } @Test - public void shouldSuccessfullyLoadXMLConfigFile() throws Exception { + public void shouldSuccessfullyLoadXMLConfigFitle() throws Exception { + System.setProperty(XPathParser.KEY_USE_XSD, "true"); String resource = "org/apache/ibatis/builder/xsd/CustomizedSettingsMapperConfig.xml"; - InputStream inputStream = Resources.getResourceAsStream(resource); - Properties variables = new Properties(); - variables.setProperty(XPathParser.KEY_USE_XSD, "true"); - variables.put("prop2", "cccc"); - XMLConfigBuilder builder = new XMLConfigBuilder(inputStream, null, variables); - Configuration config = builder.parse(); - - assertThat(config.getAutoMappingBehavior(), is(AutoMappingBehavior.NONE)); - assertThat(config.getAutoMappingUnknownColumnBehavior(), is(AutoMappingUnknownColumnBehavior.WARNING)); - assertThat(config.isCacheEnabled(), is(false)); - assertThat(config.getProxyFactory(), is(instanceOf(CglibProxyFactory.class))); - assertThat(config.isLazyLoadingEnabled(), is(true)); - assertThat(config.isAggressiveLazyLoading(), is(true)); - assertThat(config.isMultipleResultSetsEnabled(), is(false)); - assertThat(config.isUseColumnLabel(), is(false)); - assertThat(config.isUseGeneratedKeys(), is(true)); - assertThat(config.getDefaultExecutorType(), is(ExecutorType.BATCH)); - assertThat(config.getDefaultStatementTimeout(), is(10)); - assertThat(config.getDefaultFetchSize(), is(100)); - assertThat(config.isMapUnderscoreToCamelCase(), is(true)); - assertThat(config.isSafeRowBoundsEnabled(), is(true)); - assertThat(config.getLocalCacheScope(), is(LocalCacheScope.STATEMENT)); - assertThat(config.getJdbcTypeForNull(), is(JdbcType.NULL)); - assertThat(config.getLazyLoadTriggerMethods(), is((Set) new HashSet(Arrays.asList("equals", "clone", "hashCode", "toString", "xxx")))); - assertThat(config.isSafeResultHandlerEnabled(), is(false)); - assertThat(config.getDefaultScriptingLanguageInstance(), is(instanceOf(RawLanguageDriver.class))); - 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())); - - assertTrue(config.getTypeAliasRegistry().getTypeAliases().get("blogauthor").equals(Author.class)); - assertTrue(config.getTypeAliasRegistry().getTypeAliases().get("blog").equals(Blog.class)); - assertTrue(config.getTypeAliasRegistry().getTypeAliases().get("cart").equals(Cart.class)); - - assertThat(config.getTypeHandlerRegistry().getTypeHandler(Integer.class), is(instanceOf(CustomIntegerTypeHandler.class))); - assertThat(config.getTypeHandlerRegistry().getTypeHandler(Long.class), is(instanceOf(CustomLongTypeHandler.class))); - assertThat(config.getTypeHandlerRegistry().getTypeHandler(String.class), is(instanceOf(CustomStringTypeHandler.class))); - assertThat(config.getTypeHandlerRegistry().getTypeHandler(String.class, JdbcType.VARCHAR), is(instanceOf(CustomStringTypeHandler.class))); - - ExampleObjectFactory objectFactory = (ExampleObjectFactory)config.getObjectFactory(); - assertThat(objectFactory.getProperties().size(), is(1)); - assertThat(objectFactory.getProperties().getProperty("objectFactoryProperty"), is("100")); - - assertThat(config.getObjectWrapperFactory(), is(instanceOf(CustomObjectWrapperFactory.class))); - - assertThat(config.getReflectorFactory(), is(instanceOf(CustomReflectorFactory.class))); - - ExamplePlugin plugin = (ExamplePlugin)config.getInterceptors().get(0); - assertThat(plugin.getProperties().size(), is(1)); - assertThat(plugin.getProperties().getProperty("pluginProperty"), is("100")); - - Environment environment = config.getEnvironment(); - assertThat(environment.getId(), is("development")); - assertThat(environment.getDataSource(), is(instanceOf(UnpooledDataSource.class))); - assertThat(environment.getTransactionFactory(), is(instanceOf(JdbcTransactionFactory.class))); - - assertThat(config.getDatabaseId(), is("derby")); - - assertThat(config.getMapperRegistry().getMappers().size(), is(4)); - assertThat(config.getMapperRegistry().hasMapper(CachedAuthorMapper.class), is(true)); - assertThat(config.getMapperRegistry().hasMapper(CustomMapper.class), is(true)); - assertThat(config.getMapperRegistry().hasMapper(BlogMapper.class), is(true)); - assertThat(config.getMapperRegistry().hasMapper(NestedBlogMapper.class), is(true)); - + try (InputStream inputStream = Resources.getResourceAsStream(resource)) { + XMLConfigBuilder builder = new XMLConfigBuilder(inputStream); + Configuration config = builder.parse(); + + assertThat(config.getAutoMappingBehavior(), is(AutoMappingBehavior.NONE)); + assertThat(config.getAutoMappingUnknownColumnBehavior(), is(AutoMappingUnknownColumnBehavior.WARNING)); + assertThat(config.isCacheEnabled(), is(false)); + assertThat(config.getProxyFactory(), is(instanceOf(CglibProxyFactory.class))); + assertThat(config.isLazyLoadingEnabled(), is(true)); + assertThat(config.isAggressiveLazyLoading(), is(true)); + assertThat(config.isMultipleResultSetsEnabled(), is(false)); + assertThat(config.isUseColumnLabel(), is(false)); + assertThat(config.isUseGeneratedKeys(), is(true)); + assertThat(config.getDefaultExecutorType(), is(ExecutorType.BATCH)); + assertThat(config.getDefaultStatementTimeout(), is(10)); + assertThat(config.getDefaultFetchSize(), is(100)); + assertThat(config.isMapUnderscoreToCamelCase(), is(true)); + assertThat(config.isSafeRowBoundsEnabled(), is(true)); + assertThat(config.getLocalCacheScope(), is(LocalCacheScope.STATEMENT)); + assertThat(config.getJdbcTypeForNull(), is(JdbcType.NULL)); + assertThat(config.getLazyLoadTriggerMethods(), is((Set) new HashSet(Arrays.asList("equals", "clone", "hashCode", "toString", "xxx")))); + assertThat(config.isSafeResultHandlerEnabled(), is(false)); + assertThat(config.getDefaultScriptingLanguageInstance(), is(instanceOf(RawLanguageDriver.class))); + 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())); + + assertTrue(config.getTypeAliasRegistry().getTypeAliases().get("blogauthor").equals(Author.class)); + assertTrue(config.getTypeAliasRegistry().getTypeAliases().get("blog").equals(Blog.class)); + assertTrue(config.getTypeAliasRegistry().getTypeAliases().get("cart").equals(Cart.class)); + + assertThat(config.getTypeHandlerRegistry().getTypeHandler(Integer.class), is(instanceOf(CustomIntegerTypeHandler.class))); + assertThat(config.getTypeHandlerRegistry().getTypeHandler(Long.class), is(instanceOf(CustomLongTypeHandler.class))); + assertThat(config.getTypeHandlerRegistry().getTypeHandler(String.class), is(instanceOf(CustomStringTypeHandler.class))); + assertThat(config.getTypeHandlerRegistry().getTypeHandler(String.class, JdbcType.VARCHAR), is(instanceOf(CustomStringTypeHandler.class))); + + ExampleObjectFactory objectFactory = (ExampleObjectFactory)config.getObjectFactory(); + assertThat(objectFactory.getProperties().size(), is(1)); + assertThat(objectFactory.getProperties().getProperty("objectFactoryProperty"), is("100")); + + assertThat(config.getObjectWrapperFactory(), is(instanceOf(CustomObjectWrapperFactory.class))); + + assertThat(config.getReflectorFactory(), is(instanceOf(CustomReflectorFactory.class))); + + ExamplePlugin plugin = (ExamplePlugin)config.getInterceptors().get(0); + assertThat(plugin.getProperties().size(), is(1)); + assertThat(plugin.getProperties().getProperty("pluginProperty"), is("100")); + + Environment environment = config.getEnvironment(); + assertThat(environment.getId(), is("development")); + assertThat(environment.getDataSource(), is(instanceOf(UnpooledDataSource.class))); + assertThat(environment.getTransactionFactory(), is(instanceOf(JdbcTransactionFactory.class))); + + assertThat(config.getDatabaseId(), is("derby")); + + assertThat(config.getMapperRegistry().getMappers().size(), is(4)); + assertThat(config.getMapperRegistry().hasMapper(CachedAuthorMapper.class), is(true)); + assertThat(config.getMapperRegistry().hasMapper(CustomMapper.class), is(true)); + assertThat(config.getMapperRegistry().hasMapper(BlogMapper.class), is(true)); + assertThat(config.getMapperRegistry().hasMapper(NestedBlogMapper.class), is(true)); + } } - } diff --git a/src/test/java/org/apache/ibatis/builder/xsd/XmlMapperBuilderTest.java b/src/test/java/org/apache/ibatis/builder/xsd/XmlMapperBuilderTest.java index a83cd9be4eb..5dafa15d764 100644 --- a/src/test/java/org/apache/ibatis/builder/xsd/XmlMapperBuilderTest.java +++ b/src/test/java/org/apache/ibatis/builder/xsd/XmlMapperBuilderTest.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 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. @@ -25,7 +25,6 @@ import org.junit.Test; import java.io.InputStream; -import java.util.Properties; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.assertThat; @@ -34,23 +33,21 @@ public class XmlMapperBuilderTest { @Test public void mappedStatementWithOptions() throws Exception { + System.setProperty(XPathParser.KEY_USE_XSD, "true"); Configuration configuration = new Configuration(); - Properties variables = new Properties(); - variables.setProperty(XPathParser.KEY_USE_XSD, "true"); - configuration.setVariables(variables); String resource = "org/apache/ibatis/builder/xsd/AuthorMapper.xml"; - InputStream inputStream = Resources.getResourceAsStream(resource); - XMLMapperBuilder builder = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); - builder.parse(); - - MappedStatement mappedStatement = configuration.getMappedStatement("selectWithOptions"); - assertThat(mappedStatement.getFetchSize(), is(200)); - assertThat(mappedStatement.getTimeout(), is(10)); - assertThat(mappedStatement.getStatementType(), is(StatementType.PREPARED)); - assertThat(mappedStatement.getResultSetType(), is(ResultSetType.SCROLL_SENSITIVE)); - assertThat(mappedStatement.isFlushCacheRequired(), is(false)); - assertThat(mappedStatement.isUseCache(), is(false)); - + try (InputStream inputStream = Resources.getResourceAsStream(resource)) { + XMLMapperBuilder builder = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); + builder.parse(); + + MappedStatement mappedStatement = configuration.getMappedStatement("selectWithOptions"); + assertThat(mappedStatement.getFetchSize(), is(200)); + assertThat(mappedStatement.getTimeout(), is(10)); + assertThat(mappedStatement.getStatementType(), is(StatementType.PREPARED)); + assertThat(mappedStatement.getResultSetType(), is(ResultSetType.SCROLL_SENSITIVE)); + assertThat(mappedStatement.isFlushCacheRequired(), is(false)); + assertThat(mappedStatement.isUseCache(), is(false)); + } } } From 7dc07cc87e5a7fb71c4a305b6ba53298cece1a79 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Tue, 4 Dec 2018 01:41:02 +0900 Subject: [PATCH 0211/2062] Clear the system property at the end of each test. --- .../org/apache/ibatis/builder/xsd/XmlConfigBuilderTest.java | 4 ++++ .../org/apache/ibatis/builder/xsd/XmlMapperBuilderTest.java | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/test/java/org/apache/ibatis/builder/xsd/XmlConfigBuilderTest.java b/src/test/java/org/apache/ibatis/builder/xsd/XmlConfigBuilderTest.java index 20cfc29da98..efe6c4adb44 100644 --- a/src/test/java/org/apache/ibatis/builder/xsd/XmlConfigBuilderTest.java +++ b/src/test/java/org/apache/ibatis/builder/xsd/XmlConfigBuilderTest.java @@ -91,6 +91,8 @@ public void shouldSuccessfullyLoadMinimalXMLConfigFile() throws Exception { assertNull(config.getLogPrefix()); assertNull(config.getLogImpl()); assertNull(config.getConfigurationFactory()); + } finally { + System.clearProperty(XPathParser.KEY_USE_XSD); } } @@ -160,6 +162,8 @@ public void shouldSuccessfullyLoadXMLConfigFitle() throws Exception { assertThat(config.getMapperRegistry().hasMapper(CustomMapper.class), is(true)); assertThat(config.getMapperRegistry().hasMapper(BlogMapper.class), is(true)); assertThat(config.getMapperRegistry().hasMapper(NestedBlogMapper.class), is(true)); + } finally { + System.clearProperty(XPathParser.KEY_USE_XSD); } } diff --git a/src/test/java/org/apache/ibatis/builder/xsd/XmlMapperBuilderTest.java b/src/test/java/org/apache/ibatis/builder/xsd/XmlMapperBuilderTest.java index 5dafa15d764..4094574ba7d 100644 --- a/src/test/java/org/apache/ibatis/builder/xsd/XmlMapperBuilderTest.java +++ b/src/test/java/org/apache/ibatis/builder/xsd/XmlMapperBuilderTest.java @@ -47,6 +47,8 @@ public void mappedStatementWithOptions() throws Exception { assertThat(mappedStatement.getResultSetType(), is(ResultSetType.SCROLL_SENSITIVE)); assertThat(mappedStatement.isFlushCacheRequired(), is(false)); assertThat(mappedStatement.isUseCache(), is(false)); + } finally { + System.clearProperty(XPathParser.KEY_USE_XSD); } } From b2e809100a28b33479f23bf33cdde08bb85d3e87 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Mon, 10 Dec 2018 02:28:12 +0900 Subject: [PATCH 0212/2062] Fix log initialization timing on some class Fixes gh-1272 --- .../apache/ibatis/builder/xml/XMLConfigBuilder.java | 11 +++++++---- .../executor/loader/cglib/CglibProxyFactory.java | 12 ++++++++---- .../loader/javassist/JavassistProxyFactory.java | 12 ++++++++---- .../ibatis/mapping/VendorDatabaseIdProvider.java | 12 +++++++----- .../session/AutoMappingUnknownColumnBehavior.java | 13 ++++++------- 5 files changed, 36 insertions(+), 24 deletions(-) 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 2d7dff49164..32ce5ea3667 100644 --- a/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 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. @@ -106,6 +106,7 @@ private void parseConfiguration(XNode root) { propertiesElement(root.evalNode("*[local-name()='properties']")); Properties settings = settingsAsProperties(root.evalNode("*[local-name()='settings']")); loadCustomVfs(settings); + loadCustomLogImpl(settings); typeAliasesElement(root.evalNode("*[local-name()='typeAliases']")); pluginElement(root.evalNode("*[local-name()='plugins']")); objectFactoryElement(root.evalNode("*[local-name()='objectFactory']")); @@ -151,6 +152,11 @@ private void loadCustomVfs(Properties props) throws ClassNotFoundException { } } + private void loadCustomLogImpl(Properties props) { + Class logImpl = resolveClass(props.getProperty("logImpl")); + configuration.setLogImpl(logImpl); + } + private void typeAliasesElement(XNode parent) { if (parent != null) { for (XNode child : parent.getChildren()) { @@ -262,9 +268,6 @@ private void settingsElement(Properties props) throws Exception { configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true)); configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false)); configuration.setLogPrefix(props.getProperty("logPrefix")); - @SuppressWarnings("unchecked") - Class logImpl = (Class)resolveClass(props.getProperty("logImpl")); - configuration.setLogImpl(logImpl); configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory"))); } diff --git a/src/main/java/org/apache/ibatis/executor/loader/cglib/CglibProxyFactory.java b/src/main/java/org/apache/ibatis/executor/loader/cglib/CglibProxyFactory.java index 7548c6b598f..5a46f0565dd 100644 --- a/src/main/java/org/apache/ibatis/executor/loader/cglib/CglibProxyFactory.java +++ b/src/main/java/org/apache/ibatis/executor/loader/cglib/CglibProxyFactory.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 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. @@ -45,7 +45,6 @@ */ public class CglibProxyFactory implements ProxyFactory { - private static final Log log = LogFactory.getLog(CglibProxyFactory.class); private static final String FINALIZE_METHOD = "finalize"; private static final String WRITE_REPLACE_METHOD = "writeReplace"; @@ -78,8 +77,8 @@ static Object crateProxy(Class type, Callback callback, List> constr try { type.getDeclaredMethod(WRITE_REPLACE_METHOD); // ObjectOutputStream will call writeReplace of objects returned by writeReplace - if (log.isDebugEnabled()) { - log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this"); + if (LogHolder.log.isDebugEnabled()) { + LogHolder.log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this"); } } catch (NoSuchMethodException e) { enhancer.setInterfaces(new Class[]{WriteReplaceInterface.class}); @@ -194,4 +193,9 @@ protected AbstractSerialStateHolder newSerialStateHolder(Object userBean, Map type, MethodHandler callback, List> c try { type.getDeclaredMethod(WRITE_REPLACE_METHOD); // ObjectOutputStream will call writeReplace of objects returned by writeReplace - if (log.isDebugEnabled()) { - log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this"); + if (LogHolder.log.isDebugEnabled()) { + LogHolder.log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this"); } } catch (NoSuchMethodException e) { enhancer.setInterfaces(new Class[]{WriteReplaceInterface.class}); @@ -196,4 +195,9 @@ protected AbstractSerialStateHolder newSerialStateHolder(Object userBean, Map propertyType) { - log.warn(buildMessage(mappedStatement, columnName, property, propertyType)); + LogHolder.log.warn(buildMessage(mappedStatement, columnName, property, propertyType)); } }, @@ -59,11 +59,6 @@ public void doAction(MappedStatement mappedStatement, String columnName, String } }; - /** - * Logger - */ - private static final Log log = LogFactory.getLog(AutoMappingUnknownColumnBehavior.class); - /** * Perform the action when detects an unknown column (or unknown property type) of automatic mapping target. * @param mappedStatement current mapped statement @@ -88,4 +83,8 @@ private static String buildMessage(MappedStatement mappedStatement, String colum .toString(); } + private static class LogHolder { + private static final Log log = LogFactory.getLog(AutoMappingUnknownColumnBehavior.class); + } + } From c6f8ffb39510cbd1266ff4c59926a8ff0a58d124 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Mon, 10 Dec 2018 03:17:20 +0900 Subject: [PATCH 0213/2062] Added 'columnPrefix' Apply #1318 --- .../java/org/apache/ibatis/builder/xml/mybatis-mapper.xsd | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/apache/ibatis/builder/xml/mybatis-mapper.xsd b/src/main/java/org/apache/ibatis/builder/xml/mybatis-mapper.xsd index cb91decc24e..333c8c2334a 100644 --- a/src/main/java/org/apache/ibatis/builder/xml/mybatis-mapper.xsd +++ b/src/main/java/org/apache/ibatis/builder/xml/mybatis-mapper.xsd @@ -1,7 +1,7 @@ @@ -796,7 +826,7 @@ public class User { ]]>

        - Estos son los ResultMaps más sencillos. Ambos id, y result mapean una columna con una propiedad o campo de un tipo de dato simple (String, int, double, Date, etc.). + Estos son los ResultMaps más sencillos. Ambos id, y result mapean una columna con una propiedad o campo de un tipo de dato simple (String, int, double, Date, etc.).

        @@ -1120,7 +1150,7 @@ public class User {

    - @@ -1137,7 +1167,7 @@ public class User { Con este atributo se puede modificar este comportamiento especificando qué columnas deben tener un valor de forma que MyBatis sólo creará un objeto hijo si alguna de estas columnas no es nula. Pueden indicarse una lista de columnas usando la coma como separador. - + @@ -1287,13 +1317,13 @@ public class User {
    - VARCHAR-任何兼容的字符串类型,存储枚举的名称(而不是索引) + VARCHAR 或任何兼容的字符串类型,用以存储枚举的名称(而不是索引值)
    - 任何兼容的 NUMERICDOUBLE 类型,存储枚举的索引(而不是名称)。 + 任何兼容的 NUMERICDOUBLE 类型,存储枚举的序数值(而不是名称)。
    - VARCHAR or LONGVARCHAR + VARCHARLONGVARCHAR
    resultMapEl id de un resultmap que puede mapear los resultados anidados de esta asociación al grafo de objetos apropiado. Es una alternativa a llamar a otro select statement. Permite hacer join de varias tablas en un solo ResultSet. Un ResultSet de este tipo puede contener bloques repetidos de datos que deben ser descompuestos y mapeados apropiadamente a un árbol de objetos (object graph). MyBatis te permite encadenar RestultMaps para tratar resultados anidados. A continuación se muestra un ejemplo detallado. + El id de un resultmap que puede mapear los resultados anidados de esta asociación al grafo de objetos apropiado. Es una alternativa a llamar a otro select statement. Permite hacer join de varias tablas en un solo ResultSet. Un ResultSet de este tipo puede contener bloques repetidos de datos que deben ser descompuestos y mapeados apropiadamente a un árbol de objetos (object graph). MyBatis te permite encadenar RestultMaps para tratar resultados anidados. A continuación se muestra un ejemplo detallado.
    autoMapping If present, MyBatis will enable or disable automapping when mapping the result to this property. @@ -1268,7 +1298,7 @@ public class User {
    column - Cuando se usan result sets multiples este atributo especifica las columnas (separadas por comas) que se + Cuando se usan result sets multiples este atributo especifica las columnas (separadas por comas) que se correlarán con las indicadas en foreignColumn para identificar al padre y e hijo de una relación.
    - +

    Desce la versión 3.2.3 MyBatis proporciona otra forma más de resolver el problema del N+1.

    - -

    Algunas bases de datos permiten que un procedimiento almacenado devuelva más de un resultset o + +

    Algunas bases de datos permiten que un procedimiento almacenado devuelva más de un resultset o ejecutar más de una sentencia a la vez y obtener de vuelta un resultset por cada. Esto se puede usar para acceder una sola vez a la base de datos y obtener datos relacionados sin usar una join.

    - +

    En el ejemplo, el procedimiento almacenado deolverá dos result sets. El primero contendrá Blogs y el segundo Authors.

    @@ -1301,7 +1331,7 @@ public class User { SELECT * FROM AUTHOR WHERE ID = #{id}]]> -

    Se debe proporcionar un nombre a cada resultset informando el atributo +

    Se debe proporcionar un nombre a cada resultset informando el atributo resultSets del mapped statement con una lista de nombres separados por comas.

    @@ -1429,9 +1459,9 @@ SELECT * FROM AUTHOR WHERE ID = #{id}]]> ]]>

    ResultSets múltiples en Collection

    - +

    - De la misma forma que hicimos en la association, podemos llamar a un procedimiento almacenado que devuelva + De la misma forma que hicimos en la association, podemos llamar a un procedimiento almacenado que devuelva dos resultsets, uno con Blogs y otro con Posts:

    @@ -1439,13 +1469,13 @@ SELECT * FROM AUTHOR WHERE ID = #{id}]]> SELECT * FROM POST WHERE BLOG_ID = #{id}]]> -

    Se debe proporcionar un nombre a cada resultset informando el atributo +

    Se debe proporcionar un nombre a cada resultset informando el atributo resultSets del mapped statement con una lista de nombres separados por comas.

    {call getBlogsAndPosts(#{id,jdbcType=INTEGER,mode=IN})} ]]> - +

    Especificamos que la collection "posts" se rellenará con datos contenidos en el resultset llamado "posts":

    @@ -1545,15 +1575,15 @@ SELECT * FROM POST WHERE BLOG_ID = #{id}]]>

    - Como ya has visto en las secciones previas, en los casos simples MyBatis puede auto-mapear los resultados por ti y en el resto de los casos - es posible que tengas que crear un result map. Pero, como verás en esta sección también puedes combinar ambas estrategias. - Veamos en detalle cómo funciona el auto-mapeo. + Como ya has visto en las secciones previas, en los casos simples MyBatis puede auto-mapear los resultados por ti y en el resto de los casos + es posible que tengas que crear un result map. Pero, como verás en esta sección también puedes combinar ambas estrategias. + Veamos en detalle cómo funciona el auto-mapeo.

    - Al auto-mapear resultados MyBatis obtiene el nombre de columna y busca una propiedad con el mismo nombre sin tener en cuenta las mayúsculas. - Es decir, si se encuentra una columna ID y una propiedad id, MyBatis informará la propiedad id con el valor de la columna - ID. + Al auto-mapear resultados MyBatis obtiene el nombre de columna y busca una propiedad con el mismo nombre sin tener en cuenta las mayúsculas. + Es decir, si se encuentra una columna ID y una propiedad id, MyBatis informará la propiedad id con el valor de la columna + ID.

    @@ -1561,9 +1591,9 @@ SELECT * FROM POST WHERE BLOG_ID = #{id}]]> java se nombran habitualmente siguiendo la notación tipo camelcase. Para habilitar el auto-mapeo entre ellas informa el parámetro de configuración mapUnderscoreToCamelCase a true.

    - +

    - El auto-mapeo funciona incluso cuando hay un result map específico. Cuando esto sucede, para cada result map, todas las columnas que están + El auto-mapeo funciona incluso cuando hay un result map específico. Cuando esto sucede, para cada result map, todas las columnas que están presentes en el ResultSet y que no tienen un mapeo manual se auto-mapearán. Posteriormente se procesarán los mapeos manuales. En el siguiente ejemplo las columnas id y userName se auto-mapearán y la columna hashed_password se mapeará manualmente.

    @@ -1583,7 +1613,7 @@ SELECT * FROM POST WHERE BLOG_ID = #{id}]]>

    Hay tres niveles de auto-mapeo:

    - +
    • NONE - desactiva el auto-mapeo. Solo las propiedades mapeadas manaulmente se informarán. @@ -1597,11 +1627,11 @@ SELECT * FROM POST WHERE BLOG_ID = #{id}]]>

    - El valor por defecto es PARTIAL, y hay una razón para ello. Cuandos se utiliza FULL el auto-mapeo se - realiza cuando se están procesando resultados de joins y las joins obtienen datos de distintas entidades en la misma fila + El valor por defecto es PARTIAL, y hay una razón para ello. Cuandos se utiliza FULL el auto-mapeo se + realiza cuando se están procesando resultados de joins y las joins obtienen datos de distintas entidades en la misma fila por lo tanto podrían producirse mapeos automáticos indeseados. Para comprender el riesgo observa el siguiente ejemplo:

    - + select B.id, @@ -1618,7 +1648,7 @@ SELECT * FROM POST WHERE BLOG_ID = #{id}]]> ]]> - +

    Con este result map ambos Blog y Author se auto-mapearán. Pero fíjate que Author tiene un id y que hay una columna con nombre id en el ResultSet por lo que el id de Author se rellenará con el id de Blog, y eso no era lo que esperabas. @@ -1633,15 +1663,15 @@ SELECT * FROM POST WHERE BLOG_ID = #{id}]]> ]]> - - + +

    MyBatis incluye una funcionalidad de caché transaccional de segundo nivel muy potente que es ampliamente configurable y personalizable. Se han realizado muchos cambios en la caché de MyBatis 3 para hacer la a la vez más potente y más sencilla de configurar.

    - Por defecto la única caché activa es la caché local de sesión que se utiliza únicamente durante la duración de una sesión. + Por defecto la única caché activa es la caché local de sesión que se utiliza únicamente durante la duración de una sesión. Para habilitar la caché de segundo nivel global simplemente necesitas añadir una línea en tu fichero de mapping:

    @@ -1706,14 +1736,14 @@ SELECT * FROM POST WHERE BLOG_ID = #{id}]]>

    - NOTA La caché de segundo nivel es transaccional. Esto significa que solo es actualizada cuando - una sessión acaba con commit o cuando acaba con rollback pero no se ha ejecutado ninguna sentencia insert/delete/update + NOTA La caché de segundo nivel es transaccional. Esto significa que solo es actualizada cuando + una sessión acaba con commit o cuando acaba con rollback pero no se ha ejecutado ninguna sentencia insert/delete/update con el parámetro flushCache=true.

    Como usar una caché personalizada

    -

    Además de poder personalizar la caché de las formas indicadas, puedes sustituir el sistema de caché por completo y proporcionar tu propia caché, o crear un adaptador para cachés de terceros. +

    Además de poder personalizar la caché de las formas indicadas, puedes sustituir el sistema de caché por completo y proporcionar tu propia caché, o crear un adaptador para cachés de terceros.

    ]]> @@ -1756,7 +1786,7 @@ SELECT * FROM POST WHERE BLOG_ID = #{id}]]>

    NOTE Los parametros de configuración de la cache (eviction, read write..etc.) explicados anteriormente no aplican cuando se usa una caché personalizada. -

    +

    Es importante recordar que la configuración de la caches y la instancia de caché está asociadas al namespace del fichero SQL Map. Y por tanto, a todas las sentencias del mismo namespace dado que la cache está asociada a él. Los statements pueden modificar cómo interactúan con la caché, o excluirse a sí mismos completamente utilizando dos atributos simples. Por defecto los statements están configurados así:

    diff --git a/src/site/ja/xdoc/sqlmap-xml.xml b/src/site/ja/xdoc/sqlmap-xml.xml index fa581838b08..1dbd133e859 100644 --- a/src/site/ja/xdoc/sqlmap-xml.xml +++ b/src/site/ja/xdoc/sqlmap-xml.xml @@ -598,6 +598,36 @@ ps.setInt(1,id);]]> この場合、MyBatis は引数として渡された文字列を変更したりエスケープしたりしません。

    +

    + String Substitution can be very useful when the metadata(i.e. table name or column name) in the sql statement is dynamic, + for example, if you want to select from a table by any one of its columns, instead of writing code like: + + you can just write: + + in which the ${column} will be substituted directly and the #{value} will be "prepared". + Thus you can just do the same work by: + + This idea can be applied to substitute the table name as well. +

    +

    重要 この方法を使って、ユーザーが入力した文字列を直接 SQL 文に代入するのは危険です。SQL インジェクションの原因となる可能性がありますので、ユーザーによる入力を受け付けないようにしておくか、常にプログラム側で独自にエスケープやチェックの処理を行うようにしてください。

    @@ -632,7 +662,7 @@ public class User { private int id; private String username; private String hashedPassword; - + public int getId() { return id; } @@ -1318,7 +1348,7 @@ public class User {
  • ネストされた結果をマッピングする際、デフォルトでは子オブジェクトのプロパティに対応する列のうち一つでも null でない値がセットされているものがあれば子オブジェクトが生成されます。notNullColumn に列名(複数可)を指定しておくと、指定された列に null 以外の値がセットされた場合にのみ子オブジェクトが生成されます。デフォルト:未指定
    autoMapping このプロパティに結果をマッピングする際、自動マッピングを使用するかどうかを true / false で指定します。ここでの指定はグローバルな設定(autoMappingBehavior)より優先されます。この設定は別の場所で定義されている ResultMap には適用されませんので、selectresultMap が指定されている場合は無効となります。デフォルト:未指定 @@ -1467,9 +1497,9 @@ public class User {
    - +

    バージョン 3.2.3 から、N+1 セレクト問題を解決する新しい方法が追加されました。

    - +

    データベースによってはストアドプロシージャから複数の ResultSet を返すことができます。この機能を利用すると、テーブルの結合(Join)を使わず一度の問い合わせで互いに関連する複数のデータを取得することができます。

    以下の例では、ストアドプロシージャが2つの ResultSet を返します。1つめの ResultSet には複数の Blog のデータが含まれており、2つめの ResultSet には複数の Author のデータが含まれています。

    @@ -1621,7 +1651,7 @@ SELECT * FROM AUTHOR WHERE ID = #{id}]]> ]]>

    複数の ResultSets を collection にマッピングする

    - +

    association で説明したのと同様に、2つの ResultSet を返すストアドプロシージャを呼び出すことができます。1つ目の ResultSet には Blog のリスト、2つ目の ResultSet には Post のリストが含まれているものとします。

    @@ -1635,7 +1665,7 @@ SELECT * FROM POST WHERE BLOG_ID = #{id}]]> {call getBlogsAndPosts(#{id,jdbcType=INTEGER,mode=IN})} ]]> - +

    以下の resultMap では、posts に含まれる Post が、対応する Blog にマッピングされます。

    @@ -1777,7 +1807,7 @@ SELECT * FROM POST WHERE BLOG_ID = #{id}]]> 通常、データベースの列名は英数字と単語を区切るアンダースコアで定義され、Java のプロパティはキャメルケースで定義されるのが一般的です。 mapUnderscoreToCamelCase に true に設定すると、この一般的な命名規則に基づいて自動マッピングを適用することができます。

    - +

    Result Map が指定されている場合でも自動マッピングは動作します。この場合、ResultSet に含まれる列のうち、各 Result Map で明示的にマッピングが指定されていない列が自動マッピングの対象となります。 次の例では、hashed_password 列が password プロパティにマップされ、iduserName 列が自動マッピングの対象となります。 @@ -1799,7 +1829,7 @@ SELECT * FROM POST WHERE BLOG_ID = #{id}]]>

    自動マッピングには3つのレベルがあります。

    - +