├── .gitignore ├── README.md ├── pom.xml └── src ├── main └── java │ └── com │ └── github │ └── lkqm │ └── spring │ └── jpa │ └── repository │ ├── MybatisQuery.java │ ├── config │ ├── EnableMybatisJpaRepositories.java │ ├── MybatisJpaRepositoriesRegistrar.java │ └── MybatisJpaRepositoryConfigExtension.java │ ├── query │ ├── MyBatisJpaQueryLookupStrategy.java │ ├── MyBatisQueryMethod.java │ └── MyBatisRepositoryQuery.java │ └── support │ ├── MybatisJpaRepositoryFactory.java │ └── MybatisJpaRepositoryFactoryBean.java └── test ├── java └── com │ └── github │ └── lkqm │ └── spring │ └── jpa │ └── repository │ └── demo │ ├── MybatisJpaApplication.java │ ├── domain │ └── User.java │ └── repository │ ├── UserRepository.java │ └── UserRepositoryTest.java └── resources ├── application.properties └── mapper └── UserRepository.xml /.gitignore: -------------------------------------------------------------------------------- 1 | # maven ignore 2 | target/ 3 | *.jar 4 | !.mvn/wrapper/* 5 | *.war 6 | *.zip 7 | *.tar 8 | *.tar.gz 9 | 10 | # eclipse ignore 11 | .settings/ 12 | .project 13 | .classpath 14 | 15 | # idea ignore 16 | .idea/ 17 | *.ipr 18 | *.iml 19 | *.iws 20 | 21 | # temp ignore 22 | *.log 23 | *.cache 24 | *.diff 25 | *.patch 26 | *.tmp 27 | 28 | # system ignore 29 | .DS_Store 30 | Thumbs.db 31 | *.orig 32 | *.out 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # spring-data-jpa-mybatis 2 | 集成spring-data-jpa和mybatis,支持在同一个接口中定义方法. 3 | 4 | ```java 5 | public interface UserRepository extends JpaRepository { 6 | // 只有该方法会由mybatis执行,其余方法按照jpa方式正常处理 7 | @MybatisQuery 8 | User selectByAccount(String account); 9 | } 10 | ``` 11 | 12 | ## 快速开始 13 | 参见单元测试 14 | 1. 添加依赖 15 | ```xml 16 | 17 | com.github.lkqm 18 | spring-data-jpa-mybatis 19 | 2.1.0 20 | 21 | ``` 22 | 23 | 2. 配置application.properties 24 | ```properties 25 | spring.datasource.url=jdbc:h2:mem:spring-data-jpa-mybatis;DB_CLOSE_ON_EXIT=TRUE 26 | spring.jpa.show-sql=true 27 | spring.jpa.format-sql=true 28 | # mybatis 29 | mybatis.type-aliases-package=com.mario6.springdata.jpa.mybatis.repository.demo.domain 30 | mybatis.mapper-locations=classpath*:mapper/*.xml 31 | mybatis.configuration.map-underscore-to-camel-case=true 32 | ``` 33 | 3. 启动类添加注解 34 | ```java 35 | @SpringBootApplication 36 | @EnableMybatisJpaRepositories 37 | public class MybatisJpaApplication {} 38 | ``` 39 | 40 | 4. 相关方法 41 | ```java 42 | public interface UserRepository extends JpaRepository { 43 | @MybatisQuery 44 | User selectByAccount(String account); 45 | } 46 | ``` 47 | 48 | ## 说明 49 | 你可以按照正常使用spring-data-jpa使用, 只有`@MybatisQuery`注解的方法才会由mybatis处理, 完全同mybatis一样, @Param注解使用也需要是mybatis的。 50 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 2.3.9.RELEASE 10 | 11 | 12 | com.github.lkqm 13 | spring-data-jpa-mybatis 14 | 2.1.0 15 | 16 | 17 | 18 | 19 | org.springframework.boot 20 | spring-boot-starter-data-jpa 21 | 22 | 23 | org.mybatis.spring.boot 24 | mybatis-spring-boot-starter 25 | 2.1.4 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-test 31 | test 32 | 33 | 34 | com.h2database 35 | h2 36 | test 37 | 38 | 39 | 40 | 41 | 42 | release 43 | 44 | 45 | 46 | org.apache.maven.plugins 47 | maven-source-plugin 48 | 2.2.1 49 | 50 | 51 | attach-sources 52 | 53 | jar-no-fork 54 | 55 | 56 | 57 | 58 | 59 | org.apache.maven.plugins 60 | maven-javadoc-plugin 61 | 2.9.1 62 | 63 | 64 | attach-javadocs 65 | 66 | jar 67 | 68 | 69 | -Xdoclint:none 70 | 71 | 72 | 73 | 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-gpg-plugin 78 | 1.5 79 | 80 | 81 | sign-artifacts 82 | verify 83 | 84 | sign 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | org.sonatype.plugins 98 | nexus-staging-maven-plugin 99 | 1.6.7 100 | true 101 | 102 | ossrh 103 | https://oss.sonatype.org/ 104 | true 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | ossrh 113 | Nexus Releases Repository 114 | https://oss.sonatype.org/service/local/staging/deploy/maven2 115 | 116 | 117 | ossrh 118 | Nexus Snapshots Repository 119 | https://oss.sonatype.org/content/repositories/snapshots 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /src/main/java/com/github/lkqm/spring/jpa/repository/MybatisQuery.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.jpa.repository; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | import org.springframework.data.annotation.QueryAnnotation; 9 | 10 | /** 11 | * 标记方法由mybatis执行方法 12 | */ 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target({ElementType.METHOD}) 15 | @QueryAnnotation 16 | @Documented 17 | public @interface MybatisQuery { 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/github/lkqm/spring/jpa/repository/config/EnableMybatisJpaRepositories.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 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 | * https://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 com.github.lkqm.spring.jpa.repository.config; 17 | 18 | import com.github.lkqm.spring.jpa.repository.support.MybatisJpaRepositoryFactoryBean; 19 | import java.lang.annotation.Documented; 20 | import java.lang.annotation.ElementType; 21 | import java.lang.annotation.Inherited; 22 | import java.lang.annotation.Retention; 23 | import java.lang.annotation.RetentionPolicy; 24 | import java.lang.annotation.Target; 25 | import javax.persistence.EntityManagerFactory; 26 | import org.springframework.context.annotation.ComponentScan.Filter; 27 | import org.springframework.context.annotation.Import; 28 | import org.springframework.context.annotation.Lazy; 29 | import org.springframework.data.repository.config.BootstrapMode; 30 | import org.springframework.data.repository.config.DefaultRepositoryBaseClass; 31 | import org.springframework.data.repository.query.QueryLookupStrategy; 32 | import org.springframework.data.repository.query.QueryLookupStrategy.Key; 33 | import org.springframework.transaction.PlatformTransactionManager; 34 | 35 | /** 36 | * Annotation to enable JPA repositories. Will scan the package of the annotated configuration class for Spring Data 37 | * repositories by default. 38 | * 39 | * @author Oliver Gierke 40 | * @author Thomas Darimont 41 | */ 42 | @Target(ElementType.TYPE) 43 | @Retention(RetentionPolicy.RUNTIME) 44 | @Documented 45 | @Inherited 46 | @Import(MybatisJpaRepositoriesRegistrar.class) 47 | public @interface EnableMybatisJpaRepositories { 48 | 49 | 50 | //-------------jpa-mybatis---------------// 51 | Class repositoryFactoryBeanClass() default MybatisJpaRepositoryFactoryBean.class; 52 | 53 | String sqlSessionTemplateRef() default "sqlSessionTemplate"; 54 | 55 | /** 56 | * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation declarations e.g.: 57 | * {@code @EnableJpaRepositories("org.my.pkg")} instead of {@code @EnableJpaRepositories(basePackages="org.my.pkg")}. 58 | */ 59 | String[] value() default {}; 60 | 61 | /** 62 | * Base packages to scan for annotated components. {@link #value()} is an alias for (and mutually exclusive with) this 63 | * attribute. Use {@link #basePackageClasses()} for a type-safe alternative to String-based package names. 64 | */ 65 | String[] basePackages() default {}; 66 | 67 | /** 68 | * Type-safe alternative to {@link #basePackages()} for specifying the packages to scan for annotated components. The 69 | * package of each class specified will be scanned. Consider creating a special no-op marker class or interface in 70 | * each package that serves no purpose other than being referenced by this attribute. 71 | */ 72 | Class[] basePackageClasses() default {}; 73 | 74 | /** 75 | * Specifies which types are eligible for component scanning. Further narrows the set of candidate components from 76 | * everything in {@link #basePackages()} to everything in the base packages that matches the given filter or filters. 77 | */ 78 | Filter[] includeFilters() default {}; 79 | 80 | /** 81 | * Specifies which types are not eligible for component scanning. 82 | */ 83 | Filter[] excludeFilters() default {}; 84 | 85 | /** 86 | * Returns the postfix to be used when looking up custom repository implementations. Defaults to {@literal Impl}. So 87 | * for a repository named {@code PersonRepository} the corresponding implementation class will be looked up scanning 88 | * for {@code PersonRepositoryImpl}. 89 | * 90 | * @return 91 | */ 92 | String repositoryImplementationPostfix() default "Impl"; 93 | 94 | /** 95 | * Configures the location of where to find the Spring Data named queries properties file. Will default to 96 | * {@code META-INF/jpa-named-queries.properties}. 97 | * 98 | * @return 99 | */ 100 | String namedQueriesLocation() default ""; 101 | 102 | /** 103 | * Returns the key of the {@link QueryLookupStrategy} to be used for lookup queries for query methods. Defaults to 104 | * {@link Key#CREATE_IF_NOT_FOUND}. 105 | * 106 | * @return 107 | */ 108 | Key queryLookupStrategy() default Key.CREATE_IF_NOT_FOUND; 109 | 110 | /** 111 | * Configure the repository base class to be used to create repository proxies for this particular configuration. 112 | * 113 | * @return 114 | * @since 1.9 115 | */ 116 | Class repositoryBaseClass() default DefaultRepositoryBaseClass.class; 117 | 118 | // JPA specific configuration 119 | 120 | /** 121 | * Configures the name of the {@link EntityManagerFactory} bean definition to be used to create repositories 122 | * discovered through this annotation. Defaults to {@code entityManagerFactory}. 123 | * 124 | * @return 125 | */ 126 | String entityManagerFactoryRef() default "entityManagerFactory"; 127 | 128 | /** 129 | * Configures the name of the {@link PlatformTransactionManager} bean definition to be used to create repositories 130 | * discovered through this annotation. Defaults to {@code transactionManager}. 131 | * 132 | * @return 133 | */ 134 | String transactionManagerRef() default "transactionManager"; 135 | 136 | /** 137 | * Configures whether nested repository-interfaces (e.g. defined as inner classes) should be discovered by the 138 | * repositories infrastructure. 139 | */ 140 | boolean considerNestedRepositories() default false; 141 | 142 | /** 143 | * Configures whether to enable default transactions for Spring Data JPA repositories. Defaults to {@literal true}. If 144 | * disabled, repositories must be used behind a facade that's configuring transactions (e.g. using Spring's annotation 145 | * driven transaction facilities) or repository methods have to be used to demarcate transactions. 146 | * 147 | * @return whether to enable default transactions, defaults to {@literal true}. 148 | */ 149 | boolean enableDefaultTransactions() default true; 150 | 151 | /** 152 | * Configures when the repositories are initialized in the bootstrap lifecycle. {@link BootstrapMode#DEFAULT} 153 | * (default) means eager initialization except all repository interfaces annotated with {@link Lazy}, 154 | * {@link BootstrapMode#LAZY} means lazy by default including injection of lazy-initialization proxies into client 155 | * beans so that those can be instantiated but will only trigger the initialization upon first repository usage (i.e a 156 | * method invocation on it). This means repositories can still be uninitialized when the application context has 157 | * completed its bootstrap. {@link BootstrapMode#DEFERRED} is fundamentally the same as {@link BootstrapMode#LAZY}, 158 | * but triggers repository initialization when the application context finishes its bootstrap. 159 | * 160 | * @return 161 | * @since 2.1 162 | */ 163 | BootstrapMode bootstrapMode() default BootstrapMode.DEFAULT; 164 | 165 | /** 166 | * Configures what character is used to escape the wildcards {@literal _} and {@literal %} in derived queries with 167 | * {@literal contains}, {@literal startsWith} or {@literal endsWith} clauses. 168 | * 169 | * @return a single character used for escaping. 170 | */ 171 | char escapeCharacter() default '\\'; 172 | 173 | } 174 | -------------------------------------------------------------------------------- /src/main/java/com/github/lkqm/spring/jpa/repository/config/MybatisJpaRepositoriesRegistrar.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 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 | * https://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 com.github.lkqm.spring.jpa.repository.config; 17 | 18 | import java.lang.annotation.Annotation; 19 | import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; 20 | import org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport; 21 | import org.springframework.data.repository.config.RepositoryConfigurationExtension; 22 | 23 | /** 24 | * {@link ImportBeanDefinitionRegistrar} to enable {@link EnableMybatisJpaRepositories} annotation. 25 | * 26 | * @author Oliver Gierke 27 | */ 28 | class MybatisJpaRepositoriesRegistrar extends RepositoryBeanDefinitionRegistrarSupport { 29 | 30 | /* 31 | * (non-Javadoc) 32 | * @see org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport#getAnnotation() 33 | */ 34 | @Override 35 | protected Class getAnnotation() { 36 | return EnableMybatisJpaRepositories.class; 37 | } 38 | 39 | /* 40 | * (non-Javadoc) 41 | * @see org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport#getExtension() 42 | */ 43 | @Override 44 | protected RepositoryConfigurationExtension getExtension() { 45 | return new MybatisJpaRepositoryConfigExtension(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/github/lkqm/spring/jpa/repository/config/MybatisJpaRepositoryConfigExtension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2019 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 | * https://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 com.github.lkqm.spring.jpa.repository.config; 17 | 18 | import org.springframework.beans.factory.support.BeanDefinitionBuilder; 19 | import org.springframework.core.annotation.AnnotationAttributes; 20 | import org.springframework.data.jpa.repository.config.JpaRepositoryConfigExtension; 21 | import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource; 22 | import org.springframework.util.StringUtils; 23 | 24 | /** 25 | * JPA-MYBATIS自定义扩展配置 26 | */ 27 | public class MybatisJpaRepositoryConfigExtension extends JpaRepositoryConfigExtension { 28 | 29 | @Override 30 | public void postProcess(BeanDefinitionBuilder builder, AnnotationRepositoryConfigurationSource config) { 31 | super.postProcess(builder, config); 32 | postProcessMybatisJpa(builder, config); 33 | } 34 | 35 | private void postProcessMybatisJpa(BeanDefinitionBuilder builder, AnnotationRepositoryConfigurationSource config) { 36 | AnnotationAttributes attributes = config.getAttributes(); 37 | String sqlSessionTemplate = attributes.getString("sqlSessionTemplateRef"); 38 | if (!StringUtils.hasText(sqlSessionTemplate)) { 39 | sqlSessionTemplate = "sqlSessionTemplate"; 40 | } 41 | builder.addPropertyReference("sqlSessionTemplate", sqlSessionTemplate); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/github/lkqm/spring/jpa/repository/query/MyBatisJpaQueryLookupStrategy.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.jpa.repository.query; 2 | 3 | import com.github.lkqm.spring.jpa.repository.MybatisQuery; 4 | import java.lang.reflect.Method; 5 | import org.mybatis.spring.SqlSessionTemplate; 6 | import org.springframework.data.projection.ProjectionFactory; 7 | import org.springframework.data.repository.core.NamedQueries; 8 | import org.springframework.data.repository.core.RepositoryMetadata; 9 | import org.springframework.data.repository.query.QueryLookupStrategy; 10 | import org.springframework.data.repository.query.RepositoryQuery; 11 | import org.springframework.util.Assert; 12 | 13 | 14 | public class MyBatisJpaQueryLookupStrategy implements QueryLookupStrategy { 15 | 16 | private final SqlSessionTemplate sessionTemplate; 17 | private final QueryLookupStrategy jpaQueryLookupStrategy; 18 | 19 | public MyBatisJpaQueryLookupStrategy(SqlSessionTemplate sessionTemplate, QueryLookupStrategy jpaQueryLookupStrategy) { 20 | Assert.notNull(sessionTemplate, "SqlSessionTemplate must not be null"); 21 | Assert.notNull(jpaQueryLookupStrategy, "QueryLookupStrategy must not be null"); 22 | this.sessionTemplate = sessionTemplate; 23 | this.jpaQueryLookupStrategy = jpaQueryLookupStrategy; 24 | } 25 | 26 | @Override 27 | public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory, NamedQueries namedQueries) { 28 | if (isMethodToMybatisHandle(method)) { 29 | return resolveMybatisQuery(method, metadata, factory, namedQueries); 30 | } 31 | return jpaQueryLookupStrategy.resolveQuery(method, metadata, factory, namedQueries); 32 | } 33 | 34 | private boolean isMethodToMybatisHandle(Method method) { 35 | MybatisQuery mybatisQuery = method.getAnnotation(MybatisQuery.class); 36 | return mybatisQuery != null; 37 | } 38 | 39 | private RepositoryQuery resolveMybatisQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory, NamedQueries namedQueries) { 40 | MyBatisQueryMethod queryMethod = new MyBatisQueryMethod(method, metadata, factory); 41 | return new MyBatisRepositoryQuery(queryMethod, sessionTemplate); 42 | } 43 | 44 | /** 45 | * 创建查询策略 46 | */ 47 | public static QueryLookupStrategy create(QueryLookupStrategy jpaQueryLookupStrategy, SqlSessionTemplate sessionTemplate) { 48 | return new MyBatisJpaQueryLookupStrategy(sessionTemplate, jpaQueryLookupStrategy); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/github/lkqm/spring/jpa/repository/query/MyBatisQueryMethod.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.jpa.repository.query; 2 | 3 | import java.lang.reflect.Method; 4 | import org.springframework.data.projection.ProjectionFactory; 5 | import org.springframework.data.repository.core.RepositoryMetadata; 6 | import org.springframework.data.repository.query.QueryMethod; 7 | 8 | /** 9 | * 查询方法的抽象 10 | */ 11 | public class MyBatisQueryMethod extends QueryMethod { 12 | 13 | private final Class mapperInterface; 14 | private final Method method; 15 | 16 | public MyBatisQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory) { 17 | super(method, metadata, factory); 18 | this.method = method; 19 | mapperInterface = metadata.getRepositoryInterface(); 20 | } 21 | 22 | public Class getRepositoryInterface() { 23 | return mapperInterface; 24 | } 25 | 26 | public Method getMethod() { 27 | return method; 28 | } 29 | 30 | public String getNamedQueryName() { 31 | return null; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/github/lkqm/spring/jpa/repository/query/MyBatisRepositoryQuery.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.jpa.repository.query; 2 | 3 | import java.lang.reflect.Method; 4 | import org.mybatis.spring.SqlSessionTemplate; 5 | import org.springframework.data.repository.query.QueryMethod; 6 | import org.springframework.data.repository.query.RepositoryQuery; 7 | import org.springframework.util.Assert; 8 | import org.springframework.util.ReflectionUtils; 9 | 10 | /** 11 | * Mybatis的查询逻辑 12 | */ 13 | public class MyBatisRepositoryQuery implements RepositoryQuery { 14 | 15 | private final MyBatisQueryMethod queryMethod; 16 | private final SqlSessionTemplate sessionTemplate; 17 | 18 | public MyBatisRepositoryQuery(MyBatisQueryMethod queryMethod, SqlSessionTemplate sessionTemplate) { 19 | Assert.notNull(queryMethod, "MyBatisQueryMethod must not be null"); 20 | Assert.notNull(sessionTemplate, "SqlSessionTemplate must not be null"); 21 | this.queryMethod = queryMethod; 22 | this.sessionTemplate = sessionTemplate; 23 | } 24 | 25 | @Override 26 | public Object execute(Object[] parameters) { 27 | Method method = queryMethod.getMethod(); 28 | Object mapper = sessionTemplate.getMapper(queryMethod.getRepositoryInterface()); 29 | return ReflectionUtils.invokeMethod(method, mapper, parameters); 30 | } 31 | 32 | @Override 33 | public QueryMethod getQueryMethod() { 34 | return queryMethod; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/github/lkqm/spring/jpa/repository/support/MybatisJpaRepositoryFactory.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.jpa.repository.support; 2 | 3 | import com.github.lkqm.spring.jpa.repository.query.MyBatisJpaQueryLookupStrategy; 4 | import java.util.Optional; 5 | import javax.persistence.EntityManager; 6 | import org.mybatis.spring.SqlSessionTemplate; 7 | import org.springframework.data.jpa.repository.support.JpaRepositoryFactory; 8 | import org.springframework.data.repository.query.QueryLookupStrategy; 9 | import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; 10 | import org.springframework.util.Assert; 11 | 12 | public class MybatisJpaRepositoryFactory extends JpaRepositoryFactory { 13 | 14 | private SqlSessionTemplate sessionTemplate; 15 | 16 | public MybatisJpaRepositoryFactory(EntityManager entityManager, SqlSessionTemplate sessionTemplate) { 17 | super(entityManager); 18 | Assert.notNull(sessionTemplate, "SqlSessionTemplate must not be null"); 19 | this.sessionTemplate = sessionTemplate; 20 | } 21 | 22 | @Override 23 | protected Optional getQueryLookupStrategy(QueryLookupStrategy.Key key, QueryMethodEvaluationContextProvider evaluationContextProvider) { 24 | Optional jpaQueryLookupStrategy = super.getQueryLookupStrategy(key, evaluationContextProvider); 25 | return Optional.of(MyBatisJpaQueryLookupStrategy.create(jpaQueryLookupStrategy.get(), sessionTemplate)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/github/lkqm/spring/jpa/repository/support/MybatisJpaRepositoryFactoryBean.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.jpa.repository.support; 2 | 3 | import java.io.Serializable; 4 | import javax.persistence.EntityManager; 5 | import org.mybatis.spring.SqlSessionTemplate; 6 | import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean; 7 | import org.springframework.data.repository.Repository; 8 | import org.springframework.data.repository.core.support.RepositoryFactorySupport; 9 | import org.springframework.util.Assert; 10 | 11 | public class MybatisJpaRepositoryFactoryBean, S, ID extends Serializable> 12 | extends JpaRepositoryFactoryBean { 13 | 14 | private SqlSessionTemplate sqlSessionTemplate; 15 | 16 | public MybatisJpaRepositoryFactoryBean(Class repositoryInterface) { 17 | super(repositoryInterface); 18 | } 19 | 20 | @Override 21 | protected RepositoryFactorySupport createRepositoryFactory(EntityManager em) { 22 | return new MybatisJpaRepositoryFactory(em, sqlSessionTemplate); 23 | } 24 | 25 | public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) { 26 | this.sqlSessionTemplate = sqlSessionTemplate; 27 | } 28 | 29 | @Override 30 | public void afterPropertiesSet() { 31 | super.afterPropertiesSet(); 32 | Assert.state(this.sqlSessionTemplate != null, "SqlSessionTemplate must not be null!"); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/com/github/lkqm/spring/jpa/repository/demo/MybatisJpaApplication.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.jpa.repository.demo; 2 | 3 | import com.github.lkqm.spring.jpa.repository.config.EnableMybatisJpaRepositories; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | @SpringBootApplication 8 | @EnableMybatisJpaRepositories 9 | public class MybatisJpaApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(MybatisJpaApplication.class); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/com/github/lkqm/spring/jpa/repository/demo/domain/User.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.jpa.repository.demo.domain; 2 | 3 | 4 | import java.io.Serializable; 5 | import java.util.Date; 6 | import javax.persistence.Entity; 7 | import javax.persistence.GeneratedValue; 8 | import javax.persistence.Id; 9 | 10 | /** 11 | * 用户 12 | */ 13 | @Entity 14 | public class User implements Serializable { 15 | 16 | @Id 17 | @GeneratedValue 18 | private Integer id; 19 | 20 | /** 21 | * 账户 22 | */ 23 | private String account; 24 | 25 | /** 26 | * 密码 27 | */ 28 | private String password; 29 | 30 | /** 31 | * 名称 32 | */ 33 | private String name; 34 | 35 | /** 36 | * 创建日期 37 | */ 38 | private Date createTime; 39 | 40 | 41 | public Integer getId() { 42 | return id; 43 | } 44 | 45 | public void setId(Integer id) { 46 | this.id = id; 47 | } 48 | 49 | public String getAccount() { 50 | return account; 51 | } 52 | 53 | public void setAccount(String account) { 54 | this.account = account; 55 | } 56 | 57 | public String getPassword() { 58 | return password; 59 | } 60 | 61 | public void setPassword(String password) { 62 | this.password = password; 63 | } 64 | 65 | public String getName() { 66 | return name; 67 | } 68 | 69 | public void setName(String name) { 70 | this.name = name; 71 | } 72 | 73 | public Date getCreateTime() { 74 | return createTime; 75 | } 76 | 77 | public void setCreateTime(Date createTime) { 78 | this.createTime = createTime; 79 | } 80 | 81 | @Override 82 | public String toString() { 83 | return "User{" + 84 | "id=" + id + 85 | ", account='" + account + '\'' + 86 | ", password='" + password + '\'' + 87 | ", name='" + name + '\'' + 88 | ", createTime=" + createTime + 89 | '}'; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/test/java/com/github/lkqm/spring/jpa/repository/demo/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.jpa.repository.demo.repository; 2 | 3 | 4 | import com.github.lkqm.spring.jpa.repository.MybatisQuery; 5 | import com.github.lkqm.spring.jpa.repository.demo.domain.User; 6 | import java.util.List; 7 | import org.apache.ibatis.annotations.Mapper; 8 | import org.apache.ibatis.annotations.Select; 9 | import org.springframework.data.jpa.repository.JpaRepository; 10 | 11 | @Mapper 12 | public interface UserRepository extends JpaRepository { 13 | 14 | @MybatisQuery 15 | @Select("select * from user where account = #{account} limit 1") 16 | User selectByAccount(String account); 17 | 18 | @MybatisQuery 19 | int insert(User user); 20 | 21 | default List listUsers() { 22 | return this.findAll(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/com/github/lkqm/spring/jpa/repository/demo/repository/UserRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.jpa.repository.demo.repository; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertNotNull; 5 | 6 | import com.github.lkqm.spring.jpa.repository.demo.domain.User; 7 | import java.util.Date; 8 | import javax.annotation.Resource; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.test.context.junit4.SpringRunner; 13 | 14 | 15 | @RunWith(SpringRunner.class) 16 | @SpringBootTest 17 | public class UserRepositoryTest { 18 | 19 | @Resource 20 | private UserRepository userRepository; 21 | 22 | @Test 23 | public void selectByAccount() { 24 | User user = new User(); 25 | user.setAccount("luokaiqiongmou@foxmail.com"); 26 | user.setPassword("998"); 27 | user.setName("罗开"); 28 | user.setCreateTime(new Date()); 29 | userRepository.save(user); 30 | 31 | User result = userRepository.selectByAccount(user.getAccount()); 32 | assertNotNull(result); 33 | } 34 | 35 | @Test 36 | public void insert() { 37 | User user = new User(); 38 | user.setId(1); 39 | user.setAccount("luokaiqiongmou@foxmail.com"); 40 | user.setPassword("998"); 41 | user.setName("罗开"); 42 | user.setCreateTime(new Date()); 43 | int row = userRepository.insert(user); 44 | assertEquals(1, row); 45 | assertNotNull(user.getId()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.url=jdbc:h2:mem:spring-data-jpa-mybatis;DB_CLOSE_ON_EXIT=TRUE;MODE=MYSQL 2 | spring.jpa.show-sql=true 3 | spring.jpa.format-sql=true 4 | # mybatis 5 | mybatis.type-aliases-package=com.github.lkqm.spring.jpa.repository.demo.domain 6 | mybatis.mapper-locations=classpath*:mapper/*.xml 7 | mybatis.configuration.map-underscore-to-camel-case=true 8 | logging.level.com.github.lkqm.spring.jpa.repository.demo.repository=debug 9 | -------------------------------------------------------------------------------- /src/test/resources/mapper/UserRepository.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | insert into user(id, name, account, password) values(#{id}, #{name}, #{account}, #{password}) 9 | 10 | 11 | --------------------------------------------------------------------------------