From 4af67673898c432f3bbeab3fb24229ea1e2f69fe Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Sun, 7 Jan 2018 21:45:48 +0900 Subject: [PATCH] DATAJDBC-161: Share an SqlSession into a same transaction --- .../mybatis/MyBatisDataAccessStrategy.java | 13 +- .../MyBatisDataAccessStrategyUnitTests.java | 16 ++- .../data/jdbc/mybatis/DummyEntity.java | 5 +- .../MyBatisBatchHsqlIntegrationTests.java | 126 ++++++++++++++++++ .../mybatis/MyBatisHsqlIntegrationTests.java | 4 +- .../MyBatisBatchHsqlIntegrationTests-hsql.sql | 1 + .../data/jdbc/mybatis/DummyEntityMapper.xml | 10 +- 7 files changed, 166 insertions(+), 9 deletions(-) create mode 100644 src/test/java/org/springframework/data/jdbc/mybatis/MyBatisBatchHsqlIntegrationTests.java create mode 100644 src/test/resources/org.springframework.data.jdbc.mybatis/MyBatisBatchHsqlIntegrationTests-hsql.sql 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 d9e041bfca..3432756d2f 100644 --- a/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java +++ b/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java @@ -20,6 +20,7 @@ import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionTemplate; import org.springframework.data.jdbc.core.DataAccessStrategy; import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty; import org.springframework.data.mapping.PropertyPath; @@ -38,16 +39,22 @@ * For methods taking a {@link PropertyPath} the entityTyoe if the context is set to the class of the leaf type. * * @author Jens Schauder + * @author Kazuki Shimizu */ public class MyBatisDataAccessStrategy implements DataAccessStrategy { private static final String MAPPER_SUFFIX = "Mapper"; - private final SqlSessionFactory sqlSessionFactory; + private final SqlSession sqlSession; public MyBatisDataAccessStrategy(SqlSessionFactory sqlSessionFactory) { - this.sqlSessionFactory = sqlSessionFactory; + this(new SqlSessionTemplate(sqlSessionFactory)); + } + + public MyBatisDataAccessStrategy(SqlSessionTemplate sqlSessionTemplate) { + + this.sqlSession = sqlSessionTemplate; } @Override @@ -139,6 +146,6 @@ private String mapper(Class domainType) { } private SqlSession sqlSession() { - return sqlSessionFactory.openSession(); + return this.sqlSession; } } diff --git a/src/test/java/org/springframework/data/jdbc/core/MyBatisDataAccessStrategyUnitTests.java b/src/test/java/org/springframework/data/jdbc/core/MyBatisDataAccessStrategyUnitTests.java index b169b13ee0..de23613c01 100644 --- a/src/test/java/org/springframework/data/jdbc/core/MyBatisDataAccessStrategyUnitTests.java +++ b/src/test/java/org/springframework/data/jdbc/core/MyBatisDataAccessStrategyUnitTests.java @@ -21,12 +21,17 @@ import java.util.Collections; +import javax.sql.DataSource; + +import org.apache.ibatis.mapping.Environment; +import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; +import org.mybatis.spring.transaction.SpringManagedTransactionFactory; import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty; import org.springframework.data.jdbc.mybatis.MyBatisContext; import org.springframework.data.jdbc.mybatis.MyBatisDataAccessStrategy; @@ -36,20 +41,27 @@ * Unit tests for the {@link MyBatisDataAccessStrategy}, mainly ensuring that the correct statements get's looked up. * * @author Jens Schauder + * @author Kazuki Shimizu */ public class MyBatisDataAccessStrategyUnitTests { + DataSource dataSource = mock(DataSource.class); SqlSessionFactory sessionFactory = mock(SqlSessionFactory.class); SqlSession session = mock(SqlSession.class); ArgumentCaptor captor = ArgumentCaptor.forClass(MyBatisContext.class); - MyBatisDataAccessStrategy accessStrategy = new MyBatisDataAccessStrategy(sessionFactory); + MyBatisDataAccessStrategy accessStrategy; @Before public void before() { - doReturn(session).when(sessionFactory).openSession(); + Configuration configuration = new Configuration(); + Environment environment = new Environment("unittest", new SpringManagedTransactionFactory(), dataSource); + configuration.setEnvironment(environment); + doReturn(configuration).when(sessionFactory).getConfiguration(); + doReturn(session).when(sessionFactory).openSession(configuration.getDefaultExecutorType()); doReturn(false).when(session).selectOne(any(), any()); + this.accessStrategy = new MyBatisDataAccessStrategy(sessionFactory); } @Test // DATAJDBC-123 diff --git a/src/test/java/org/springframework/data/jdbc/mybatis/DummyEntity.java b/src/test/java/org/springframework/data/jdbc/mybatis/DummyEntity.java index 3a1e96ba83..3c646ce97a 100644 --- a/src/test/java/org/springframework/data/jdbc/mybatis/DummyEntity.java +++ b/src/test/java/org/springframework/data/jdbc/mybatis/DummyEntity.java @@ -24,9 +24,12 @@ @Alias("DummyEntity") class DummyEntity { - @Id final Long id; + @Id final long id; final String name; + public DummyEntity(String name) { + this(0L, name); + } public DummyEntity(Long id, String name) { this.id = id; this.name = name; diff --git a/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisBatchHsqlIntegrationTests.java b/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisBatchHsqlIntegrationTests.java new file mode 100644 index 0000000000..4b6ecbaa4a --- /dev/null +++ b/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisBatchHsqlIntegrationTests.java @@ -0,0 +1,126 @@ +/* + * Copyright 2017 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.util.Iterator; + +import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.session.ExecutorType; +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.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.junit4.rules.SpringClassRule; +import org.springframework.test.context.junit4.rules.SpringMethodRule; +import org.springframework.transaction.annotation.Transactional; + +/** + * Tests the integration with Mybatis batch mode. + * + * @author Kazuki Shimizu + */ +@ContextConfiguration +@Transactional +public class MyBatisBatchHsqlIntegrationTests { + + @org.springframework.context.annotation.Configuration + @Import(TestConfiguration.class) + @EnableJdbcRepositories(considerNestedRepositories = true) + static class Config { + + @Bean + Class testClass() { + return MyBatisBatchHsqlIntegrationTests.class; + } + + @Bean + SqlSessionFactoryBean createSessionFactory(EmbeddedDatabase db) { + + Configuration configuration = new Configuration(); + configuration.setDefaultExecutorType(ExecutorType.BATCH); + configuration.getTypeAliasRegistry().registerAlias("MyBatisContext", MyBatisContext.class); + + configuration.getTypeAliasRegistry().registerAlias(DummyEntity.class); + configuration.addMapper(DummyEntityMapper.class); + + SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); + sqlSessionFactoryBean.setDataSource(db); + sqlSessionFactoryBean.setConfiguration(configuration); + + return sqlSessionFactoryBean; + } + + @Bean + SqlSessionTemplate SqlSessionTemplate(SqlSessionFactory factory) { + return new SqlSessionTemplate(factory); + } + + @Bean + MyBatisDataAccessStrategy dataAccessStrategy(SqlSessionTemplate template) { + return new MyBatisDataAccessStrategy(template); + } + } + + @ClassRule public static final SpringClassRule classRule = new SpringClassRule(); + @Rule public SpringMethodRule methodRule = new SpringMethodRule(); + + @Autowired DummyEntityRepository repository; + + @Test + public void batchInsertAndSelect() { + + DummyEntity entity1 = new DummyEntity("some name"); + DummyEntity saved1 = repository.save(entity1); + + DummyEntity entity2 = new DummyEntity("some name"); + DummyEntity saved2 = repository.save(entity2); + + assertThat(saved1.id).isEqualTo(0); + assertThat(saved2.id).isEqualTo(0); + + Iterator loadedEntities = repository.findAll().iterator(); + + DummyEntity loaded1 = loadedEntities.next(); + assertThat(saved1.id).isNotEqualTo(0); + assertThat(loaded1.id).isEqualTo(saved1.id); + assertThat(loaded1).isNotNull().extracting(e -> e.id, e -> e.name); + + DummyEntity loaded2 = loadedEntities.next(); + assertThat(saved2.id).isNotEqualTo(0); + assertThat(loaded2.id).isEqualTo(saved2.id); + assertThat(loaded2).isNotNull().extracting(e -> e.id, e -> e.name); + + assertThat(loadedEntities.hasNext()).isFalse(); + + } + + interface DummyEntityRepository extends CrudRepository { + + } + +} diff --git a/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisHsqlIntegrationTests.java b/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisHsqlIntegrationTests.java index 3dbd418035..d294d89386 100644 --- a/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisHsqlIntegrationTests.java +++ b/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisHsqlIntegrationTests.java @@ -97,10 +97,10 @@ public void mybatisSelfTest() { @Test // DATAJDBC-123 public void myBatisGetsUsedForInsertAndSelect() { - DummyEntity entity = new DummyEntity(null, "some name"); + DummyEntity entity = new DummyEntity("some name"); DummyEntity saved = repository.save(entity); - assertThat(saved.id).isNotNull(); + assertThat(saved.id).isNotEqualTo(0); DummyEntity reloaded = repository.findById(saved.id).orElseThrow(AssertionFailedError::new); diff --git a/src/test/resources/org.springframework.data.jdbc.mybatis/MyBatisBatchHsqlIntegrationTests-hsql.sql b/src/test/resources/org.springframework.data.jdbc.mybatis/MyBatisBatchHsqlIntegrationTests-hsql.sql new file mode 100644 index 0000000000..a2f6eb9021 --- /dev/null +++ b/src/test/resources/org.springframework.data.jdbc.mybatis/MyBatisBatchHsqlIntegrationTests-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/DummyEntityMapper.xml b/src/test/resources/org/springframework/data/jdbc/mybatis/DummyEntityMapper.xml index c2e00541c5..d3718b5dd0 100644 --- a/src/test/resources/org/springframework/data/jdbc/mybatis/DummyEntityMapper.xml +++ b/src/test/resources/org/springframework/data/jdbc/mybatis/DummyEntityMapper.xml @@ -12,11 +12,19 @@ INSERT INTO DummyEntity (id) VALUES (DEFAULT) - SELECT id, 'Name based on an id' || id AS name FROM DummyEntity WHERE id = #{id} + + \ No newline at end of file