Skip to content

Commit e8b9024

Browse files
authoredMay 13, 2023
Merge pull request #817 from kazuki43zoo/gh-787_support-appendable-method-on-sqlsessionfactorybean-2.1.x
Support appendable method on SqlSessionFactoryBean on 2.1.x
2 parents 042aec1 + 7569e8b commit e8b9024

File tree

4 files changed

+304
-1
lines changed

4 files changed

+304
-1
lines changed
 

‎src/main/java/org/mybatis/spring/SqlSessionFactoryBean.java

+86
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,14 @@
2424
import java.io.IOException;
2525
import java.lang.reflect.Modifier;
2626
import java.sql.SQLException;
27+
import java.util.ArrayList;
28+
import java.util.Arrays;
2729
import java.util.HashSet;
30+
import java.util.List;
2831
import java.util.Optional;
2932
import java.util.Properties;
3033
import java.util.Set;
34+
import java.util.function.IntFunction;
3135
import java.util.stream.Stream;
3236

3337
import javax.sql.DataSource;
@@ -478,6 +482,88 @@ public void setDefaultScriptingLanguageDriver(Class<? extends LanguageDriver> de
478482
this.defaultScriptingLanguageDriver = defaultScriptingLanguageDriver;
479483
}
480484

485+
/**
486+
* Add locations of MyBatis mapper files that are going to be merged into the {@code SqlSessionFactory} configuration
487+
* at runtime.
488+
* <p>
489+
* This is an alternative to specifying "&lt;sqlmapper&gt;" entries in an MyBatis config file. This property being
490+
* based on Spring's resource abstraction also allows for specifying resource patterns here: e.g.
491+
* "classpath*:sqlmap/*-mapper.xml".
492+
*
493+
* @param mapperLocations
494+
* location of MyBatis mapper files
495+
*
496+
* @see #setMapperLocations(Resource...)
497+
*
498+
* @since 2.1.1
499+
*/
500+
public void addMapperLocations(Resource... mapperLocations) {
501+
setMapperLocations(appendArrays(this.mapperLocations, mapperLocations, Resource[]::new));
502+
}
503+
504+
/**
505+
* Add type handlers.
506+
*
507+
* @param typeHandlers
508+
* Type handler list
509+
*
510+
* @since 2.1.1
511+
*/
512+
public void addTypeHandlers(TypeHandler<?>... typeHandlers) {
513+
setTypeHandlers(appendArrays(this.typeHandlers, typeHandlers, TypeHandler[]::new));
514+
}
515+
516+
/**
517+
* Add scripting language drivers.
518+
*
519+
* @param scriptingLanguageDrivers
520+
* scripting language drivers
521+
*
522+
* @since 2.1.1
523+
*/
524+
public void addScriptingLanguageDrivers(LanguageDriver... scriptingLanguageDrivers) {
525+
setScriptingLanguageDrivers(
526+
appendArrays(this.scriptingLanguageDrivers, scriptingLanguageDrivers, LanguageDriver[]::new));
527+
}
528+
529+
/**
530+
* Add Mybatis plugins.
531+
*
532+
* @param plugins
533+
* list of plugins
534+
*
535+
* @since 2.1.1
536+
*/
537+
public void addPlugins(Interceptor... plugins) {
538+
setPlugins(appendArrays(this.plugins, plugins, Interceptor[]::new));
539+
}
540+
541+
/**
542+
* Add type aliases.
543+
*
544+
* @param typeAliases
545+
* Type aliases list
546+
*
547+
* @since 2.1.1
548+
*/
549+
public void addTypeAliases(Class<?>... typeAliases) {
550+
setTypeAliases(appendArrays(this.typeAliases, typeAliases, Class[]::new));
551+
}
552+
553+
private <T> T[] appendArrays(T[] oldArrays, T[] newArrays, IntFunction<T[]> generator) {
554+
if (oldArrays == null) {
555+
return newArrays;
556+
} else {
557+
if (newArrays == null) {
558+
return oldArrays;
559+
} else {
560+
List<T> newList = new ArrayList<>(Arrays.asList(oldArrays));
561+
newList.addAll(Arrays.asList(newArrays));
562+
return newList.toArray(generator.apply(0));
563+
}
564+
}
565+
}
566+
481567
/**
482568
* {@inheritDoc}
483569
*/

