From d570a023e6e024e9c30ab13dee2532eb15681588 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Mon, 19 Sep 2022 05:26:32 +0900 Subject: [PATCH 001/382] [maven-release-plugin] prepare for next development iteration --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 376742575fc..f820b21ebc4 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ mybatis - 3.5.11 + 3.5.12-SNAPSHOT jar mybatis @@ -112,7 +112,7 @@ https://github.com/mybatis/mybatis-3 scm:git:ssh://github.com/mybatis/mybatis-3.git scm:git:ssh://git@github.com/mybatis/mybatis-3.git - mybatis-3.5.11 + HEAD GitHub Issue Management @@ -142,7 +142,7 @@ -Djdk.attach.allowAttachSelf -Xmx2048m - 1663532582 + 1663532792 From a2c241af3588189a9066b47061fa8c886f6383eb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 20 Sep 2022 20:21:51 +0000 Subject: [PATCH 002/382] Update dependency org.slf4j:slf4j-api to v2.0.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f820b21ebc4..f4420308435 100644 --- a/pom.xml +++ b/pom.xml @@ -163,7 +163,7 @@ org.slf4j slf4j-api - 2.0.1 + 2.0.2 true From 422eba1667b689400fbe42fc20280aeff67cad95 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 20 Sep 2022 20:21:57 +0000 Subject: [PATCH 003/382] Update junit5 monorepo to v5.9.1 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f820b21ebc4..fdbc3022fc0 100644 --- a/pom.xml +++ b/pom.xml @@ -195,13 +195,13 @@ org.junit.jupiter junit-jupiter-engine - 5.9.0 + 5.9.1 test org.junit.jupiter junit-jupiter-params - 5.9.0 + 5.9.1 test From cae4537dd9f51ed23639ca267108efef7342ed05 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Sat, 24 Sep 2022 02:51:45 +0900 Subject: [PATCH 004/382] Referencing collection parameter by name fails ... when the first argument is a special one (i.e. `RowBounds` or `ResultHandler`). fixes #2693 --- .../ibatis/reflection/ParamNameResolver.java | 2 +- .../param_name_resolve/ActualParamNameTest.java | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/apache/ibatis/reflection/ParamNameResolver.java b/src/main/java/org/apache/ibatis/reflection/ParamNameResolver.java index f6b993a7507..e47c51ca218 100644 --- a/src/main/java/org/apache/ibatis/reflection/ParamNameResolver.java +++ b/src/main/java/org/apache/ibatis/reflection/ParamNameResolver.java @@ -125,7 +125,7 @@ public Object getNamedParams(Object[] args) { return null; } else if (!hasParamAnnotation && paramCount == 1) { Object value = args[names.firstKey()]; - return wrapToMapIfCollection(value, useActualParamName ? names.get(0) : null); + return wrapToMapIfCollection(value, useActualParamName ? names.get(names.firstKey()) : null); } else { final Map param = new ParamMap<>(); int i = 0; diff --git a/src/test/java/org/apache/ibatis/submitted/param_name_resolve/ActualParamNameTest.java b/src/test/java/org/apache/ibatis/submitted/param_name_resolve/ActualParamNameTest.java index 570aea64449..8d00bcf1955 100644 --- a/src/test/java/org/apache/ibatis/submitted/param_name_resolve/ActualParamNameTest.java +++ b/src/test/java/org/apache/ibatis/submitted/param_name_resolve/ActualParamNameTest.java @@ -25,6 +25,7 @@ import org.apache.ibatis.annotations.Select; import org.apache.ibatis.io.Resources; import org.apache.ibatis.jdbc.ScriptRunner; +import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; @@ -72,6 +73,11 @@ void testSingleListParameterWhenUseActualParamNameIsTrue() { long count = mapper.getUserCountUsingListWithAliasIsList(Arrays.asList(1, 2)); assertEquals(2, count); } + // use actual name #2693 + { + long count = mapper.getUserCountUsingList_RowBounds(new RowBounds(0, 5), Arrays.asList(1, 2)); + assertEquals(2, count); + } } } @@ -142,6 +148,16 @@ interface Mapper { "" }) Long getUserCountUsingArrayWithAliasArray(Integer... ids); + + @Select({ + "" + }) + Long getUserCountUsingList_RowBounds(RowBounds rowBounds, List ids); } } From 62bf4857baee49d70f8e9a2bf95b5b2ffc16b462 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 28 Sep 2022 11:57:47 +0000 Subject: [PATCH 005/382] Update dependency org.slf4j:slf4j-api to v2.0.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ac9f6421b9f..2633b8f74d4 100644 --- a/pom.xml +++ b/pom.xml @@ -163,7 +163,7 @@ org.slf4j slf4j-api - 2.0.2 + 2.0.3 true From 82b35d7fe8a248d6fe6a222ff0176abedb6abc2a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 19:17:57 +0000 Subject: [PATCH 006/382] Update dependency org.testcontainers:junit-jupiter to v1.17.4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2633b8f74d4..38435f6df2a 100644 --- a/pom.xml +++ b/pom.xml @@ -280,7 +280,7 @@ org.testcontainers junit-jupiter - 1.17.3 + 1.17.4 test From 9aa02511c017f7697194799b48350c2629674f42 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 19:18:03 +0000 Subject: [PATCH 007/382] Update dependency org.testcontainers:mysql to v1.17.4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2633b8f74d4..d94d636d69a 100644 --- a/pom.xml +++ b/pom.xml @@ -292,7 +292,7 @@ org.testcontainers mysql - 1.17.3 + 1.17.4 test From 2287c3a1427c9737822faa66b2eeff52f39e8303 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:44:30 +0000 Subject: [PATCH 008/382] Update dependency org.testcontainers:postgresql to v1.17.4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2633b8f74d4..f4d3045c45c 100644 --- a/pom.xml +++ b/pom.xml @@ -286,7 +286,7 @@ org.testcontainers postgresql - 1.17.3 + 1.17.4 test From d64edc518968409da337ad65120a9c486570b854 Mon Sep 17 00:00:00 2001 From: tianshuang Date: Sat, 1 Oct 2022 21:00:54 +0800 Subject: [PATCH 009/382] Fix a race condition caused by other threads calling mapper methods while mappedStatements are being constructed --- .../org/apache/ibatis/session/Configuration.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/apache/ibatis/session/Configuration.java b/src/main/java/org/apache/ibatis/session/Configuration.java index 3c257379e8b..82f0f90b263 100644 --- a/src/main/java/org/apache/ibatis/session/Configuration.java +++ b/src/main/java/org/apache/ibatis/session/Configuration.java @@ -25,6 +25,7 @@ import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiFunction; import org.apache.ibatis.binding.MapperRegistry; @@ -998,7 +999,7 @@ protected void checkLocallyForDiscriminatedNestedResultMaps(ResultMap rm) { } } - protected static class StrictMap extends HashMap { + protected static class StrictMap extends ConcurrentHashMap { private static final long serialVersionUID = -4950446264854982944L; private final String name; @@ -1055,6 +1056,15 @@ public V put(String key, V value) { return super.put(key, value); } + @Override + public boolean containsKey(Object key) { + if (key == null) { + return false; + } + + return super.get(key) != null; + } + @Override public V get(Object key) { V value = super.get(key); From d9e3f66a4691510a871df79c66b0adaa7183c954 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 2 Oct 2022 21:16:20 +0000 Subject: [PATCH 010/382] Update dependency ch.qos.logback:logback-classic to v1.4.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9012b3908a3..945db42208f 100644 --- a/pom.xml +++ b/pom.xml @@ -305,7 +305,7 @@ ch.qos.logback logback-classic - 1.4.1 + 1.4.2 test From 8a53d36fba250462b7832a5fd117f8335a5ff41e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 2 Oct 2022 23:09:04 +0000 Subject: [PATCH 011/382] Update dependency ch.qos.logback:logback-classic to v1.4.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 945db42208f..7162f31aa32 100644 --- a/pom.xml +++ b/pom.xml @@ -305,7 +305,7 @@ ch.qos.logback logback-classic - 1.4.2 + 1.4.3 test From 40809ef787cf12b34e34d05a058cc1f9ce697909 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 4 Oct 2022 17:39:03 +0000 Subject: [PATCH 012/382] Update dependency org.testcontainers:junit-jupiter to v1.17.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7162f31aa32..5937a574fc4 100644 --- a/pom.xml +++ b/pom.xml @@ -280,7 +280,7 @@ org.testcontainers junit-jupiter - 1.17.4 + 1.17.5 test From 679823adf8972cd302b806c040c40b6e056a1c3a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 4 Oct 2022 17:39:10 +0000 Subject: [PATCH 013/382] Update dependency org.testcontainers:mysql to v1.17.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7162f31aa32..b001fdd8ba1 100644 --- a/pom.xml +++ b/pom.xml @@ -292,7 +292,7 @@ org.testcontainers mysql - 1.17.4 + 1.17.5 test From 2290e593a26d6aae7f47dc3ad7d5ac69fe521a39 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 4 Oct 2022 21:38:49 +0000 Subject: [PATCH 014/382] Update dependency org.testcontainers:postgresql to v1.17.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7162f31aa32..c056b85eccf 100644 --- a/pom.xml +++ b/pom.xml @@ -286,7 +286,7 @@ org.testcontainers postgresql - 1.17.4 + 1.17.5 test From 09294118a37a8d8148087a27be1a49153ba306a4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 10 Oct 2022 09:24:17 +0000 Subject: [PATCH 015/382] Update dependency ch.qos.logback:logback-classic to v1.4.4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 79146d150ea..00299f0cfa9 100644 --- a/pom.xml +++ b/pom.xml @@ -305,7 +305,7 @@ ch.qos.logback logback-classic - 1.4.3 + 1.4.4 test From ea2a2a1eaa2248f4001e70106d5c9c6f840df3f0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 17 Oct 2022 21:28:28 +0000 Subject: [PATCH 016/382] Update dependency org.mockito:mockito-junit-jupiter to v4.8.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 00299f0cfa9..2068ffd317f 100644 --- a/pom.xml +++ b/pom.xml @@ -243,7 +243,7 @@ org.mockito mockito-junit-jupiter - 4.8.0 + 4.8.1 test From c025110f791fd40ea1a94dfd54326ec615aeaa5a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 19 Oct 2022 11:42:26 +0000 Subject: [PATCH 017/382] Update dependency mysql:mysql-connector-java to v8.0.31 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 00299f0cfa9..c669c5b0f98 100644 --- a/pom.xml +++ b/pom.xml @@ -262,7 +262,7 @@ mysql mysql-connector-java - 8.0.30 + 8.0.31 test From e7f484456e640c98604e28afc231ee6b51a4644f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 21 Oct 2022 20:48:07 +0000 Subject: [PATCH 018/382] Update dependency org.mockito:mockito-core to v4.8.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bdfc019f2ef..db0c7e35b8d 100644 --- a/pom.xml +++ b/pom.xml @@ -237,7 +237,7 @@ org.mockito mockito-core - 4.8.0 + 4.8.1 test From 5fd03c399a00185eccf3d28a6ae7a8b51fdfc7c2 Mon Sep 17 00:00:00 2001 From: Willie Scholtz Date: Thu, 27 Oct 2022 13:06:53 +0200 Subject: [PATCH 019/382] Enable ability to provide custom configuration to XMLConfigBuilder Supported via the InputStream/Reader constructors Default constructors will keep using a new configuration --- .../ibatis/builder/xml/XMLConfigBuilder.java | 16 ++++++++++++---- .../ibatis/builder/XmlConfigBuilderTest.java | 19 ++++++++++++++++++- 2 files changed, 30 insertions(+), 5 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 131600e5581..72980d3d6f2 100644 --- a/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java @@ -67,7 +67,11 @@ public XMLConfigBuilder(Reader reader, String environment) { } public XMLConfigBuilder(Reader reader, String environment, Properties props) { - this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props); + this(new Configuration(), reader, environment, props); + } + + public XMLConfigBuilder(Configuration configuration, Reader reader, String environment, Properties props) { + this(configuration, new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props); } public XMLConfigBuilder(InputStream inputStream) { @@ -79,11 +83,15 @@ public XMLConfigBuilder(InputStream inputStream, String environment) { } public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) { - this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props); + this(new Configuration(), inputStream, environment, props); + } + + public XMLConfigBuilder(Configuration configuration, InputStream inputStream, String environment, Properties props) { + this(configuration, new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props); } - private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { - super(new Configuration()); + private XMLConfigBuilder(Configuration configuration, XPathParser parser, String environment, Properties props) { + super(configuration); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); this.parsed = false; diff --git a/src/test/java/org/apache/ibatis/builder/XmlConfigBuilderTest.java b/src/test/java/org/apache/ibatis/builder/XmlConfigBuilderTest.java index fd9895af5a4..3cc472194bd 100644 --- a/src/test/java/org/apache/ibatis/builder/XmlConfigBuilderTest.java +++ b/src/test/java/org/apache/ibatis/builder/XmlConfigBuilderTest.java @@ -23,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.math.RoundingMode; @@ -114,7 +115,7 @@ enum MyEnum { public static class EnumOrderTypeHandler> extends BaseTypeHandler { - private E[] constants; + private final E[] constants; public EnumOrderTypeHandler(Class javaType) { constants = javaType.getEnumConstants(); @@ -319,4 +320,20 @@ public static String provideSql() { } } + @Test + void shouldAllowSubclassedConfiguration() throws IOException { + String resource = "org/apache/ibatis/builder/MinimalMapperConfig.xml"; + try (InputStream inputStream = Resources.getResourceAsStream(resource)) { + XMLConfigBuilder builder = new XMLConfigBuilder( + new MyConfiguration(), inputStream, null, null); + Configuration config = builder.parse(); + + assertThat(config).isInstanceOf(MyConfiguration.class); + } + } + + private static class MyConfiguration extends Configuration { + // only using to check configuration was used + } + } From d7826d71a7005a8b4d4e0e7a020db0f6c7e253a4 Mon Sep 17 00:00:00 2001 From: Willie Scholtz Date: Fri, 28 Oct 2022 11:31:30 +0200 Subject: [PATCH 020/382] A subclassed configuration class can now be passed to allow XMLConfigBuilder to use a specific implementation when creating the configuration --- .../ibatis/builder/xml/XMLConfigBuilder.java | 24 ++++++++++++------- .../ibatis/builder/XmlConfigBuilderTest.java | 23 ++++++++++++++++-- 2 files changed, 37 insertions(+), 10 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 72980d3d6f2..0bb9a4d827d 100644 --- a/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java @@ -67,11 +67,11 @@ public XMLConfigBuilder(Reader reader, String environment) { } public XMLConfigBuilder(Reader reader, String environment, Properties props) { - this(new Configuration(), reader, environment, props); + this(Configuration.class, reader, environment, props); } - public XMLConfigBuilder(Configuration configuration, Reader reader, String environment, Properties props) { - this(configuration, new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props); + public XMLConfigBuilder(Class configClass, Reader reader, String environment, Properties props) { + this(configClass, new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props); } public XMLConfigBuilder(InputStream inputStream) { @@ -83,15 +83,15 @@ public XMLConfigBuilder(InputStream inputStream, String environment) { } public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) { - this(new Configuration(), inputStream, environment, props); + this(Configuration.class, inputStream, environment, props); } - public XMLConfigBuilder(Configuration configuration, InputStream inputStream, String environment, Properties props) { - this(configuration, new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props); + public XMLConfigBuilder(Class configClass, InputStream inputStream, String environment, Properties props) { + this(configClass, new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props); } - private XMLConfigBuilder(Configuration configuration, XPathParser parser, String environment, Properties props) { - super(configuration); + private XMLConfigBuilder(Class configClass, XPathParser parser, String environment, Properties props) { + super(newConfig(configClass)); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); this.parsed = false; @@ -414,4 +414,12 @@ private boolean isSpecifiedEnvironment(String id) { return environment.equals(id); } + private static Configuration newConfig(Class configClass) { + try { + return configClass.getDeclaredConstructor().newInstance(); + } catch (Exception ex) { + throw new BuilderException("Failed to create a new Configuration instance.", ex); + } + } + } diff --git a/src/test/java/org/apache/ibatis/builder/XmlConfigBuilderTest.java b/src/test/java/org/apache/ibatis/builder/XmlConfigBuilderTest.java index 3cc472194bd..f19547b77dc 100644 --- a/src/test/java/org/apache/ibatis/builder/XmlConfigBuilderTest.java +++ b/src/test/java/org/apache/ibatis/builder/XmlConfigBuilderTest.java @@ -19,6 +19,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.BDDAssertions.then; import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -65,6 +66,7 @@ import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.TypeHandler; import org.apache.ibatis.type.TypeHandlerRegistry; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -325,15 +327,32 @@ void shouldAllowSubclassedConfiguration() throws IOException { String resource = "org/apache/ibatis/builder/MinimalMapperConfig.xml"; try (InputStream inputStream = Resources.getResourceAsStream(resource)) { XMLConfigBuilder builder = new XMLConfigBuilder( - new MyConfiguration(), inputStream, null, null); + MyConfiguration.class, inputStream, null, null); Configuration config = builder.parse(); assertThat(config).isInstanceOf(MyConfiguration.class); } } - private static class MyConfiguration extends Configuration { + @Test + void noDefaultConstructorForSubclassedConfiguration() throws IOException { + String resource = "org/apache/ibatis/builder/MinimalMapperConfig.xml"; + try (InputStream inputStream = Resources.getResourceAsStream(resource)) { + Exception exception = Assertions.assertThrows(Exception.class, () -> new XMLConfigBuilder( + BadConfiguration.class, inputStream, null, null)); + assertEquals("Failed to create a new Configuration instance.", exception.getMessage()); + } + } + + public static class MyConfiguration extends Configuration { // only using to check configuration was used } + public static class BadConfiguration extends Configuration { + + public BadConfiguration(String parameter) { + // should have a default constructor + } + } + } From ced797363fbb9bb31c1b404c8fa3d39cfcfda8e2 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Sat, 12 Nov 2022 06:57:36 +0900 Subject: [PATCH 021/382] Adding mapper could fail under JPMS Under JPMS, ClassLoader#getResources() throws `FileSystemException` it seems. It might be better to catch IOException, but it could swallow some exception that should be thrown. Should fix #2598 --- src/main/java/org/apache/ibatis/io/DefaultVFS.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/ibatis/io/DefaultVFS.java b/src/main/java/org/apache/ibatis/io/DefaultVFS.java index 69caffc213e..a8d4b03f70f 100644 --- a/src/main/java/org/apache/ibatis/io/DefaultVFS.java +++ b/src/main/java/org/apache/ibatis/io/DefaultVFS.java @@ -26,6 +26,7 @@ import java.net.URL; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystemException; import java.nio.file.InvalidPathException; import java.util.ArrayList; import java.util.Arrays; @@ -107,8 +108,8 @@ public List list(URL url, String path) throws IOException { break; } } - } catch (InvalidPathException e) { - // #1974 + } catch (InvalidPathException | FileSystemException e) { + // #1974 #2598 lines.clear(); } if (!lines.isEmpty()) { From e6a11a3bd0e39dac44cf79a88d4dd8a9263cb9b7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 20:29:39 +0000 Subject: [PATCH 022/382] Update mockito monorepo to v4.9.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index db0c7e35b8d..e9f648e0d0e 100644 --- a/pom.xml +++ b/pom.xml @@ -237,13 +237,13 @@ org.mockito mockito-core - 4.8.1 + 4.9.0 test org.mockito mockito-junit-jupiter - 4.8.1 + 4.9.0 test From 45ed04b7e87618b72fab369e95ed8aeb0f38d323 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 16 Nov 2022 18:26:11 +0000 Subject: [PATCH 023/382] Update dependency org.testcontainers:junit-jupiter to v1.17.6 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e9f648e0d0e..9a0e83197c2 100644 --- a/pom.xml +++ b/pom.xml @@ -280,7 +280,7 @@ org.testcontainers junit-jupiter - 1.17.5 + 1.17.6 test From 82973fff1eb83694028688468cf3f4f91b04f2a7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 16 Nov 2022 18:26:17 +0000 Subject: [PATCH 024/382] Update dependency org.testcontainers:mysql to v1.17.6 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e9f648e0d0e..46ca1b16311 100644 --- a/pom.xml +++ b/pom.xml @@ -292,7 +292,7 @@ org.testcontainers mysql - 1.17.5 + 1.17.6 test From 2d02c63a2cd7def1bea4904e2c11673deede8607 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 16 Nov 2022 21:24:40 +0000 Subject: [PATCH 025/382] Update dependency org.testcontainers:postgresql to v1.17.6 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 46ca1b16311..59c99d11ab7 100644 --- a/pom.xml +++ b/pom.xml @@ -286,7 +286,7 @@ org.testcontainers postgresql - 1.17.5 + 1.17.6 test From bb6107f98cbe6c763cd765abc27e986a574a286b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 17 Nov 2022 23:44:51 +0000 Subject: [PATCH 026/382] Update dependency org.slf4j:slf4j-api to v2.0.4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8efa22e2705..b44bc1cc8a2 100644 --- a/pom.xml +++ b/pom.xml @@ -163,7 +163,7 @@ org.slf4j slf4j-api - 2.0.3 + 2.0.4 true From bdc4bb3be2962992285a7dfc83a1198baf4b681f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 18 Nov 2022 23:06:53 +0000 Subject: [PATCH 027/382] Update dependency ch.qos.logback:logback-classic to v1.4.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b44bc1cc8a2..4dde713884f 100644 --- a/pom.xml +++ b/pom.xml @@ -305,7 +305,7 @@ ch.qos.logback logback-classic - 1.4.4 + 1.4.5 test From aad75eb4c1dfa641159f38ced9650aae5f04b04b Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Sat, 19 Nov 2022 00:35:38 +0900 Subject: [PATCH 028/382] Minor correction: boolean can never be null --- .../org/apache/ibatis/builder/MapperBuilderAssistant.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/apache/ibatis/builder/MapperBuilderAssistant.java b/src/main/java/org/apache/ibatis/builder/MapperBuilderAssistant.java index 1f4ab1ec9f7..993031c997d 100644 --- a/src/main/java/org/apache/ibatis/builder/MapperBuilderAssistant.java +++ b/src/main/java/org/apache/ibatis/builder/MapperBuilderAssistant.java @@ -268,7 +268,6 @@ public MappedStatement addMappedStatement( } id = applyCurrentNamespace(id, false); - boolean isSelect = sqlCommandType == SqlCommandType.SELECT; MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType) .resource(resource) @@ -284,8 +283,8 @@ public MappedStatement addMappedStatement( .resultSets(resultSets) .resultMaps(getStatementResultMaps(resultMap, resultType, id)) .resultSetType(resultSetType) - .flushCacheRequired(valueOrDefault(flushCache, !isSelect)) - .useCache(valueOrDefault(useCache, isSelect)) + .flushCacheRequired(flushCache) + .useCache(useCache) .cache(currentCache); ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id); From eccb9c68db99bfaf9eb8c716b2a247c2a4644a13 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Sat, 19 Nov 2022 01:01:08 +0900 Subject: [PATCH 029/382] Added failing tests Some DBs support INSERT, UPDATE or DELETE statement that returns result set. PostgreSQL has RETURNING MS SQL Server has OUTPUT MyBatis can return results by using @Select, @SelectProvider or + insert into users (name) values (#{name}) + returning id, name + + + From 52fd6ebbf8c6cb6591d389f4613727cf278c70ec Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Sat, 19 Nov 2022 10:45:18 +0900 Subject: [PATCH 030/382] Added 'affectData' attribute to SELECT statements To indicate the SELECT affects DB data. e.g. PostgreSQL's RETURNING, MS SQL Server's OUTPUT --- .../org/apache/ibatis/annotations/Select.java | 9 ++++++++ .../ibatis/annotations/SelectProvider.java | 9 ++++++++ .../builder/MapperBuilderAssistant.java | 22 ++++++++++++++---- .../annotation/MapperAnnotationBuilder.java | 12 ++++++++-- .../builder/xml/XMLStatementBuilder.java | 5 ++-- .../ibatis/mapping/MappedStatement.java | 10 ++++++++ .../session/defaults/DefaultSqlSession.java | 1 + .../ibatis/builder/xml/mybatis-3-mapper.dtd | 1 + .../dirty_select/DirtySelectTest.java | 23 +++++++++++++++++++ .../ibatis/submitted/dirty_select/Mapper.java | 10 ++++++-- .../submitted/dirty_select/CreateDB.sql | 2 ++ .../ibatis/submitted/dirty_select/Mapper.xml | 19 ++++++++++++++- 12 files changed, 112 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/apache/ibatis/annotations/Select.java b/src/main/java/org/apache/ibatis/annotations/Select.java index ada5affeafb..3796871090c 100644 --- a/src/main/java/org/apache/ibatis/annotations/Select.java +++ b/src/main/java/org/apache/ibatis/annotations/Select.java @@ -55,6 +55,15 @@ */ String databaseId() default ""; + /** + * Returns whether this select affects DB data.
+ * e.g. RETURNING of PostgreSQL or OUTPUT of MS SQL Server. + * + * @return {@code true} if this select affects DB data; {@code false} if otherwise + * @since 3.5.12 + */ + boolean affectData() default false; + /** * The container annotation for {@link Select}. * @author Kazuki Shimizu diff --git a/src/main/java/org/apache/ibatis/annotations/SelectProvider.java b/src/main/java/org/apache/ibatis/annotations/SelectProvider.java index f109ebb43de..282374c6459 100644 --- a/src/main/java/org/apache/ibatis/annotations/SelectProvider.java +++ b/src/main/java/org/apache/ibatis/annotations/SelectProvider.java @@ -99,6 +99,15 @@ */ String databaseId() default ""; + /** + * Returns whether this select affects DB data.
+ * e.g. RETURNING of PostgreSQL or OUTPUT of MS SQL Server. + * + * @return {@code true} if this select affects DB data; {@code false} if otherwise + * @since 3.5.12 + */ + boolean affectData() default false; + /** * The container annotation for {@link SelectProvider}. * @author Kazuki Shimizu diff --git a/src/main/java/org/apache/ibatis/builder/MapperBuilderAssistant.java b/src/main/java/org/apache/ibatis/builder/MapperBuilderAssistant.java index 993031c997d..4c9341a16a9 100644 --- a/src/main/java/org/apache/ibatis/builder/MapperBuilderAssistant.java +++ b/src/main/java/org/apache/ibatis/builder/MapperBuilderAssistant.java @@ -261,7 +261,8 @@ public MappedStatement addMappedStatement( String keyColumn, String databaseId, LanguageDriver lang, - String resultSets) { + String resultSets, + boolean dirtySelect) { if (unresolvedCacheRef) { throw new IncompleteElementException("Cache-ref not yet resolved"); @@ -285,7 +286,8 @@ public MappedStatement addMappedStatement( .resultSetType(resultSetType) .flushCacheRequired(flushCache) .useCache(useCache) - .cache(currentCache); + .cache(currentCache) + .dirtySelect(dirtySelect); ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id); if (statementParameterMap != null) { @@ -344,12 +346,24 @@ public MappedStatement addMappedStatement(String id, SqlSource sqlSource, Statem SqlCommandType sqlCommandType, Integer fetchSize, Integer timeout, String parameterMap, Class parameterType, String resultMap, Class resultType, ResultSetType resultSetType, boolean flushCache, boolean useCache, boolean resultOrdered, KeyGenerator keyGenerator, String keyProperty, String keyColumn, String databaseId, - LanguageDriver lang) { + LanguageDriver lang, String resultSets) { return addMappedStatement( id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterType, resultMap, resultType, resultSetType, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, - keyColumn, databaseId, lang, null); + keyColumn, databaseId, lang, null, false); + } + + public MappedStatement addMappedStatement(String id, SqlSource sqlSource, StatementType statementType, + SqlCommandType sqlCommandType, Integer fetchSize, Integer timeout, String parameterMap, Class parameterType, + String resultMap, Class resultType, ResultSetType resultSetType, boolean flushCache, boolean useCache, + boolean resultOrdered, KeyGenerator keyGenerator, String keyProperty, String keyColumn, String databaseId, + LanguageDriver lang) { + return addMappedStatement( + id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, + parameterMap, parameterType, resultMap, resultType, resultSetType, + flushCache, useCache, resultOrdered, keyGenerator, keyProperty, + keyColumn, databaseId, lang, null); } private T valueOrDefault(T value, T defaultValue) { 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 dbf3019e876..af32cafc2dc 100644 --- a/src/main/java/org/apache/ibatis/builder/annotation/MapperAnnotationBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/annotation/MapperAnnotationBuilder.java @@ -378,7 +378,8 @@ void parseStatement(Method method) { statementAnnotation.getDatabaseId(), languageDriver, // ResultSets - options != null ? nullOrEmpty(options.resultSets()) : null); + options != null ? nullOrEmpty(options.resultSets()) : null, + statementAnnotation.isDirtySelect()); }); } @@ -604,7 +605,7 @@ private KeyGenerator handleSelectKeyAnnotation(SelectKey selectKeyAnnotation, St assistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, false, - keyGenerator, keyProperty, keyColumn, databaseId, languageDriver, null); + keyGenerator, keyProperty, keyColumn, databaseId, languageDriver, null, false); id = assistant.applyCurrentNamespace(id, false); @@ -672,6 +673,7 @@ private class AnnotationWrapper { private final Annotation annotation; private final String databaseId; private final SqlCommandType sqlCommandType; + private boolean dirtySelect; AnnotationWrapper(Annotation annotation) { super(); @@ -679,6 +681,7 @@ private class AnnotationWrapper { if (annotation instanceof Select) { databaseId = ((Select) annotation).databaseId(); sqlCommandType = SqlCommandType.SELECT; + dirtySelect = ((Select) annotation).affectData(); } else if (annotation instanceof Update) { databaseId = ((Update) annotation).databaseId(); sqlCommandType = SqlCommandType.UPDATE; @@ -691,6 +694,7 @@ private class AnnotationWrapper { } else if (annotation instanceof SelectProvider) { databaseId = ((SelectProvider) annotation).databaseId(); sqlCommandType = SqlCommandType.SELECT; + dirtySelect = ((SelectProvider) annotation).affectData(); } else if (annotation instanceof UpdateProvider) { databaseId = ((UpdateProvider) annotation).databaseId(); sqlCommandType = SqlCommandType.UPDATE; @@ -723,5 +727,9 @@ SqlCommandType getSqlCommandType() { String getDatabaseId() { return databaseId; } + + boolean isDirtySelect() { + return dirtySelect; + } } } 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 cbe8f63af3c..82316bb49c9 100644 --- a/src/main/java/org/apache/ibatis/builder/xml/XMLStatementBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/xml/XMLStatementBuilder.java @@ -109,11 +109,12 @@ public void parseStatementNode() { String keyProperty = context.getStringAttribute("keyProperty"); String keyColumn = context.getStringAttribute("keyColumn"); String resultSets = context.getStringAttribute("resultSets"); + boolean dirtySelect = context.getBooleanAttribute("affectData", Boolean.FALSE); builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, - keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); + keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets, dirtySelect); } private void processSelectKeyNodes(String id, Class parameterTypeClass, LanguageDriver langDriver) { @@ -160,7 +161,7 @@ private void parseSelectKeyNode(String id, XNode nodeToHandle, Class paramete builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, - keyGenerator, keyProperty, keyColumn, databaseId, langDriver, null); + keyGenerator, keyProperty, keyColumn, databaseId, langDriver, null, false); id = builderAssistant.applyCurrentNamespace(id, false); diff --git a/src/main/java/org/apache/ibatis/mapping/MappedStatement.java b/src/main/java/org/apache/ibatis/mapping/MappedStatement.java index af3454869c2..c5269af470f 100644 --- a/src/main/java/org/apache/ibatis/mapping/MappedStatement.java +++ b/src/main/java/org/apache/ibatis/mapping/MappedStatement.java @@ -56,6 +56,7 @@ public final class MappedStatement { private Log statementLog; private LanguageDriver lang; private String[] resultSets; + private boolean dirtySelect; MappedStatement() { // constructor disabled @@ -174,6 +175,11 @@ public Builder resultSets(String resultSet) { return this; } + public Builder dirtySelect(boolean dirtySelect) { + mappedStatement.dirtySelect = dirtySelect; + return this; + } + /** * Resul sets. * @@ -290,6 +296,10 @@ public String[] getResultSets() { return resultSets; } + public boolean isDirtySelect() { + return dirtySelect; + } + /** * Gets the resul sets. * diff --git a/src/main/java/org/apache/ibatis/session/defaults/DefaultSqlSession.java b/src/main/java/org/apache/ibatis/session/defaults/DefaultSqlSession.java index aca3e32770b..18819dc6b4d 100644 --- a/src/main/java/org/apache/ibatis/session/defaults/DefaultSqlSession.java +++ b/src/main/java/org/apache/ibatis/session/defaults/DefaultSqlSession.java @@ -148,6 +148,7 @@ public List selectList(String statement, Object parameter, RowBounds rowB private List selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { try { MappedStatement ms = configuration.getMappedStatement(statement); + dirty |= ms.isDirtySelect(); return executor.query(ms, wrapCollection(parameter), rowBounds, handler); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); diff --git a/src/main/resources/org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd b/src/main/resources/org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd index 81965360dae..28aff86fa6e 100644 --- a/src/main/resources/org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd +++ b/src/main/resources/org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd @@ -184,6 +184,7 @@ databaseId CDATA #IMPLIED lang CDATA #IMPLIED resultOrdered (true|false) #IMPLIED resultSets CDATA #IMPLIED +affectData (true|false) #IMPLIED > diff --git a/src/test/java/org/apache/ibatis/submitted/dirty_select/DirtySelectTest.java b/src/test/java/org/apache/ibatis/submitted/dirty_select/DirtySelectTest.java index 4fb43ff6d44..0e98ccb5ecd 100644 --- a/src/test/java/org/apache/ibatis/submitted/dirty_select/DirtySelectTest.java +++ b/src/test/java/org/apache/ibatis/submitted/dirty_select/DirtySelectTest.java @@ -101,4 +101,27 @@ void shouldRollbackIfCalled_Provider() { } } + @Test + void shouldNonDirtySelectNotUnsetDirtyFlag() { + Integer id; + try (SqlSession sqlSession = sqlSessionFactory.openSession(false)) { + Mapper mapper = sqlSession.getMapper(Mapper.class); + // INSERT + User user = new User(); + user.setName("Bobby"); + mapper.insert(user); + id = user.getId(); + assertNotNull(id); + assertEquals("Bobby", user.getName()); + // Non-dirty SELECT + mapper.selectById(id); + sqlSession.rollback(); + } + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + Mapper mapper = sqlSession.getMapper(Mapper.class); + User user = mapper.selectById(id); + assertNull(user); + } + } + } diff --git a/src/test/java/org/apache/ibatis/submitted/dirty_select/Mapper.java b/src/test/java/org/apache/ibatis/submitted/dirty_select/Mapper.java index d02bfe4e680..d59d519fe91 100644 --- a/src/test/java/org/apache/ibatis/submitted/dirty_select/Mapper.java +++ b/src/test/java/org/apache/ibatis/submitted/dirty_select/Mapper.java @@ -15,6 +15,8 @@ */ package org.apache.ibatis.submitted.dirty_select; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Options; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.SelectProvider; @@ -23,14 +25,18 @@ public interface Mapper { @Select("select * from users where id = #{id}") User selectById(Integer id); - @Select(value = "insert into users (name) values (#{name}) returning id, name") + @Select(value = "insert into users (name) values (#{name}) returning id, name", affectData = true) User insertReturn(String name); User insertReturnXml(String name); - @SelectProvider(type = MyProvider.class, method = "getSql") + @SelectProvider(type = MyProvider.class, method = "getSql", affectData = true) User insertReturnProvider(String name); + @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") + @Insert("insert into users (name) values (#{name}) returning id, name") + int insert(User user); + static class MyProvider { public static String getSql() { return "insert into users (name) values (#{name}) returning id, name"; diff --git a/src/test/resources/org/apache/ibatis/submitted/dirty_select/CreateDB.sql b/src/test/resources/org/apache/ibatis/submitted/dirty_select/CreateDB.sql index 1f254e88333..fe7d925cce5 100644 --- a/src/test/resources/org/apache/ibatis/submitted/dirty_select/CreateDB.sql +++ b/src/test/resources/org/apache/ibatis/submitted/dirty_select/CreateDB.sql @@ -14,6 +14,8 @@ -- limitations under the License. -- +drop table if exists users; + create table users ( id serial primary key, name varchar(30) diff --git a/src/test/resources/org/apache/ibatis/submitted/dirty_select/Mapper.xml b/src/test/resources/org/apache/ibatis/submitted/dirty_select/Mapper.xml index f8b36b580d6..b6b69ebfc99 100644 --- a/src/test/resources/org/apache/ibatis/submitted/dirty_select/Mapper.xml +++ b/src/test/resources/org/apache/ibatis/submitted/dirty_select/Mapper.xml @@ -1,9 +1,26 @@ + - ]]> + diff --git a/src/site/ja/xdoc/java-api.xml b/src/site/ja/xdoc/java-api.xml index 320e3399fbc..e019d3bdfbb 100644 --- a/src/site/ja/xdoc/java-api.xml +++ b/src/site/ja/xdoc/java-api.xml @@ -249,13 +249,13 @@ public interface ResultHandler {

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

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

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

void commit() void commit(boolean force) void rollback() void rollback(boolean force) -

デフォルトでは、データベースが insert, update, delete メソッドの実行によって変更されない限り MyBatis は commit を実行しません。何らかの理由でこれらのメソッドを使わずにデータを変更した場合は確実にコミットされるように commit メソッドに引数 true を渡してください(ただし、auto-commit モードのセッションや外部のトランザクションマネージャーを使っている場合は true を渡してもコミットされません)。commit が実行されない場合、MyBatis がロールバックを実行するので、通常明示的に rollback() メソッドを呼び出す必要はありません。しかし、一つのセッションの中で複数のコミットやロールバックが必要とされるようなケースでは、rollback() メソッドを使ってより細かい制御を行うことが可能です。

+

デフォルトでは、データベースの変更を伴うメソッド insert, update, delete, affectData を有効化した select が実行されない限り MyBatis は commit を実行しません。何らかの理由でこれらのメソッドを使わずにデータを変更した場合は確実にコミットされるように commit メソッドに引数 true を渡してください(ただし、auto-commit モードのセッションや外部のトランザクションマネージャーを使っている場合は true を渡してもコミットされません)。commit が実行されない場合、MyBatis がロールバックを実行するので、通常明示的に rollback() メソッドを呼び出す必要はありません。しかし、一つのセッションの中で複数のコミットやロールバックが必要とされるようなケースでは、rollback() メソッドを使ってより細かい制御を行うことが可能です。

NOTE Mybatis-Spring と MyBatis-Guice では宣言的トランザクションがサポートされています。詳細は各サブプロジェクトのドキュメントを参照してください。

ローカルキャッシュ
diff --git a/src/site/ja/xdoc/sqlmap-xml.xml b/src/site/ja/xdoc/sqlmap-xml.xml index 27b7b92f571..eae6ca58207 100644 --- a/src/site/ja/xdoc/sqlmap-xml.xml +++ b/src/site/ja/xdoc/sqlmap-xml.xml @@ -233,6 +233,11 @@ ps.setInt(1,id);]]> 複数の ResultSet を利用する場合にのみ有効です。ステートメントが返す ResultSet にそれぞれ任意の名前を付けてリストアップします。名前はカンマで区切ります。 + + affectData + ResultSet を返す INSERT, UPDATE, DELETE 文を記述する場合に true をセットします。これによりトランザクション制御が正しく実行されるようになります。トランザクションを制御するメソッド も参照してください。 デフォルト: false (3.5.12 以降) + +
@@ -453,6 +458,18 @@ ps.setInt(1,id);]]> + +

