diff --git a/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java b/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java index 4bcae4edc4..cf0856b0f7 100644 --- a/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java +++ b/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java @@ -26,7 +26,7 @@ /** * {@link DataAccessStrategy} implementation based on MyBatis. Each method gets mapped to a statement. The name of the - * statement gets constructed as follows: The namespace is based on the class of the entity plus the suffix "Mapper". + * statement gets constructed as follows: By default, the namespace is based on the class of the entity plus the suffix "Mapper". * This is then followed by the method name separated by a dot. For methods taking a {@link PropertyPath} as argument, * the relevant entity is that of the root of the path, and the path itself gets as dot separated String appended to the * statement name. Each statement gets an instance of {@link MyBatisContext}, which at least has the entityType set. For @@ -37,9 +37,8 @@ */ public class MyBatisDataAccessStrategy implements DataAccessStrategy { - private static final String MAPPER_SUFFIX = "Mapper"; - private final SqlSession sqlSession; + private MyBatisNamingStrategy namingStrategy = new MyBatisNamingStrategy() {}; /** * Constructs a {@link DataAccessStrategy} based on MyBatis. @@ -53,30 +52,38 @@ public MyBatisDataAccessStrategy(SqlSession sqlSession) { this.sqlSession = sqlSession; } + /** + * Set a naming strategy for MyBatis objects. + * @param namingStrategy Must be non {@literal null} + */ + public void setNamingStrategy(MyBatisNamingStrategy namingStrategy) { + this.namingStrategy = namingStrategy; + } + @Override public void insert(T instance, Class domainType, Map additionalParameters) { - sqlSession().insert(mapper(domainType) + ".insert", + sqlSession().insert(namespace(domainType) + ".insert", new MyBatisContext(null, instance, domainType, additionalParameters)); } @Override public void update(S instance, Class domainType) { - sqlSession().update(mapper(domainType) + ".update", + sqlSession().update(namespace(domainType) + ".update", new MyBatisContext(null, instance, domainType, Collections.emptyMap())); } @Override public void delete(Object id, Class domainType) { - sqlSession().delete(mapper(domainType) + ".delete", + sqlSession().delete(namespace(domainType) + ".delete", new MyBatisContext(id, null, domainType, Collections.emptyMap())); } @Override public void delete(Object rootId, PropertyPath propertyPath) { - sqlSession().delete(mapper(propertyPath.getOwningType().getType()) + ".delete-" + toDashPath(propertyPath), + sqlSession().delete(namespace(propertyPath.getOwningType().getType()) + ".delete-" + toDashPath(propertyPath), new MyBatisContext(rootId, null, propertyPath.getLeafProperty().getTypeInformation().getType(), Collections.emptyMap())); } @@ -85,7 +92,7 @@ public void delete(Object rootId, PropertyPath propertyPath) { public void deleteAll(Class domainType) { sqlSession().delete( // - mapper(domainType) + ".deleteAll", // + namespace(domainType) + ".deleteAll", // new MyBatisContext(null, null, domainType, Collections.emptyMap()) // ); } @@ -97,49 +104,49 @@ public void deleteAll(PropertyPath propertyPath) { Class leaveType = propertyPath.getLeafProperty().getTypeInformation().getType(); sqlSession().delete( // - mapper(baseType) + ".deleteAll-" + toDashPath(propertyPath), // + namespace(baseType) + ".deleteAll-" + toDashPath(propertyPath), // new MyBatisContext(null, null, leaveType, Collections.emptyMap()) // ); } @Override public T findById(Object id, Class domainType) { - return sqlSession().selectOne(mapper(domainType) + ".findById", + return sqlSession().selectOne(namespace(domainType) + ".findById", new MyBatisContext(id, null, domainType, Collections.emptyMap())); } @Override public Iterable findAll(Class domainType) { - return sqlSession().selectList(mapper(domainType) + ".findAll", + return sqlSession().selectList(namespace(domainType) + ".findAll", new MyBatisContext(null, null, domainType, Collections.emptyMap())); } @Override public Iterable findAllById(Iterable ids, Class domainType) { - return sqlSession().selectList(mapper(domainType) + ".findAllById", + return sqlSession().selectList(namespace(domainType) + ".findAllById", new MyBatisContext(ids, null, domainType, Collections.emptyMap())); } @Override public Iterable findAllByProperty(Object rootId, JdbcPersistentProperty property) { - return sqlSession().selectList(mapper(property.getOwner().getType()) + ".findAllByProperty-" + property.getName(), + return sqlSession().selectList(namespace(property.getOwner().getType()) + ".findAllByProperty-" + property.getName(), new MyBatisContext(rootId, null, property.getType(), Collections.emptyMap())); } @Override public boolean existsById(Object id, Class domainType) { - return sqlSession().selectOne(mapper(domainType) + ".existsById", + return sqlSession().selectOne(namespace(domainType) + ".existsById", new MyBatisContext(id, null, domainType, Collections.emptyMap())); } @Override public long count(Class domainType) { - return sqlSession().selectOne(mapper(domainType) + ".count", + return sqlSession().selectOne(namespace(domainType) + ".count", new MyBatisContext(null, null, domainType, Collections.emptyMap())); } - private String mapper(Class domainType) { - return domainType.getName() + MAPPER_SUFFIX; + private String namespace(Class domainType) { + return this.namingStrategy.getNamespace(domainType); } private SqlSession sqlSession() { diff --git a/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisNamingStrategy.java b/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisNamingStrategy.java new file mode 100644 index 0000000000..89c23cb736 --- /dev/null +++ b/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisNamingStrategy.java @@ -0,0 +1,36 @@ +/* + * Copyright 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.springframework.data.jdbc.mybatis; + +/** + * The naming strategy for MyBatis. + * + * @author Kazuki Shimizu + */ +public interface MyBatisNamingStrategy { + + /** + * Get a namespace that correspond domain type. + *

+ * By default, the namespace is based on the class of the entity plus the suffix "Mapper". + * @param domainType Must be non {@literal null}. + * @return a namespace that correspond domain type + */ + default String getNamespace(Class domainType) { + return domainType.getName() + "Mapper"; + } + +} diff --git a/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisCustomizingNamespaceHsqlIntegrationTests.java b/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisCustomizingNamespaceHsqlIntegrationTests.java new file mode 100644 index 0000000000..b74835edd3 --- /dev/null +++ b/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisCustomizingNamespaceHsqlIntegrationTests.java @@ -0,0 +1,121 @@ +/* + * Copyright 2017-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.springframework.data.jdbc.mybatis; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; + +import junit.framework.AssertionFailedError; + +import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.mybatis.spring.SqlSessionTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories; +import org.springframework.data.jdbc.testing.TestConfiguration; +import org.springframework.data.repository.CrudRepository; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.junit4.rules.SpringClassRule; +import org.springframework.test.context.junit4.rules.SpringMethodRule; +import org.springframework.transaction.annotation.Transactional; + +/** + * Tests the integration for customizing namespace with Mybatis. + * + * @author Kazuki Shimizu + */ +@ContextConfiguration +@Transactional +public class MyBatisCustomizingNamespaceHsqlIntegrationTests { + + @org.springframework.context.annotation.Configuration + @Import(TestConfiguration.class) + @EnableJdbcRepositories(considerNestedRepositories = true) + static class Config { + + @Bean + Class testClass() { + return MyBatisCustomizingNamespaceHsqlIntegrationTests.class; + } + + @Bean + SqlSessionFactoryBean createSessionFactory(EmbeddedDatabase db) throws IOException { + + Configuration configuration = new Configuration(); + configuration.getTypeAliasRegistry().registerAlias("MyBatisContext", MyBatisContext.class); + configuration.getTypeAliasRegistry().registerAlias(DummyEntity.class); + + SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); + sqlSessionFactoryBean.setDataSource(db); + sqlSessionFactoryBean.setConfiguration(configuration); + sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver() + .getResources("classpath*:org/springframework/data/jdbc/mybatis/mapper/*Mapper.xml")); + + return sqlSessionFactoryBean; + } + + @Bean + SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory factory) { + return new SqlSessionTemplate(factory); + } + + @Bean + MyBatisDataAccessStrategy dataAccessStrategy(SqlSession sqlSession) { + MyBatisDataAccessStrategy strategy = new MyBatisDataAccessStrategy(sqlSession); + strategy.setNamingStrategy(new MyBatisNamingStrategy() { + @Override + public String getNamespace(Class domainType) { + return domainType.getPackage().getName() + ".mapper." + domainType.getSimpleName() + "Mapper"; + } + }); + return strategy; + } + } + + @ClassRule public static final SpringClassRule classRule = new SpringClassRule(); + @Rule public SpringMethodRule methodRule = new SpringMethodRule(); + + @Autowired SqlSessionFactory sqlSessionFactory; + @Autowired DummyEntityRepository repository; + + @Test // DATAJDBC-178 + public void myBatisGetsUsedForInsertAndSelect() { + + DummyEntity entity = new DummyEntity(null, "some name"); + DummyEntity saved = repository.save(entity); + + assertThat(saved.id).isNotNull(); + + DummyEntity reloaded = repository.findById(saved.id).orElseThrow(AssertionFailedError::new); + + assertThat(reloaded.name).isEqualTo("name " + saved.id); + } + + interface DummyEntityRepository extends CrudRepository { + } + +} diff --git a/src/test/resources/org.springframework.data.jdbc.mybatis/MyBatisCustomizingNamespaceHsqlIntegrationTests-hsql.sql b/src/test/resources/org.springframework.data.jdbc.mybatis/MyBatisCustomizingNamespaceHsqlIntegrationTests-hsql.sql new file mode 100644 index 0000000000..a2f6eb9021 --- /dev/null +++ b/src/test/resources/org.springframework.data.jdbc.mybatis/MyBatisCustomizingNamespaceHsqlIntegrationTests-hsql.sql @@ -0,0 +1 @@ +CREATE TABLE dummyentity(id BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY); \ No newline at end of file diff --git a/src/test/resources/org/springframework/data/jdbc/mybatis/mapper/DummyEntityMapper.xml b/src/test/resources/org/springframework/data/jdbc/mybatis/mapper/DummyEntityMapper.xml new file mode 100644 index 0000000000..1c6a04ee0e --- /dev/null +++ b/src/test/resources/org/springframework/data/jdbc/mybatis/mapper/DummyEntityMapper.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + INSERT INTO DummyEntity (id) VALUES (DEFAULT) + + + \ No newline at end of file