Skip to content

Commit 45a6d34

Browse files
kazuki43zooschauder
authored andcommitted
DATAJDBC-178 - Allow customizing the MyBatis namespace.
The MyBatis namespace used can now be customised by providing a NamespaceStrategy to the MyBatisDataAccessStrategy. Original pull request: #44.
1 parent db2b44d commit 45a6d34

File tree

5 files changed

+204
-17
lines changed

5 files changed

+204
-17
lines changed

src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333

3434
/**
3535
* {@link DataAccessStrategy} implementation based on MyBatis. Each method gets mapped to a statement. The name of the
36-
* statement gets constructed as follows: The namespace is based on the class of the entity plus the suffix "Mapper".
36+
* statement gets constructed as follows: By default, the namespace is based on the class of the entity plus the suffix "Mapper".
3737
* This is then followed by the method name separated by a dot. For methods taking a {@link PropertyPath} as argument,
3838
* the relevant entity is that of the root of the path, and the path itself gets as dot separated String appended to the
3939
* statement name. Each statement gets an instance of {@link MyBatisContext}, which at least has the entityType set. For
@@ -44,9 +44,8 @@
4444
*/
4545
public class MyBatisDataAccessStrategy implements DataAccessStrategy {
4646

47-
private static final String MAPPER_SUFFIX = "Mapper";
48-
4947
private final SqlSession sqlSession;
48+
private MyBatisNamingStrategy namingStrategy = new MyBatisNamingStrategy() {};
5049

5150
/**
5251
* Create a {@link DataAccessStrategy} that first checks for queries defined by MyBatis and if it doesn't find one
@@ -92,30 +91,38 @@ public MyBatisDataAccessStrategy(SqlSession sqlSession) {
9291
this.sqlSession = sqlSession;
9392
}
9493

94+
/**
95+
* Set a naming strategy for MyBatis objects.
96+
* @param namingStrategy Must be non {@literal null}
97+
*/
98+
public void setNamingStrategy(MyBatisNamingStrategy namingStrategy) {
99+
this.namingStrategy = namingStrategy;
100+
}
101+
95102
@Override
96103
public <T> void insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters) {
97-
sqlSession().insert(mapper(domainType) + ".insert",
104+
sqlSession().insert(namespace(domainType) + ".insert",
98105
new MyBatisContext(null, instance, domainType, additionalParameters));
99106
}
100107

101108
@Override
102109
public <S> void update(S instance, Class<S> domainType) {
103110

104-
sqlSession().update(mapper(domainType) + ".update",
111+
sqlSession().update(namespace(domainType) + ".update",
105112
new MyBatisContext(null, instance, domainType, Collections.emptyMap()));
106113
}
107114

108115
@Override
109116
public void delete(Object id, Class<?> domainType) {
110117

111-
sqlSession().delete(mapper(domainType) + ".delete",
118+
sqlSession().delete(namespace(domainType) + ".delete",
112119
new MyBatisContext(id, null, domainType, Collections.emptyMap()));
113120
}
114121

115122
@Override
116123
public void delete(Object rootId, PropertyPath propertyPath) {
117124

118-
sqlSession().delete(mapper(propertyPath.getOwningType().getType()) + ".delete-" + toDashPath(propertyPath),
125+
sqlSession().delete(namespace(propertyPath.getOwningType().getType()) + ".delete-" + toDashPath(propertyPath),
119126
new MyBatisContext(rootId, null, propertyPath.getLeafProperty().getTypeInformation().getType(),
120127
Collections.emptyMap()));
121128
}
@@ -124,7 +131,7 @@ public void delete(Object rootId, PropertyPath propertyPath) {
124131
public <T> void deleteAll(Class<T> domainType) {
125132

126133
sqlSession().delete( //
127-
mapper(domainType) + ".deleteAll", //
134+
namespace(domainType) + ".deleteAll", //
128135
new MyBatisContext(null, null, domainType, Collections.emptyMap()) //
129136
);
130137
}
@@ -136,49 +143,49 @@ public <T> void deleteAll(PropertyPath propertyPath) {
136143
Class leaveType = propertyPath.getLeafProperty().getTypeInformation().getType();
137144

138145
sqlSession().delete( //
139-
mapper(baseType) + ".deleteAll-" + toDashPath(propertyPath), //
146+
namespace(baseType) + ".deleteAll-" + toDashPath(propertyPath), //
140147
new MyBatisContext(null, null, leaveType, Collections.emptyMap()) //
141148
);
142149
}
143150

144151
@Override
145152
public <T> T findById(Object id, Class<T> domainType) {
146-
return sqlSession().selectOne(mapper(domainType) + ".findById",
153+
return sqlSession().selectOne(namespace(domainType) + ".findById",
147154
new MyBatisContext(id, null, domainType, Collections.emptyMap()));
148155
}
149156

150157
@Override
151158
public <T> Iterable<T> findAll(Class<T> domainType) {
152-
return sqlSession().selectList(mapper(domainType) + ".findAll",
159+
return sqlSession().selectList(namespace(domainType) + ".findAll",
153160
new MyBatisContext(null, null, domainType, Collections.emptyMap()));
154161
}
155162

156163
@Override
157164
public <T> Iterable<T> findAllById(Iterable<?> ids, Class<T> domainType) {
158-
return sqlSession().selectList(mapper(domainType) + ".findAllById",
165+
return sqlSession().selectList(namespace(domainType) + ".findAllById",
159166
new MyBatisContext(ids, null, domainType, Collections.emptyMap()));
160167
}
161168

162169
@Override
163170
public <T> Iterable<T> findAllByProperty(Object rootId, JdbcPersistentProperty property) {
164-
return sqlSession().selectList(mapper(property.getOwner().getType()) + ".findAllByProperty-" + property.getName(),
171+
return sqlSession().selectList(namespace(property.getOwner().getType()) + ".findAllByProperty-" + property.getName(),
165172
new MyBatisContext(rootId, null, property.getType(), Collections.emptyMap()));
166173
}
167174

168175
@Override
169176
public <T> boolean existsById(Object id, Class<T> domainType) {
170-
return sqlSession().selectOne(mapper(domainType) + ".existsById",
177+
return sqlSession().selectOne(namespace(domainType) + ".existsById",
171178
new MyBatisContext(id, null, domainType, Collections.emptyMap()));
172179
}
173180

174181
@Override
175182
public long count(Class<?> domainType) {
176-
return sqlSession().selectOne(mapper(domainType) + ".count",
183+
return sqlSession().selectOne(namespace(domainType) + ".count",
177184
new MyBatisContext(null, null, domainType, Collections.emptyMap()));
178185
}
179186

180-
private String mapper(Class<?> domainType) {
181-
return domainType.getName() + MAPPER_SUFFIX;
187+
private String namespace(Class<?> domainType) {
188+
return this.namingStrategy.getNamespace(domainType);
182189
}
183190

184191
private SqlSession sqlSession() {
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.jdbc.mybatis;
17+
18+
/**
19+
* The naming strategy for MyBatis.
20+
*
21+
* @author Kazuki Shimizu
22+
*/
23+
public interface MyBatisNamingStrategy {
24+
25+
/**
26+
* Get a namespace that correspond domain type.
27+
* <p>
28+
* By default, the namespace is based on the class of the entity plus the suffix "Mapper".
29+
* @param domainType Must be non {@literal null}.
30+
* @return a namespace that correspond domain type
31+
*/
32+
default String getNamespace(Class<?> domainType) {
33+
return domainType.getName() + "Mapper";
34+
}
35+
36+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
* Copyright 2017-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.jdbc.mybatis;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
20+
import java.io.IOException;
21+
22+
import junit.framework.AssertionFailedError;
23+
24+
import org.apache.ibatis.session.Configuration;
25+
import org.apache.ibatis.session.SqlSession;
26+
import org.apache.ibatis.session.SqlSessionFactory;
27+
import org.junit.ClassRule;
28+
import org.junit.Rule;
29+
import org.junit.Test;
30+
import org.mybatis.spring.SqlSessionFactoryBean;
31+
import org.mybatis.spring.SqlSessionTemplate;
32+
import org.springframework.beans.factory.annotation.Autowired;
33+
import org.springframework.context.annotation.Bean;
34+
import org.springframework.context.annotation.Import;
35+
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
36+
import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories;
37+
import org.springframework.data.jdbc.testing.TestConfiguration;
38+
import org.springframework.data.repository.CrudRepository;
39+
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
40+
import org.springframework.test.context.ContextConfiguration;
41+
import org.springframework.test.context.jdbc.Sql;
42+
import org.springframework.test.context.junit4.rules.SpringClassRule;
43+
import org.springframework.test.context.junit4.rules.SpringMethodRule;
44+
import org.springframework.transaction.annotation.Transactional;
45+
46+
/**
47+
* Tests the integration for customizing namespace with Mybatis.
48+
*
49+
* @author Kazuki Shimizu
50+
*/
51+
@ContextConfiguration
52+
@Transactional
53+
public class MyBatisCustomizingNamespaceHsqlIntegrationTests {
54+
55+
@org.springframework.context.annotation.Configuration
56+
@Import(TestConfiguration.class)
57+
@EnableJdbcRepositories(considerNestedRepositories = true)
58+
static class Config {
59+
60+
@Bean
61+
Class<?> testClass() {
62+
return MyBatisCustomizingNamespaceHsqlIntegrationTests.class;
63+
}
64+
65+
@Bean
66+
SqlSessionFactoryBean createSessionFactory(EmbeddedDatabase db) throws IOException {
67+
68+
Configuration configuration = new Configuration();
69+
configuration.getTypeAliasRegistry().registerAlias("MyBatisContext", MyBatisContext.class);
70+
configuration.getTypeAliasRegistry().registerAlias(DummyEntity.class);
71+
72+
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
73+
sqlSessionFactoryBean.setDataSource(db);
74+
sqlSessionFactoryBean.setConfiguration(configuration);
75+
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
76+
.getResources("classpath*:org/springframework/data/jdbc/mybatis/mapper/*Mapper.xml"));
77+
78+
return sqlSessionFactoryBean;
79+
}
80+
81+
@Bean
82+
SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory factory) {
83+
return new SqlSessionTemplate(factory);
84+
}
85+
86+
@Bean
87+
MyBatisDataAccessStrategy dataAccessStrategy(SqlSession sqlSession) {
88+
MyBatisDataAccessStrategy strategy = new MyBatisDataAccessStrategy(sqlSession);
89+
strategy.setNamingStrategy(new MyBatisNamingStrategy() {
90+
@Override
91+
public String getNamespace(Class<?> domainType) {
92+
return domainType.getPackage().getName() + ".mapper." + domainType.getSimpleName() + "Mapper";
93+
}
94+
});
95+
return strategy;
96+
}
97+
}
98+
99+
@ClassRule public static final SpringClassRule classRule = new SpringClassRule();
100+
@Rule public SpringMethodRule methodRule = new SpringMethodRule();
101+
102+
@Autowired SqlSessionFactory sqlSessionFactory;
103+
@Autowired DummyEntityRepository repository;
104+
105+
@Test // DATAJDBC-178
106+
public void myBatisGetsUsedForInsertAndSelect() {
107+
108+
DummyEntity entity = new DummyEntity(null, "some name");
109+
DummyEntity saved = repository.save(entity);
110+
111+
assertThat(saved.id).isNotNull();
112+
113+
DummyEntity reloaded = repository.findById(saved.id).orElseThrow(AssertionFailedError::new);
114+
115+
assertThat(reloaded.name).isEqualTo("name " + saved.id);
116+
}
117+
118+
interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {
119+
}
120+
121+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CREATE TABLE dummyentity(id BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY);
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<!DOCTYPE mapper
3+
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4+
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5+
<mapper namespace="org.springframework.data.jdbc.mybatis.mapper.DummyEntityMapper">
6+
<resultMap id="dummyEntityMap" type="DummyEntity">
7+
<constructor>
8+
<idArg column="id" javaType="long"/>
9+
<arg column="name" javaType="String"/>
10+
</constructor>
11+
</resultMap>
12+
<insert id="insert" parameterType="MyBatisContext" useGeneratedKeys="true" keyProperty="instance.id" keyColumn="ID">
13+
INSERT INTO DummyEntity (id) VALUES (DEFAULT)
14+
</insert>
15+
<select id="findById" resultType="MyBatisContext" resultMap="dummyEntityMap">
16+
SELECT
17+
id,
18+
'name ' || id as name
19+
FROM DummyEntity
20+
WHERE id = #{id}
21+
</select>
22+
</mapper>

0 commit comments

Comments
 (0)