+ 例外として、INSERT, UPDATE, DELETE 文から ResultSet を返す SQL 文(PostgreSQL, MariaDB の RETURNING , MS SQL Server の OUTPUT など)で結果をマップするためには ]]> を使用する必要があります。 +

+ + + insert into Author (username, password, email, bio) + values (#{username}, #{password}, #{email}, #{bio}) + returning id, username, password, email, bio +]]> + diff --git a/src/site/ko/xdoc/java-api.xml b/src/site/ko/xdoc/java-api.xml index 8d3a6bbb9b5..21028e14b34 100644 --- a/src/site/ko/xdoc/java-api.xml +++ b/src/site/ko/xdoc/java-api.xml @@ -316,7 +316,7 @@ public interface ResultHandler { 이 방법은 ExecutorTypeExecutorType.BATCH로 설정한 경우 사용가능하다.

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

트랜잭션을 제어하기 위해 4개의 메소드가 있다. 물론 자동커밋을 선택하였거나 외부 트랜잭션 관리자를 사용하면 영향이 없다. 어쨌든 Connection인스턴스에 의해 관리되고 JDBC 트랜잭션 관리자를 사용하면 이 4개의 메소드를 사용할 수 있다.

@@ -324,7 +324,7 @@ public interface ResultHandler { void commit(boolean force) void rollback() void rollback(boolean force) -

기본적으로 마이바티스는 insert, update 또는 delete 를 호출하여 데이터베이스가 변경된 것으로 감지하지 않는 한 실제로 커밋하지 않는다. +

기본적으로 마이바티스는 insert, update, delete 또는 affectData가 활성화된select 를 호출하여 데이터베이스가 변경된 것으로 감지하지 않는 한 실제로 커밋하지 않는다. 이러한 메소드 호출없이 변경되면 커밋된 것으로 보장하기 위해 commit 와 rollback 메소드에 true 값을 전달한다.

참고 MyBatis-Spring 과 MyBatis-Guice는 선언적인 트랜잭션 관리기법을 제공한다. 그래서 스프링이나 쥬스와 함께 마이바티스를 사용한다면 해당되는 메뉴얼을 꼭 참고하길 바란다.

diff --git a/src/site/ko/xdoc/sqlmap-xml.xml b/src/site/ko/xdoc/sqlmap-xml.xml index 70a6615a425..4ddde0df518 100644 --- a/src/site/ko/xdoc/sqlmap-xml.xml +++ b/src/site/ko/xdoc/sqlmap-xml.xml @@ -188,6 +188,11 @@ ps.setInt(1,id);]]> 디폴트값은 false 이다. + + affectData + Set this to true when writing a INSERT, UPDATE or DELETE statement that returns data so that the transaction is controlled properly. Also see Transaction Control Method. Default: false (since 3.5.12) + +
@@ -392,6 +397,18 @@ ps.setInt(1,id);]]> + +

