diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/support/MybatisContext.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/support/MybatisContext.java new file mode 100644 index 0000000000..7273447fd1 --- /dev/null +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/support/MybatisContext.java @@ -0,0 +1,22 @@ +package org.springframework.data.jdbc.mybatis.support; + +import org.mybatis.spring.SqlSessionTemplate; +import org.springframework.lang.Nullable; + +/** + * @author Songling.Dong + * Created on 2019/5/15. + */ +public interface MybatisContext { + + + @Nullable + default SqlSessionTemplate getSqlSessionTemplate() { + return null; + } + + MybatisContext EMPTY = new MybatisContext() { + + + }; +} diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/support/MybatisQuery.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/support/MybatisQuery.java new file mode 100644 index 0000000000..ee34c18fe2 --- /dev/null +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/support/MybatisQuery.java @@ -0,0 +1,16 @@ +package org.springframework.data.jdbc.mybatis.support; + +import org.springframework.data.annotation.QueryAnnotation; + +import java.lang.annotation.*; + +/** + * @author Songling.Dong + * Created on 2019/5/8. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@QueryAnnotation +@Documented +public @interface MybatisQuery { +} diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/support/MybatisQueryMethod.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/support/MybatisQueryMethod.java new file mode 100644 index 0000000000..5a13291096 --- /dev/null +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/support/MybatisQueryMethod.java @@ -0,0 +1,45 @@ +package org.springframework.data.jdbc.mybatis.support; + +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.data.jdbc.repository.query.Modifying; +import org.springframework.data.projection.ProjectionFactory; +import org.springframework.data.repository.core.RepositoryMetadata; +import org.springframework.data.repository.query.QueryMethod; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * @author Songling.Dong + * Created on 2019/5/9. + */ +public class MybatisQueryMethod extends QueryMethod { + + private final Method method; + private final Object mapper; + + /** + * Creates a new {@link QueryMethod} from the given parameters. Looks up the correct query to use for following + * invocations of the method given. + * + * @param method must not be {@literal null}. + * @param metadata must not be {@literal null}. + * @param factory must not be {@literal null}. + */ + public MybatisQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory, Object mapper) { + super(method, metadata, factory); + this.method = method; + this.mapper = mapper; + } + + + + Object invoke(Object[] args) throws InvocationTargetException, IllegalAccessException { + return method.invoke(mapper, args); + } + + @Override + public boolean isModifyingQuery() { + return AnnotationUtils.findAnnotation(method, Modifying.class) != null; + } +} diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/support/MybatisRepositoryQuery.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/support/MybatisRepositoryQuery.java new file mode 100644 index 0000000000..db3115212e --- /dev/null +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/support/MybatisRepositoryQuery.java @@ -0,0 +1,77 @@ +package org.springframework.data.jdbc.mybatis.support; + +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.data.relational.core.mapping.RelationalMappingContext; +import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; +import org.springframework.data.relational.core.mapping.event.AfterLoadEvent; +import org.springframework.data.relational.core.mapping.event.Identifier; +import org.springframework.data.repository.query.RepositoryQuery; +import org.springframework.lang.Nullable; + +import java.lang.reflect.InvocationTargetException; + +/** + * @author Songling.Dong + * Created on 2019/5/9. + */ +public class MybatisRepositoryQuery implements RepositoryQuery { + + + private final ApplicationEventPublisher publisher; + private final RelationalMappingContext context; + private final MybatisQueryMethod mybatisQueryMethod; + + public MybatisRepositoryQuery(ApplicationEventPublisher publisher, RelationalMappingContext context, MybatisQueryMethod mybatisQueryMethod) { + this.publisher = publisher; + this.context = context; + this.mybatisQueryMethod = mybatisQueryMethod; + } + + @Override + public Object execute(Object[] parameters) { + try { + Object retVal = mybatisQueryMethod.invoke(parameters); + if (!mybatisQueryMethod.isModifyingQuery()) { + if (mybatisQueryMethod.isCollectionQuery() || mybatisQueryMethod.isStreamQuery()) { + publishAfterLoad((Iterable) retVal); + } else { + publishAfterLoad(retVal); + } + } + return retVal; + } catch (InvocationTargetException e) { + throw new RuntimeException(e.getTargetException()); + } catch (IllegalAccessException e) { + throw new RuntimeException("invoke from mybatis mapper failed", e); + } + } + + @Override + public MybatisQueryMethod getQueryMethod() { + return mybatisQueryMethod; + } + + + private void publishAfterLoad(Iterable all) { + + for (T e : all) { + publishAfterLoad(e); + } + } + + private void publishAfterLoad(@Nullable T entity) { + //duplicate code. + if (entity != null && context.hasPersistentEntityFor(entity.getClass())) { + + RelationalPersistentEntity e = context.getRequiredPersistentEntity(entity.getClass()); + Object identifier = e.getIdentifierAccessor(entity) + .getIdentifier(); + + if (identifier != null) { + publisher.publishEvent(new AfterLoadEvent(Identifier.of(identifier), entity)); + } + } + + } + +} diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/support/package-info.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/support/package-info.java new file mode 100644 index 0000000000..98290f2286 --- /dev/null +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/support/package-info.java @@ -0,0 +1,4 @@ +@NonNullApi +package org.springframework.data.jdbc.mybatis.support; + +import org.springframework.lang.NonNullApi; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java index 8d0ccf62bf..4d8caf5db3 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java @@ -15,12 +15,15 @@ */ package org.springframework.data.jdbc.repository.support; -import java.lang.reflect.Method; - +import org.mybatis.spring.SqlSessionTemplate; import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.jdbc.core.convert.DataAccessStrategy; import org.springframework.data.jdbc.core.convert.EntityRowMapper; import org.springframework.data.jdbc.core.convert.JdbcConverter; +import org.springframework.data.jdbc.mybatis.support.MybatisContext; +import org.springframework.data.jdbc.mybatis.support.MybatisQuery; +import org.springframework.data.jdbc.mybatis.support.MybatisQueryMethod; +import org.springframework.data.jdbc.mybatis.support.MybatisRepositoryQuery; import org.springframework.data.jdbc.repository.QueryMappingConfiguration; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.relational.core.mapping.RelationalMappingContext; @@ -34,6 +37,8 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.util.Assert; +import java.lang.reflect.Method; + /** * {@link QueryLookupStrategy} for JDBC repositories. Currently only supports annotated queries. * @@ -45,83 +50,105 @@ */ class JdbcQueryLookupStrategy implements QueryLookupStrategy { - private final ApplicationEventPublisher publisher; - private final RelationalMappingContext context; - private final JdbcConverter converter; - private final DataAccessStrategy accessStrategy; - private final QueryMappingConfiguration queryMappingConfiguration; - private final NamedParameterJdbcOperations operations; - - /** - * Creates a new {@link JdbcQueryLookupStrategy} for the given {@link RelationalMappingContext}, - * {@link DataAccessStrategy} and {@link QueryMappingConfiguration}. - * - * @param publisher must not be {@literal null}. - * @param context must not be {@literal null}. - * @param converter must not be {@literal null}. - * @param accessStrategy must not be {@literal null}. - * @param queryMappingConfiguration must not be {@literal null}. - */ - JdbcQueryLookupStrategy(ApplicationEventPublisher publisher, RelationalMappingContext context, - JdbcConverter converter, DataAccessStrategy accessStrategy, QueryMappingConfiguration queryMappingConfiguration, - NamedParameterJdbcOperations operations) { - - Assert.notNull(publisher, "Publisher must not be null!"); - Assert.notNull(context, "RelationalMappingContext must not be null!"); - Assert.notNull(converter, "RelationalConverter must not be null!"); - Assert.notNull(accessStrategy, "DataAccessStrategy must not be null!"); - Assert.notNull(queryMappingConfiguration, "RowMapperMap must not be null!"); - - this.publisher = publisher; - this.context = context; - this.converter = converter; - this.accessStrategy = accessStrategy; - this.queryMappingConfiguration = queryMappingConfiguration; - this.operations = operations; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.QueryLookupStrategy#resolveQuery(java.lang.reflect.Method, org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.projection.ProjectionFactory, org.springframework.data.repository.core.NamedQueries) - */ - @Override - public RepositoryQuery resolveQuery(Method method, RepositoryMetadata repositoryMetadata, - ProjectionFactory projectionFactory, NamedQueries namedQueries) { - - JdbcQueryMethod queryMethod = new JdbcQueryMethod(method, repositoryMetadata, projectionFactory); - - RowMapper mapper = queryMethod.isModifyingQuery() ? null : createMapper(queryMethod); - - return new JdbcRepositoryQuery(publisher, context, queryMethod, operations, mapper); - } - - private RowMapper createMapper(JdbcQueryMethod queryMethod) { - - Class returnedObjectType = queryMethod.getReturnedObjectType(); - - RelationalPersistentEntity persistentEntity = context.getPersistentEntity(returnedObjectType); - - if (persistentEntity == null) { - return SingleColumnRowMapper.newInstance(returnedObjectType, converter.getConversionService()); - } - - return determineDefaultMapper(queryMethod); - } - - private RowMapper determineDefaultMapper(JdbcQueryMethod queryMethod) { - - Class domainType = queryMethod.getReturnedObjectType(); - RowMapper configuredQueryMapper = queryMappingConfiguration.getRowMapper(domainType); - - if (configuredQueryMapper != null) - return configuredQueryMapper; - - EntityRowMapper defaultEntityRowMapper = new EntityRowMapper<>( // - context.getRequiredPersistentEntity(domainType), // - // - converter, // - accessStrategy); - - return defaultEntityRowMapper; - } + private final ApplicationEventPublisher publisher; + private final RelationalMappingContext context; + private final JdbcConverter converter; + private final DataAccessStrategy accessStrategy; + private final QueryMappingConfiguration queryMappingConfiguration; + private final NamedParameterJdbcOperations operations; + + private MybatisContext mybatisContent; + + /** + * Creates a new {@link JdbcQueryLookupStrategy} for the given {@link RelationalMappingContext}, + * {@link DataAccessStrategy} and {@link QueryMappingConfiguration}. + * + * @param publisher must not be {@literal null}. + * @param context must not be {@literal null}. + * @param converter must not be {@literal null}. + * @param accessStrategy must not be {@literal null}. + * @param queryMappingConfiguration must not be {@literal null}. + */ + JdbcQueryLookupStrategy(ApplicationEventPublisher publisher, RelationalMappingContext context, + JdbcConverter converter, DataAccessStrategy accessStrategy, QueryMappingConfiguration queryMappingConfiguration, + NamedParameterJdbcOperations operations) { + + Assert.notNull(publisher, "Publisher must not be null!"); + Assert.notNull(context, "RelationalMappingContext must not be null!"); + Assert.notNull(converter, "RelationalConverter must not be null!"); + Assert.notNull(accessStrategy, "DataAccessStrategy must not be null!"); + Assert.notNull(queryMappingConfiguration, "RowMapperMap must not be null!"); + + this.publisher = publisher; + this.context = context; + this.converter = converter; + this.accessStrategy = accessStrategy; + this.queryMappingConfiguration = queryMappingConfiguration; + this.operations = operations; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.QueryLookupStrategy#resolveQuery(java.lang.reflect.Method, org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.projection.ProjectionFactory, org.springframework.data.repository.core.NamedQueries) + */ + @Override + public RepositoryQuery resolveQuery(Method method, RepositoryMetadata repositoryMetadata, + ProjectionFactory projectionFactory, NamedQueries namedQueries) { + + + if (method.isAnnotationPresent(MybatisQuery.class)) { + return createMybatisRepositoryQuery(method, repositoryMetadata, projectionFactory); + } else { + JdbcQueryMethod queryMethod = new JdbcQueryMethod(method, repositoryMetadata, projectionFactory); + + RowMapper mapper = queryMethod.isModifyingQuery() ? null : createMapper(queryMethod); + + return new JdbcRepositoryQuery(publisher, context, queryMethod, operations, mapper); + } + + } + + private RepositoryQuery createMybatisRepositoryQuery(Method method, RepositoryMetadata repositoryMetadata, ProjectionFactory projectionFactory) { + SqlSessionTemplate sqlSession = mybatisContent.getSqlSessionTemplate(); + if (sqlSession == null) { + throw new IllegalStateException(String.format("You have annotated @MybatisQuery on method:%s ,but no org.mybatis.spring.SqlSessionTemplate provided.", method.getName())); + } + Object mapper = sqlSession.getMapper(method.getDeclaringClass()); + MybatisQueryMethod mybatisQueryMethod = new MybatisQueryMethod(method, repositoryMetadata, projectionFactory, mapper); + return new MybatisRepositoryQuery(publisher, context, mybatisQueryMethod); + } + + private RowMapper createMapper(JdbcQueryMethod queryMethod) { + + Class returnedObjectType = queryMethod.getReturnedObjectType(); + + RelationalPersistentEntity persistentEntity = context.getPersistentEntity(returnedObjectType); + + if (persistentEntity == null) { + return SingleColumnRowMapper.newInstance(returnedObjectType, converter.getConversionService()); + } + + return determineDefaultMapper(queryMethod); + } + + private RowMapper determineDefaultMapper(JdbcQueryMethod queryMethod) { + + Class domainType = queryMethod.getReturnedObjectType(); + RowMapper configuredQueryMapper = queryMappingConfiguration.getRowMapper(domainType); + + if (configuredQueryMapper != null) + return configuredQueryMapper; + + EntityRowMapper defaultEntityRowMapper = new EntityRowMapper<>( // + context.getRequiredPersistentEntity(domainType), // + // + converter, // + accessStrategy); + + return defaultEntityRowMapper; + } + + public void setMybatisContext(MybatisContext mybatisContent) { + this.mybatisContent = mybatisContent; + } } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java index ba301d4e7d..aed4774827 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java @@ -15,12 +15,11 @@ */ package org.springframework.data.jdbc.repository.support; -import java.util.Optional; - import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.jdbc.core.JdbcAggregateTemplate; import org.springframework.data.jdbc.core.convert.DataAccessStrategy; import org.springframework.data.jdbc.core.convert.JdbcConverter; +import org.springframework.data.jdbc.mybatis.support.MybatisContext; import org.springframework.data.jdbc.repository.QueryMappingConfiguration; import org.springframework.data.jdbc.repository.RowMapperMap; import org.springframework.data.relational.core.mapping.RelationalMappingContext; @@ -36,6 +35,8 @@ import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import java.util.Optional; + /** * Creates repository implementation based on JDBC. * @@ -54,6 +55,8 @@ public class JdbcRepositoryFactory extends RepositoryFactorySupport { private QueryMappingConfiguration queryMappingConfiguration = QueryMappingConfiguration.EMPTY; + private MybatisContext mybatisContext = MybatisContext.EMPTY; + /** * Creates a new {@link JdbcRepositoryFactory} for the given {@link DataAccessStrategy}, * {@link RelationalMappingContext} and {@link ApplicationEventPublisher}. @@ -98,6 +101,10 @@ public void setRowMapperMap(RowMapperMap rowMapperMap) { setQueryMappingConfiguration(rowMapperMap); } + public void setMyBatisContext(MybatisContext mybatisContext){ + this.mybatisContext = mybatisContext; + } + @SuppressWarnings("unchecked") @Override public EntityInformation getEntityInformation(Class aClass) { @@ -143,7 +150,11 @@ protected Optional getQueryLookupStrategy(@Nullable QueryLo throw new IllegalArgumentException(String.format("Unsupported query lookup strategy %s!", key)); } - return Optional.of(new JdbcQueryLookupStrategy(publisher, context, converter, accessStrategy, - queryMappingConfiguration, operations)); + JdbcQueryLookupStrategy jdbcQueryLookupStrategy = new JdbcQueryLookupStrategy(publisher, context, converter, accessStrategy, + queryMappingConfiguration, operations); + jdbcQueryLookupStrategy.setMybatisContext(mybatisContext); + return Optional.of(jdbcQueryLookupStrategy); } + + } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java index 2948d68011..4007a95066 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java @@ -15,8 +15,7 @@ */ package org.springframework.data.jdbc.repository.support; -import java.io.Serializable; - +import org.mybatis.spring.SqlSessionTemplate; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; @@ -25,6 +24,7 @@ import org.springframework.data.jdbc.core.convert.DefaultDataAccessStrategy; import org.springframework.data.jdbc.core.convert.JdbcConverter; import org.springframework.data.jdbc.core.convert.SqlGeneratorSource; +import org.springframework.data.jdbc.mybatis.support.MybatisContext; import org.springframework.data.jdbc.repository.QueryMappingConfiguration; import org.springframework.data.jdbc.repository.RowMapperMap; import org.springframework.data.relational.core.mapping.RelationalMappingContext; @@ -34,6 +34,8 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.util.Assert; +import java.io.Serializable; + /** * Special adapter for Springs {@link org.springframework.beans.factory.FactoryBean} interface to allow easy setup of * repository factories via Spring configuration. @@ -53,6 +55,7 @@ public class JdbcRepositoryFactoryBean, S, ID extend private JdbcConverter converter; private DataAccessStrategy dataAccessStrategy; private QueryMappingConfiguration queryMappingConfiguration = QueryMappingConfiguration.EMPTY; + private MybatisContext mybatisContext = MybatisContext.EMPTY; private NamedParameterJdbcOperations operations; /** @@ -85,7 +88,7 @@ protected RepositoryFactorySupport doCreateRepositoryFactory() { JdbcRepositoryFactory jdbcRepositoryFactory = new JdbcRepositoryFactory(dataAccessStrategy, mappingContext, converter, publisher, operations); jdbcRepositoryFactory.setQueryMappingConfiguration(queryMappingConfiguration); - + jdbcRepositoryFactory.setMyBatisContext(mybatisContext); return jdbcRepositoryFactory; } @@ -132,6 +135,19 @@ public void setConverter(JdbcConverter converter) { this.converter = converter; } + /** + * for mybatis support + */ + @Autowired(required = false) + public void setSqlSession(SqlSessionTemplate sqlSessionTemplate){ + this.mybatisContext = new MybatisContext() { + @Override + public SqlSessionTemplate getSqlSessionTemplate() { + return sqlSessionTemplate; + } + }; + } + @Override public void setBeanFactory(BeanFactory beanFactory) { @@ -168,6 +184,10 @@ public void afterPropertiesSet() { this.queryMappingConfiguration = QueryMappingConfiguration.EMPTY; } + if(this.mybatisContext == null){ + this.mybatisContext = MybatisContext.EMPTY; + } + super.afterPropertiesSet(); } } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/DummyEntity.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/DummyEntity.java index a9f2e92a63..2e08f8d750 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/DummyEntity.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/DummyEntity.java @@ -24,10 +24,11 @@ * @author Jens Schauder */ @Alias("DummyEntity") -class DummyEntity { +public class DummyEntity { - @Wither @Id final Long id; - final String name; + @Wither @Id + public final Long id; + public final String name; public DummyEntity(Long id, String name) { this.id = id; diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MybatisMixedWithSpringDataJdbcIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MybatisMixedWithSpringDataJdbcIntegrationTests.java new file mode 100644 index 0000000000..59e86a290e --- /dev/null +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MybatisMixedWithSpringDataJdbcIntegrationTests.java @@ -0,0 +1,117 @@ +package org.springframework.data.jdbc.mybatis; + +import junit.framework.AssertionFailedError; +import org.apache.ibatis.session.Configuration; +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.mybatis.support.DummyEntityRepository; +import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories; +import org.springframework.data.jdbc.testing.TestConfiguration; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; +import org.springframework.test.context.ActiveProfiles; +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; + +import java.io.IOException; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Songling.Dong + * Created on 2019/5/15. + * Tests the integration for Mybatis mixed with spring-data-jdbc + */ +@ContextConfiguration +@ActiveProfiles("hsql") +@Transactional +public class MybatisMixedWithSpringDataJdbcIntegrationTests { + + + @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); + + DummyEntity reloadedOptionalByMybatis = repository.notSpringDataNamingConventionsFindByIdOptional(saved.id).orElseThrow(AssertionFailedError::new); + + assertThat(reloaded.name).isEqualTo(reloadedOptionalByMybatis.name); + assertThat(reloaded.id).isEqualTo(reloadedOptionalByMybatis.id); + + + String newName = "John doe"; + boolean updateSuccess = repository.notSpringDataNamingConventionsUpdateById(saved.id, newName); + assertThat(updateSuccess).isTrue(); + DummyEntity dummyEntityWithNewName = repository.findById(saved.id).orElseThrow(AssertionFailedError::new); + assertThat(dummyEntityWithNewName.name).isEqualTo(newName); + + String newName2 = "John doe DSL"; + int updateRowCount = repository.notSpringDataNamingConventionsUpdateByIdReturnUpdatedRowCount(saved.id, newName2); + assertThat(updateRowCount).isEqualTo(1); + DummyEntity dummyEntityWithNewName2 = repository.findById(saved.id).orElseThrow(AssertionFailedError::new); + assertThat(dummyEntityWithNewName2.name).isEqualTo(newName2); + } + + @org.springframework.context.annotation.Configuration + @Import(TestConfiguration.class) + @EnableJdbcRepositories + static class Config { + + @Bean + Class testClass() { + return MybatisMixedWithSpringDataJdbcIntegrationTests.class; + } + + @Bean + SqlSessionFactoryBean createSessionFactory(EmbeddedDatabase db) throws IOException { + + Configuration configuration = new Configuration(); + configuration.getTypeAliasRegistry().registerAlias("MyBatisContext", MyBatisContext.class); + configuration.getTypeAliasRegistry().registerAlias(DummyEntity.class); + + configuration.setMapUnderscoreToCamelCase(true); + + SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); + sqlSessionFactoryBean.setDataSource(db); + sqlSessionFactoryBean.setConfiguration(configuration); + sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver() + .getResources("classpath*:org/springframework/data/jdbc/mybatis/support/*.xml")); + + return sqlSessionFactoryBean; + } + + @Bean + SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory factory) { + return new SqlSessionTemplate(factory); + } + + + } + + +} + diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/support/DummyEntityRepository.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/support/DummyEntityRepository.java new file mode 100644 index 0000000000..3a1db6fc4f --- /dev/null +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/support/DummyEntityRepository.java @@ -0,0 +1,25 @@ +package org.springframework.data.jdbc.mybatis.support; + +import org.apache.ibatis.annotations.Param; +import org.springframework.data.jdbc.mybatis.DummyEntity; +import org.springframework.data.jdbc.repository.query.Modifying; +import org.springframework.data.repository.CrudRepository; + +import java.util.Optional; + +public interface DummyEntityRepository extends CrudRepository { + + @MybatisQuery + DummyEntity notSpringDataNamingConventionsFindById(@Param("id") Long id); + + @MybatisQuery + Optional notSpringDataNamingConventionsFindByIdOptional(@Param("id") Long id); + + @Modifying + @MybatisQuery + boolean notSpringDataNamingConventionsUpdateById(@Param("id") Long id, @Param("newName") String newName); + + @Modifying + @MybatisQuery + int notSpringDataNamingConventionsUpdateByIdReturnUpdatedRowCount(@Param("id") Long id, @Param("newName") String newName); +} \ No newline at end of file diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.mybatis/MybatisMixedWithSpringDataJdbcIntegrationTests-hsql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.mybatis/MybatisMixedWithSpringDataJdbcIntegrationTests-hsql.sql new file mode 100644 index 0000000000..69de166366 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.mybatis/MybatisMixedWithSpringDataJdbcIntegrationTests-hsql.sql @@ -0,0 +1 @@ +CREATE TABLE DUMMY_ENTITY(id BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, name VARCHAR(500)); \ No newline at end of file diff --git a/spring-data-jdbc/src/test/resources/org/springframework/data/jdbc/mybatis/support/DummyEntityRepository.xml b/spring-data-jdbc/src/test/resources/org/springframework/data/jdbc/mybatis/support/DummyEntityRepository.xml new file mode 100644 index 0000000000..753d00b136 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org/springframework/data/jdbc/mybatis/support/DummyEntityRepository.xml @@ -0,0 +1,31 @@ + + + + + UPDATE Dummy_Entity + SET name = #{newName} + WHERE id = #{id} + + + UPDATE Dummy_Entity + SET name = #{newName} + WHERE id = #{id} + + + + + \ No newline at end of file