‎src/test/java/org/mybatis/spring/SqlSessionFactoryBeanTest.java

+162-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2010-2022 the original author or authors.
2+
* Copyright 2010-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,13 +22,24 @@
2222

2323
import java.math.BigDecimal;
2424
import java.math.BigInteger;
25+
import java.sql.CallableStatement;
26+
import java.sql.PreparedStatement;
27+
import java.sql.ResultSet;
28+
import java.sql.SQLException;
2529
import java.util.Properties;
2630
import java.util.UUID;
2731
import java.util.concurrent.atomic.AtomicInteger;
2832
import java.util.concurrent.atomic.AtomicLong;
33+
import java.util.stream.Collectors;
2934

3035
import org.apache.ibatis.cache.impl.PerpetualCache;
36+
import org.apache.ibatis.executor.Executor;
3137
import org.apache.ibatis.io.JBoss6VFS;
38+
import org.apache.ibatis.mapping.MappedStatement;
39+
import org.apache.ibatis.plugin.Interceptor;
40+
import org.apache.ibatis.plugin.Intercepts;
41+
import org.apache.ibatis.plugin.Invocation;
42+
import org.apache.ibatis.plugin.Signature;
3243
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
3344
import org.apache.ibatis.reflection.factory.ObjectFactory;
3445
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
@@ -41,9 +52,12 @@
4152
import org.apache.ibatis.session.SqlSessionFactory;
4253
import org.apache.ibatis.transaction.TransactionFactory;
4354
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
55+
import org.apache.ibatis.type.BaseTypeHandler;
4456
import org.apache.ibatis.type.EnumOrdinalTypeHandler;
57+
import org.apache.ibatis.type.JdbcType;
4558
import org.apache.ibatis.type.TypeAliasRegistry;
4659
import org.apache.ibatis.type.TypeException;
60+
import org.apache.ibatis.type.TypeHandler;
4761
import org.apache.ibatis.type.TypeHandlerRegistry;
4862
import org.junit.jupiter.api.Test;
4963
import org.mybatis.core.jdk.type.AtomicNumberTypeHandler;
@@ -492,6 +506,88 @@ void testScriptingLanguageDriverWithDefault() throws Exception {
492506
assertThat(registry.getDriver(RawLanguageDriver.class)).isNotNull();
493507
}
494508