+ As an irregular case, some databases allow INSERT, UPDATE or DELETE statement to return result set (e.g. RETURNING clause of PostgreSQL and MariaDB or OUTPUT clause of MS SQL Server). This type of statement must be written as ]]> to map the returned data. +

+ + + insert into Author (username, password, email, bio) + values (#{username}, #{password}, #{email}, #{bio}) + returning id, username, password, email, bio +]]> + diff --git a/src/site/xdoc/java-api.xml b/src/site/xdoc/java-api.xml index 8305480e587..f67e1fe959c 100644 --- a/src/site/xdoc/java-api.xml +++ b/src/site/xdoc/java-api.xml @@ -251,13 +251,13 @@ public interface ResultHandler {

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

flushStatements()]]> -
Transaction Control Methods
+
Transaction Control Methods

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

void commit() void commit(boolean force) void rollback() void rollback(boolean force) -

By default MyBatis does not actually commit unless it detects that the database has been changed by a call to insert, update or delete. If you've somehow made changes without calling these methods, then you can pass true into the commit and rollback methods to guarantee that they will be committed (note, you still can't force a session in auto-commit mode, or one that is using an external transaction manager). Most of the time you won't have to call rollback(), as MyBatis will do that for you if you don't call commit. However, if you need more fine-grained control over a session where multiple commits and rollbacks are possible, you have the rollback option there to make that possible.

