Skip to content

Commit

Permalink
Support appendable method on SqlSessionFactoryBean
Browse files Browse the repository at this point in the history
Fixes mybatisgh-787

* addMapperLocations
* addTypeHandlers
* addScriptingLanguageDrivers
* addPlugins
* addTypeAliases
  • Loading branch information
kazuki43zoo committed May 13, 2023
1 parent 3004d20 commit f206e7e
Show file tree
Hide file tree
Showing 4 changed files with 304 additions and 1 deletion.
86 changes: 86 additions & 0 deletions src/main/java/org/mybatis/spring/SqlSessionFactoryBean.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.function.IntFunction;
import java.util.stream.Stream;

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

/**
* Add locations of MyBatis mapper files that are going to be merged into the {@code SqlSessionFactory} configuration
* at runtime.
* <p>
* This is an alternative to specifying "&lt;sqlmapper&gt;" entries in an MyBatis config file. This property being
* based on Spring's resource abstraction also allows for specifying resource patterns here: e.g.
* "classpath*:sqlmap/*-mapper.xml".
*
* @param mapperLocations
* location of MyBatis mapper files
*
* @see #setMapperLocations(Resource...)
*
* @since 3.0.2
*/
public void addMapperLocations(Resource... mapperLocations) {
setMapperLocations(appendArrays(this.mapperLocations, mapperLocations, Resource[]::new));
}

/**
* Add type handlers.
*
* @param typeHandlers
* Type handler list
*
* @since 3.0.2
*/
public void addTypeHandlers(TypeHandler<?>... typeHandlers) {
setTypeHandlers(appendArrays(this.typeHandlers, typeHandlers, TypeHandler[]::new));
}

/**
* Add scripting language drivers.
*
* @param scriptingLanguageDrivers
* scripting language drivers
*
* @since 3.0.2
*/
public void addScriptingLanguageDrivers(LanguageDriver... scriptingLanguageDrivers) {
setScriptingLanguageDrivers(
appendArrays(this.scriptingLanguageDrivers, scriptingLanguageDrivers, LanguageDriver[]::new));
}

/**
* Add Mybatis plugins.
*
* @param plugins
* list of plugins
*
* @since 3.0.2
*/
public void addPlugins(Interceptor... plugins) {
setPlugins(appendArrays(this.plugins, plugins, Interceptor[]::new));
}

/**
* Add type aliases.
*
* @param typeAliases
* Type aliases list
*
* @since 3.0.2
*/
public void addTypeAliases(Class<?>... typeAliases) {
setTypeAliases(appendArrays(this.typeAliases, typeAliases, Class[]::new));
}

private <T> T[] appendArrays(T[] oldArrays, T[] newArrays, IntFunction<T[]> generator) {
if (oldArrays == null) {
return newArrays;
} else {
if (newArrays == null) {
return oldArrays;
} else {
List<T> newList = new ArrayList<>(Arrays.asList(oldArrays));
newList.addAll(Arrays.asList(newArrays));
return newList.toArray(generator.apply(0));
}
}
}

/**
* {@inheritDoc}
*/
Expand Down
163 changes: 162 additions & 1 deletion src/test/java/org/mybatis/spring/SqlSessionFactoryBeanTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2022 the original author or authors.
* Copyright 2010-2023 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.
Expand All @@ -22,13 +22,24 @@

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

import org.apache.ibatis.cache.impl.PerpetualCache;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.io.JBoss6VFS;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
Expand All @@ -41,9 +52,12 @@
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.EnumOrdinalTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeAliasRegistry;
import org.apache.ibatis.type.TypeException;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.junit.jupiter.api.Test;
import org.mybatis.core.jdk.type.AtomicNumberTypeHandler;
Expand Down Expand Up @@ -492,6 +506,88 @@ void testScriptingLanguageDriverWithDefault() throws Exception {
assertThat(registry.getDriver(RawLanguageDriver.class)).isNotNull();
}