509+
@Test
510+
void testAppendableMethod() throws Exception {
511+
setupFactoryBean();
512+
// add values
513+
this.factoryBean.addScriptingLanguageDrivers(new MyLanguageDriver1());
514+
this.factoryBean.addScriptingLanguageDrivers(new MyLanguageDriver2());
515+
this.factoryBean.addPlugins(new MyPlugin1(), new MyPlugin2());
516+
this.factoryBean.addPlugins(new MyPlugin3());
517+
this.factoryBean.addTypeHandlers(new MyTypeHandler1());
518+
this.factoryBean.addTypeHandlers(new MyTypeHandler2(), new MyTypeHandler3());
519+
this.factoryBean.addTypeAliases(MyTypeHandler1.class, MyTypeHandler2.class, MyTypeHandler3.class);
520+
this.factoryBean.addTypeAliases(MyPlugin1.class);
521+
this.factoryBean.addMapperLocations(new ClassPathResource("org/mybatis/spring/TestMapper.xml"),
522+
new ClassPathResource("org/mybatis/spring/TestMapper2.xml"));
523+
this.factoryBean.addMapperLocations(new ClassPathResource("org/mybatis/spring/TestMapper3.xml"));
524+
// ignore null value
525+
this.factoryBean.addScriptingLanguageDrivers(null);
526+
this.factoryBean.addPlugins(null);
527+
this.factoryBean.addTypeHandlers(null);
528+
this.factoryBean.addTypeAliases(null);
529+
this.factoryBean.addMapperLocations(null);
530+
SqlSessionFactory factory = this.factoryBean.getObject();
531+
LanguageDriverRegistry languageDriverRegistry = factory.getConfiguration().getLanguageRegistry();
532+
TypeHandlerRegistry typeHandlerRegistry = factory.getConfiguration().getTypeHandlerRegistry();
533+
TypeAliasRegistry typeAliasRegistry = factory.getConfiguration().getTypeAliasRegistry();
534+
assertThat(languageDriverRegistry.getDriver(MyLanguageDriver1.class)).isNotNull();
535+
assertThat(languageDriverRegistry.getDriver(MyLanguageDriver2.class)).isNotNull();
536+
assertThat(typeHandlerRegistry.getTypeHandlers().stream().map(TypeHandler::getClass).map(Class::getSimpleName)
537+
.collect(Collectors.toSet())).contains(MyTypeHandler1.class.getSimpleName(),
538+
MyTypeHandler2.class.getSimpleName(), MyTypeHandler3.class.getSimpleName());
539+
assertThat(typeAliasRegistry.getTypeAliases()).containsKeys(MyTypeHandler1.class.getSimpleName().toLowerCase(),
540+
MyTypeHandler2.class.getSimpleName().toLowerCase(), MyTypeHandler3.class.getSimpleName().toLowerCase(),
541+
MyPlugin1.class.getSimpleName().toLowerCase());
542+
assertThat(factory.getConfiguration().getMappedStatement("org.mybatis.spring.TestMapper.findFail")).isNotNull();
543+
assertThat(factory.getConfiguration().getMappedStatement("org.mybatis.spring.TestMapper2.selectOne")).isNotNull();
544+
assertThat(factory.getConfiguration().getMappedStatement("org.mybatis.spring.TestMapper3.selectOne")).isNotNull();
545+
assertThat(
546+
factory.getConfiguration().getInterceptors().stream().map(Interceptor::getClass).map(Class::getSimpleName))
547+
.contains(MyPlugin1.class.getSimpleName(), MyPlugin2.class.getSimpleName(),
548+
MyPlugin3.class.getSimpleName());
549+
}
550+
551+
@Test
552+
void testAppendableMethodWithEmpty() throws Exception {
553+
setupFactoryBean();
554+
this.factoryBean.addScriptingLanguageDrivers();
555+
this.factoryBean.addPlugins();
556+
this.factoryBean.addTypeHandlers();
557+
this.factoryBean.addTypeAliases();
558+
this.factoryBean.addMapperLocations();
559+
SqlSessionFactory factory = this.factoryBean.getObject();
560+
LanguageDriverRegistry languageDriverRegistry = factory.getConfiguration().getLanguageRegistry();
561+
TypeHandlerRegistry typeHandlerRegistry = factory.getConfiguration().getTypeHandlerRegistry();
562+
TypeAliasRegistry typeAliasRegistry = factory.getConfiguration().getTypeAliasRegistry();
563+
assertThat(languageDriverRegistry.getDriver(MyLanguageDriver1.class)).isNull();
564+
assertThat(languageDriverRegistry.getDriver(MyLanguageDriver2.class)).isNull();
565+
assertThat(typeHandlerRegistry.getTypeHandlers()).hasSize(40);
566+
assertThat(typeAliasRegistry.getTypeAliases()).hasSize(80);
567+
assertThat(factory.getConfiguration().getMappedStatementNames()).isEmpty();
568+
assertThat(factory.getConfiguration().getInterceptors()).isEmpty();
569+
}
570+
571+
@Test
572+
void testAppendableMethodWithNull() throws Exception {
573+
setupFactoryBean();
574+
this.factoryBean.addScriptingLanguageDrivers(null);
575+
this.factoryBean.addPlugins(null);
576+
this.factoryBean.addTypeHandlers(null);
577+
this.factoryBean.addTypeAliases(null);
578+
this.factoryBean.addMapperLocations(null);
579+
SqlSessionFactory factory = this.factoryBean.getObject();
580+
LanguageDriverRegistry languageDriverRegistry = factory.getConfiguration().getLanguageRegistry();
581+
TypeHandlerRegistry typeHandlerRegistry = factory.getConfiguration().getTypeHandlerRegistry();
582+
TypeAliasRegistry typeAliasRegistry = factory.getConfiguration().getTypeAliasRegistry();
583+
assertThat(languageDriverRegistry.getDriver(MyLanguageDriver1.class)).isNull();
584+
assertThat(languageDriverRegistry.getDriver(MyLanguageDriver2.class)).isNull();
585+
assertThat(typeHandlerRegistry.getTypeHandlers()).hasSize(40);
586+
assertThat(typeAliasRegistry.getTypeAliases()).hasSize(80);
587+
assertThat(factory.getConfiguration().getMappedStatementNames()).isEmpty();
588+
assertThat(factory.getConfiguration().getInterceptors()).isEmpty();
589+
}
590+
495591
private void assertDefaultConfig(SqlSessionFactory factory) {
496592
assertConfig(factory, SqlSessionFactoryBean.class.getSimpleName(),
497593
org.mybatis.spring.transaction.SpringManagedTransactionFactory.class);
@@ -522,6 +618,71 @@ private static class MyLanguageDriver1 extends RawLanguageDriver {
522618
private static class MyLanguageDriver2 extends RawLanguageDriver {
523619
}
524620

621+
private static class MyBasePlugin implements Interceptor {
622+
623+
@Override
624+
public Object intercept(Invocation invocation) throws Throwable {
625+
return null;
626+
}
627+
628+
@Override
629+
public Object plugin(Object target) {
630+
return Interceptor.super.plugin(target);
631+
}
632+
633+
@Override
634+
public void setProperties(Properties properties) {
635+
Interceptor.super.setProperties(properties);
636+
}
637+
}
638+
639+
@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
640+
private static class MyPlugin1 extends MyBasePlugin {
641+
642+
}
643+
644+
@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
645+
private static class MyPlugin2 extends MyBasePlugin {
646+
647+
}
648+
649+
@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
650+
private static class MyPlugin3 extends MyBasePlugin {
651+
652+
}
653+
654+
private static class MyBaseTypeHandler extends BaseTypeHandler<String> {
655+
656+
@Override
657+
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
658+
throws SQLException {
659+
}
660+
661+
@Override
662+
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
663+
return null;
664+
}
665+
666+
@Override
667+
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
668+
return null;
669+
}
670+
671+
@Override
672+
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
673+
return null;
674+
}
675+
}
676+
677+
private static class MyTypeHandler1 extends MyBaseTypeHandler {
678+
}
679+
680+
private static class MyTypeHandler2 extends MyBaseTypeHandler {
681+
}
682+
683+
private static class MyTypeHandler3 extends MyBaseTypeHandler {
684+
}
685+
525686
private static enum MyEnum {
526687
}
527688

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
Copyright 2010-2023 the original author or authors.
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
https://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
18+
-->
19+
<!DOCTYPE mapper
20+
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
21+
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
22+
<mapper namespace="org.mybatis.spring.TestMapper2">
23+
24+
<select id="selectOne" resultType="int">
25+
SELECT 1
26+
</select>
27+
28+
</mapper>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
Copyright 2010-2023 the original author or authors.
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
https://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
18+
-->
19+
<!DOCTYPE mapper
20+
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
21+
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
22+
<mapper namespace="org.mybatis.spring.TestMapper3">
23+
24+
<select id="selectOne" resultType="int">
25+
SELECT 1
26+
</select>
27+
28+
</mapper>

0 commit comments

Comments
 (0)