+

By default MyBatis does not actually commit unless it detects that the database has been changed by a call to insert, update, delete or select with affectData enabled. If you've somehow made changes without calling these methods, then you can pass true into the commit and rollback methods to guarantee that they will be committed (note, you still can't force a session in auto-commit mode, or one that is using an external transaction manager). Most of the time you won't have to call rollback(), as MyBatis will do that for you if you don't call commit. However, if you need more fine-grained control over a session where multiple commits and rollbacks are possible, you have the rollback option there to make that possible.

NOTE MyBatis-Spring and MyBatis-Guice provide declarative transaction handling. So if you are using MyBatis with Spring or Guice please refer to their specific manuals.

Local Cache
diff --git a/src/site/xdoc/sqlmap-xml.xml b/src/site/xdoc/sqlmap-xml.xml index 7ba060720c4..21332e04a5a 100644 --- a/src/site/xdoc/sqlmap-xml.xml +++ b/src/site/xdoc/sqlmap-xml.xml @@ -245,6 +245,11 @@ ps.setInt(1,id);]]> be returned by the statement and gives a name to each one. Names are separated by commas. + + affectData + Set this to true when writing a INSERT, UPDATE or DELETE statement that returns data so that the transaction is controlled properly. Also see Transaction Control Method. Default: false (since 3.5.12) + +
@@ -491,6 +496,18 @@ ps.setInt(1,id);]]> + +