@Test
void testAppendableMethod() throws Exception {
setupFactoryBean();
// add values
this.factoryBean.addScriptingLanguageDrivers(new MyLanguageDriver1());
this.factoryBean.addScriptingLanguageDrivers(new MyLanguageDriver2());
this.factoryBean.addPlugins(new MyPlugin1(), new MyPlugin2());
this.factoryBean.addPlugins(new MyPlugin3());
this.factoryBean.addTypeHandlers(new MyTypeHandler1());
this.factoryBean.addTypeHandlers(new MyTypeHandler2(), new MyTypeHandler3());
this.factoryBean.addTypeAliases(MyTypeHandler1.class, MyTypeHandler2.class, MyTypeHandler3.class);
this.factoryBean.addTypeAliases(MyPlugin1.class);
this.factoryBean.addMapperLocations(new ClassPathResource("org/mybatis/spring/TestMapper.xml"),
new ClassPathResource("org/mybatis/spring/TestMapper2.xml"));
this.factoryBean.addMapperLocations(new ClassPathResource("org/mybatis/spring/TestMapper3.xml"));
// ignore null value
this.factoryBean.addScriptingLanguageDrivers(null);
this.factoryBean.addPlugins(null);
this.factoryBean.addTypeHandlers(null);
this.factoryBean.addTypeAliases(null);
this.factoryBean.addMapperLocations(null);
SqlSessionFactory factory = this.factoryBean.getObject();
LanguageDriverRegistry languageDriverRegistry = factory.getConfiguration().getLanguageRegistry();
TypeHandlerRegistry typeHandlerRegistry = factory.getConfiguration().getTypeHandlerRegistry();
TypeAliasRegistry typeAliasRegistry = factory.getConfiguration().getTypeAliasRegistry();
assertThat(languageDriverRegistry.getDriver(MyLanguageDriver1.class)).isNotNull();
assertThat(languageDriverRegistry.getDriver(MyLanguageDriver2.class)).isNotNull();
assertThat(typeHandlerRegistry.getTypeHandlers().stream().map(TypeHandler::getClass).map(Class::getSimpleName)
.collect(Collectors.toSet())).contains(MyTypeHandler1.class.getSimpleName(),
MyTypeHandler2.class.getSimpleName(), MyTypeHandler3.class.getSimpleName());
assertThat(typeAliasRegistry.getTypeAliases()).containsKeys(MyTypeHandler1.class.getSimpleName().toLowerCase(),
MyTypeHandler2.class.getSimpleName().toLowerCase(), MyTypeHandler3.class.getSimpleName().toLowerCase(),
MyPlugin1.class.getSimpleName().toLowerCase());
assertThat(factory.getConfiguration().getMappedStatement("org.mybatis.spring.TestMapper.findFail")).isNotNull();
assertThat(factory.getConfiguration().getMappedStatement("org.mybatis.spring.TestMapper2.selectOne")).isNotNull();
assertThat(factory.getConfiguration().getMappedStatement("org.mybatis.spring.TestMapper3.selectOne")).isNotNull();
assertThat(
factory.getConfiguration().getInterceptors().stream().map(Interceptor::getClass).map(Class::getSimpleName))
.contains(MyPlugin1.class.getSimpleName(), MyPlugin2.class.getSimpleName(),
MyPlugin3.class.getSimpleName());
}