+ As an irregular case, some databases allow INSERT, UPDATE or DELETE statement to return result set (e.g. RETURNING clause of PostgreSQL and MariaDB or OUTPUT clause of MS SQL Server). This type of statement must be written as ]]> to map the returned data. +

+ + + insert into Author (username, password, email, bio) + values (#{username}, #{password}, #{email}, #{bio}) + returning id, username, password, email, bio +]]> + diff --git a/src/site/zh/xdoc/java-api.xml b/src/site/zh/xdoc/java-api.xml index 59a812531fa..e6c8a1f872f 100644 --- a/src/site/zh/xdoc/java-api.xml +++ b/src/site/zh/xdoc/java-api.xml @@ -245,7 +245,7 @@ public interface ResultHandler {

当你将 ExecutorType 设置为 ExecutorType.BATCH 时,可以使用这个方法清除(执行)缓存在 JDBC 驱动类中的批量更新语句。

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

有四个方法用来控制事务作用域。当然,如果你已经设置了自动提交或你使用了外部事务管理器,这些方法就没什么作用了。然而,如果你正在使用由 Connection 实例控制的 JDBC 事务管理器,那么这四个方法就会派上用场:

@@ -253,7 +253,7 @@ public interface ResultHandler { void commit(boolean force) void rollback() void rollback(boolean force) -

默认情况下 MyBatis 不会自动提交事务,除非它侦测到调用了插入、更新或删除方法改变了数据库。如果你没有使用这些方法提交修改,那么你可以在 commit 和 rollback 方法参数中传入 true 值,来保证事务被正常提交(注意,在自动提交模式或者使用了外部事务管理器的情况下,设置 force 值对 session 无效)。大部分情况下你无需调用 rollback(),因为 MyBatis 会在你没有调用 commit 时替你完成回滚操作。不过,当你要在一个可能多次提交或回滚的 session 中详细控制事务,回滚操作就派上用场了。

+

默认情况下 MyBatis 不会自动提交事务,除非它侦测到调用了插入、更新、删除或 select with affectData enabled 方法改变了数据库。如果你没有使用这些方法提交修改,那么你可以在 commit 和 rollback 方法参数中传入 true 值,来保证事务被正常提交(注意,在自动提交模式或者使用了外部事务管理器的情况下,设置 force 值对 session 无效)。大部分情况下你无需调用 rollback(),因为 MyBatis 会在你没有调用 commit 时替你完成回滚操作。不过,当你要在一个可能多次提交或回滚的 session 中详细控制事务,回滚操作就派上用场了。

提示 MyBatis-Spring 和 MyBatis-Guice 提供了声明式事务处理,所以如果你在使用 Mybatis 的同时使用了 Spring 或者 Guice,请参考它们的手册以获取更多的内容。

本地缓存
diff --git a/src/site/zh/xdoc/sqlmap-xml.xml b/src/site/zh/xdoc/sqlmap-xml.xml index 00150259943..87b50547083 100644 --- a/src/site/zh/xdoc/sqlmap-xml.xml +++ b/src/site/zh/xdoc/sqlmap-xml.xml @@ -233,6 +233,11 @@ ps.setInt(1,id);]]> 这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔。 + + affectData + Set this to true when writing a INSERT, UPDATE or DELETE statement that returns data so that the transaction is controlled properly. Also see Transaction Control Method. Default: false (since 3.5.12) + +
@@ -461,6 +466,18 @@ ps.setInt(1,id);]]> + +

+ As an irregular case, some databases allow INSERT, UPDATE or DELETE statement to return result set (e.g. RETURNING clause of PostgreSQL and MariaDB or OUTPUT clause of MS SQL Server). This type of statement must be written as ]]> to map the returned data. +

+ + + insert into Author (username, password, email, bio) + values (#{username}, #{password}, #{email}, #{bio}) + returning id, username, password, email, bio +]]> + From 9e786dc270325ec95c4ee3a04c1833adfa0e86aa Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Mon, 28 Nov 2022 01:17:37 +0900 Subject: [PATCH 035/382] selectCursor() should respect affectData as well --- .../session/defaults/DefaultSqlSession.java | 1 + .../dirty_select/DirtySelectTest.java | 50 +++++++++++++++++++ .../ibatis/submitted/dirty_select/Mapper.java | 7 +++ 3 files changed, 58 insertions(+) diff --git a/src/main/java/org/apache/ibatis/session/defaults/DefaultSqlSession.java b/src/main/java/org/apache/ibatis/session/defaults/DefaultSqlSession.java index 18819dc6b4d..2dad6548e3c 100644 --- a/src/main/java/org/apache/ibatis/session/defaults/DefaultSqlSession.java +++ b/src/main/java/org/apache/ibatis/session/defaults/DefaultSqlSession.java @@ -120,6 +120,7 @@ public Cursor selectCursor(String statement, Object parameter) { public Cursor selectCursor(String statement, Object parameter, RowBounds rowBounds) { try { MappedStatement ms = configuration.getMappedStatement(statement); + dirty |= ms.isDirtySelect(); Cursor cursor = executor.queryCursor(ms, wrapCollection(parameter), rowBounds); registerCursor(cursor); return cursor; diff --git a/src/test/java/org/apache/ibatis/submitted/dirty_select/DirtySelectTest.java b/src/test/java/org/apache/ibatis/submitted/dirty_select/DirtySelectTest.java index 0e98ccb5ecd..64d6cd31a50 100644 --- a/src/test/java/org/apache/ibatis/submitted/dirty_select/DirtySelectTest.java +++ b/src/test/java/org/apache/ibatis/submitted/dirty_select/DirtySelectTest.java @@ -17,7 +17,10 @@ import static org.junit.jupiter.api.Assertions.*; +import java.util.Iterator; + import org.apache.ibatis.BaseDataTest; +import org.apache.ibatis.cursor.Cursor; import org.apache.ibatis.mapping.Environment; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.SqlSession; @@ -101,6 +104,27 @@ void shouldRollbackIfCalled_Provider() { } } + @Test + void shouldRollbackIfCalled_Cursor() throws Exception { + Integer id; + try (SqlSession sqlSession = sqlSessionFactory.openSession(false)) { + Mapper mapper = sqlSession.getMapper(Mapper.class); + try (Cursor cursor = mapper.insertReturnCursor("Kate")) { + Iterator iterator = cursor.iterator(); + User user = iterator.next(); + id = user.getId(); + assertNotNull(id); + assertEquals("Kate", user.getName()); + } + sqlSession.rollback(); + } + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + Mapper mapper = sqlSession.getMapper(Mapper.class); + User user = mapper.selectById(id); + assertNull(user); + } + } + @Test void shouldNonDirtySelectNotUnsetDirtyFlag() { Integer id; @@ -124,4 +148,30 @@ void shouldNonDirtySelectNotUnsetDirtyFlag() { } } + @Test + void shouldNonDirtySelectNotUnsetDirtyFlag_Cursor() throws Exception { + Integer id; + try (SqlSession sqlSession = sqlSessionFactory.openSession(false)) { + Mapper mapper = sqlSession.getMapper(Mapper.class); + // INSERT + User user = new User(); + user.setName("Bobby"); + mapper.insert(user); + id = user.getId(); + assertNotNull(id); + assertEquals("Bobby", user.getName()); + // Non-dirty SELECT + try (Cursor cursor = mapper.selectCursorById(id)) { + Iterator iterator = cursor.iterator(); + iterator.next(); + } + sqlSession.rollback(); + } + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + Mapper mapper = sqlSession.getMapper(Mapper.class); + User user = mapper.selectById(id); + assertNull(user); + } + } + } diff --git a/src/test/java/org/apache/ibatis/submitted/dirty_select/Mapper.java b/src/test/java/org/apache/ibatis/submitted/dirty_select/Mapper.java index d59d519fe91..65fc52fe40f 100644 --- a/src/test/java/org/apache/ibatis/submitted/dirty_select/Mapper.java +++ b/src/test/java/org/apache/ibatis/submitted/dirty_select/Mapper.java @@ -19,15 +19,22 @@ import org.apache.ibatis.annotations.Options; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.SelectProvider; +import org.apache.ibatis.cursor.Cursor; public interface Mapper { @Select("select * from users where id = #{id}") User selectById(Integer id); + @Select("select * from users where id = #{id}") + Cursor selectCursorById(Integer id); + @Select(value = "insert into users (name) values (#{name}) returning id, name", affectData = true) User insertReturn(String name); + @Select(value = "insert into users (name) values (#{name}) returning id, name", affectData = true) + Cursor insertReturnCursor(String name); + User insertReturnXml(String name); @SelectProvider(type = MyProvider.class, method = "getSql", affectData = true) From a36e9a10e49f50e822aa5856dcdd55910e85c866 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 28 Nov 2022 22:51:09 +0000 Subject: [PATCH 036/382] Update dependency ch.qos.reload4j:reload4j to v1.2.24 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e37afd32b56..65b4d7fee33 100644 --- a/pom.xml +++ b/pom.xml @@ -169,7 +169,7 @@ ch.qos.reload4j reload4j - 1.2.23 + 1.2.24 true From 4e14c632b9d259242dcc9ccf9353063765ac5c98 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Wed, 30 Nov 2022 11:41:13 +0900 Subject: [PATCH 037/382] Missing entry in es and ko doc [ci skip] --- src/site/es/xdoc/sqlmap-xml.xml | 6 ++++++ src/site/ko/xdoc/sqlmap-xml.xml | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/site/es/xdoc/sqlmap-xml.xml b/src/site/es/xdoc/sqlmap-xml.xml index e7c0f6e2f45..48b530de187 100644 --- a/src/site/es/xdoc/sqlmap-xml.xml +++ b/src/site/es/xdoc/sqlmap-xml.xml @@ -201,6 +201,12 @@ ps.setInt(1,id);]]> false. + + resultSets + This is only applicable for multiple result sets. It lists the result sets that will + be returned by the statement and gives a name to each one. Names are separated by commas. + + affectData Set this to true when writing a INSERT, UPDATE or DELETE statement that returns data so that the transaction is controlled properly. Also see Transaction Control Method. Default: false (since 3.5.12) diff --git a/src/site/ko/xdoc/sqlmap-xml.xml b/src/site/ko/xdoc/sqlmap-xml.xml index 4ddde0df518..d49d9cd7ef2 100644 --- a/src/site/ko/xdoc/sqlmap-xml.xml +++ b/src/site/ko/xdoc/sqlmap-xml.xml @@ -188,6 +188,12 @@ ps.setInt(1,id);]]> 디폴트값은 false 이다. + + resultSets + This is only applicable for multiple result sets. It lists the result sets that will + be returned by the statement and gives a name to each one. Names are separated by commas. + + affectData Set this to true when writing a INSERT, UPDATE or DELETE statement that returns data so that the transaction is controlled properly. Also see Transaction Control Method. Default: false (since 3.5.12) From 5911cb6476a707992536c8c7d1c1785131bac409 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Sun, 4 Dec 2022 01:21:47 +0900 Subject: [PATCH 038/382] Awful typo [ci skip] --- src/site/es/xdoc/configuration.xml | 2 +- src/site/ko/xdoc/configuration.xml | 2 +- src/site/xdoc/configuration.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/site/es/xdoc/configuration.xml b/src/site/es/xdoc/configuration.xml index 1fdeaeed130..d61a93f96e7 100644 --- a/src/site/es/xdoc/configuration.xml +++ b/src/site/es/xdoc/configuration.xml @@ -1764,7 +1764,7 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader,properti

MyBatis incluye dos tipos de TransactionManager (ej. type=”[JDBC|MANAGED]”):