@Test
void testAppendableMethodWithEmpty() throws Exception {
setupFactoryBean();
this.factoryBean.addScriptingLanguageDrivers();
this.factoryBean.addPlugins();
this.factoryBean.addTypeHandlers();
this.factoryBean.addTypeAliases();
this.factoryBean.addMapperLocations();
SqlSessionFactory factory = this.factoryBean.getObject();
LanguageDriverRegistry languageDriverRegistry = factory.getConfiguration().getLanguageRegistry();
TypeHandlerRegistry typeHandlerRegistry = factory.getConfiguration().getTypeHandlerRegistry();
TypeAliasRegistry typeAliasRegistry = factory.getConfiguration().getTypeAliasRegistry();
assertThat(languageDriverRegistry.getDriver(MyLanguageDriver1.class)).isNull();
assertThat(languageDriverRegistry.getDriver(MyLanguageDriver2.class)).isNull();
assertThat(typeHandlerRegistry.getTypeHandlers()).hasSize(40);
assertThat(typeAliasRegistry.getTypeAliases()).hasSize(80);
assertThat(factory.getConfiguration().getMappedStatementNames()).isEmpty();
assertThat(factory.getConfiguration().getInterceptors()).isEmpty();
}

@Test
void testAppendableMethodWithNull() throws Exception {
setupFactoryBean();
this.factoryBean.addScriptingLanguageDrivers(null);
this.factoryBean.addPlugins(null);
this.factoryBean.addTypeHandlers(null);
this.factoryBean.addTypeAliases(null);
this.factoryBean.addMapperLocations(null);
SqlSessionFactory factory = this.factoryBean.getObject();
LanguageDriverRegistry languageDriverRegistry = factory.getConfiguration().getLanguageRegistry();
TypeHandlerRegistry typeHandlerRegistry = factory.getConfiguration().getTypeHandlerRegistry();
TypeAliasRegistry typeAliasRegistry = factory.getConfiguration().getTypeAliasRegistry();
assertThat(languageDriverRegistry.getDriver(MyLanguageDriver1.class)).isNull();
assertThat(languageDriverRegistry.getDriver(MyLanguageDriver2.class)).isNull();
assertThat(typeHandlerRegistry.getTypeHandlers()).hasSize(40);
assertThat(typeAliasRegistry.getTypeAliases()).hasSize(80);
assertThat(factory.getConfiguration().getMappedStatementNames()).isEmpty();
assertThat(factory.getConfiguration().getInterceptors()).isEmpty();
}

private void assertDefaultConfig(SqlSessionFactory factory) {
assertConfig(factory, SqlSessionFactoryBean.class.getSimpleName(),
org.mybatis.spring.transaction.SpringManagedTransactionFactory.class);
Expand Down Expand Up @@ -522,6 +618,71 @@ private static class MyLanguageDriver1 extends RawLanguageDriver {
private static class MyLanguageDriver2 extends RawLanguageDriver {
}

private static class MyBasePlugin implements Interceptor {

@Override
public Object intercept(Invocation invocation) throws Throwable {
return null;
}

@Override
public Object plugin(Object target) {
return Interceptor.super.plugin(target);
}

@Override
public void setProperties(Properties properties) {
Interceptor.super.setProperties(properties);
}
}

@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
private static class MyPlugin1 extends MyBasePlugin {

}

@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
private static class MyPlugin2 extends MyBasePlugin {

}

@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
private static class MyPlugin3 extends MyBasePlugin {

}

private static class MyBaseTypeHandler extends BaseTypeHandler<String> {

@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
throws SQLException {
}

@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
return null;
}

@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return null;
}

@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return null;
}
}

private static class MyTypeHandler1 extends MyBaseTypeHandler {
}

private static class MyTypeHandler2 extends MyBaseTypeHandler {
}

private static class MyTypeHandler3 extends MyBaseTypeHandler {
}

private static enum MyEnum {
}

Expand Down
28 changes: 28 additions & 0 deletions src/test/java/org/mybatis/spring/TestMapper2.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2010-2023 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
https://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.
-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mybatis.spring.TestMapper2">

<select id="selectOne" resultType="int">
SELECT 1
</select>

</mapper>
28 changes: 28 additions & 0 deletions src/test/java/org/mybatis/spring/TestMapper3.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2010-2023 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
https://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.
-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mybatis.spring.TestMapper3">

<select id="selectOne" resultType="int">
SELECT 1
</select>

</mapper>

0 comments on commit f206e7e

Please sign in to comment.