摘要
看完本文你将掌握如下知识点:
Spring Boot项目中JPA的配置及使用方法
Spring Boot项目配置Spring Data JPA的方法
Spring Data JPA与Atomikos整合实现多数据源事务管理
扩展JPA的方法
SpringBoot系列 :Spring Boot学习笔记
前言
JPA即Java Persistence API,是一个基于O/R映射的标准规范,该规范只负责定义规则的标准(注解或接口),而不需要提供具体实现,具体的实现交由软件提供商来实现,目前主要的JPA提供商为Hibernate,EclipseLink和OperJPA。
Spring Data JPA 是Spring Data的一个子项目,通过提供基于JPA的Repository来简化代码量。
其提供了一个org.springframework.data.jpa.repository.JpaRepository ,我们的Repository只要继承该JpaRepository,即可享受到JPA带来的好处。
Spring Boot通过spring-boot-starter-data-jpa 来提供对JPA的支持,Spring Boot默认的JPA实现者是Hibernate。
说明
在讲解下面的内容前,我们先在数据库中创建一张表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # 创建库1 CREATE SCHEMA `springboot1` DEFAULT CHARACTER SET utf8 ;CREATE TABLE `springboot1`.`person` ( `p_id` INT NOT NULL AUTO_INCREMENT COMMENT '主键' , `p_name` VARCHAR (45 ) NULL COMMENT '姓名' , `p_age` INT NULL COMMENT '年龄' , PRIMARY KEY (`p_id`)) ENGINE = InnoDB COMMENT = '人员信息表' ; INSERT INTO `springboot1`.`person` (`p_id`, `p_name`, `p_age`) VALUES ('1' , '张三' , '20' );INSERT INTO `springboot1`.`person` (`p_id`, `p_name`, `p_age`) VALUES ('2' , '李四' , '25' );INSERT INTO `springboot1`.`person` (`p_id`, `p_name`, `p_age`) VALUES ('3' , '王五' , '18' );INSERT INTO `springboot1`.`person` (`p_id`, `p_name`, `p_age`) VALUES ('4' , '王五' , '18' );
Spring Boot项目中使用JPA
创建项目时选择JPA依赖,或者手工将spring-boot-starter-data-jpa 添加到pom中。
1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-jpa</artifactId > </dependency >
此时项目会自动开启如下两个自动配置类:
JpaRepositoriesAutoConfiguration
HibernateJpaAutoConfiguration
application.properties 中增加jpa相关配置
1 2 3 4 5 6 7 8 9 10 11 12 13 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/springboot1?useUnicode=true&characterEncoding=utf-8 spring.datasource.username=root spring.datasource.password=newpwd spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true spring.jackson.serialization.indent-output=true
在项目中使用JPA时,只需要创建一个继承于JpaRepository 的Repository接口,即可拥有JpaRepository 及其父类中提供的全部数据访问方法。如果提供的方法不满足业务需要,可以按如下规则扩展数据方法。
JpaRepository
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package org.springframework.data.jpa.repository;import java.io.Serializable;import java.util.List;import org.springframework.data.domain.Example;import org.springframework.data.domain.Sort;import org.springframework.data.repository.NoRepositoryBean;import org.springframework.data.repository.PagingAndSortingRepository;import org.springframework.data.repository.query.QueryByExampleExecutor;@NoRepositoryBean public interface JpaRepository <T, ID extends Serializable > extends PagingAndSortingRepository <T, ID>, QueryByExampleExecutor<T> { List<T> findAll () ; List<T> findAll (Sort var1) ; List<T> findAll (Iterable<ID> var1) ; <S extends T > List<S> save (Iterable<S> var1) ; void flush () ; <S extends T > S saveAndFlush (S var1) ; void deleteInBatch (Iterable<T> var1) ; void deleteAllInBatch () ; T getOne (ID var1) ; <S extends T > List<S> findAll (Example<S> var1) ; <S extends T > List<S> findAll (Example<S> var1, Sort var2) ; }
自定义Repository:PersonRepository ,并扩展数据访问方法,具体扩展方法参看示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 package com.example.dao;import com.example.model.Person;import org.springframework.data.domain.Page;import org.springframework.data.domain.Pageable;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.data.jpa.repository.Modifying;import org.springframework.data.jpa.repository.Query;import org.springframework.data.repository.query.Param;import java.util.List;public interface PersonRepository extends JpaRepository <Person, Integer> { List<Person> findByPName (String PName) ; List<Person> findByPNameAndPAge (String PName, Integer PAge) ; List<Person> findByPNameOrPAge (String PName, Integer PAge) ; List<Person> findByPAgeBetween (Integer min, Integer max) ; List<Person> findByPAgeLessThan (Integer max) ; List<Person> findByPAgeLessThanEqual (Integer max) ; List<Person> findByPAgeGreaterThan (Integer min) ; List<Person> findByPAgeGreaterThanEqual (Integer min) ; List<Person> findByPNameIsNull () ; List<Person> findByPNameIsNotNull () ; List<Person> findByPNameNotNull () ; List<Person> findByPNameLike (String PName) ; List<Person> findByPNameNotLike (String PName) ; List<Person> findByPNameNotNullOrderByPAgeAsc () ; List<Person> findByPNameNot (String PName) ; List<Person> findByPNameIn (String PName) ; List<Person> findByPNameNotIn (String PName) ; List<Person> findTop2ByPName (String PName) ; @Query("select p from Person p where p.pName = :name and p.pAge = :age") List<Person> withNameAndAgeQuery (@Param("name") String name, @Param("age") Integer age) ; @Query("select p from Person p where p.pName = ?1 and p.pAge = ?2") List<Person> withNameAndAgeQuery2 (String name, Integer age) ; @Query(value = "delete from Person where pId=?1") @Modifying int deletePersonById (Integer id) ; @Query(value = "update Person set pName=?1 where pId=?2 ") @Modifying int updatePersonName (String name, Integer id) ; @Query(value = "insert into person(p_name,p_age) value(?1,?2)",nativeQuery = true) @Modifying int insertPersonByParam (String name, Integer age) ; Page<Person> findByPNameNot (String name, Pageable pageable) ; @Query("select p from Person p where p.pName = :name ") Page<Person> withNameQueryPage (@Param("name") String name, Pageable pageable) ; }
POJO实体对象:Person
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package com.example.model;import javax.persistence.*;import static javax.persistence.GenerationType.IDENTITY;@Entity @Table(name = "person" , catalog = "springboot1" ) public class Person implements java .io.Serializable { @Id @GeneratedValue(strategy = IDENTITY) @Column(name = "p_id", unique = true, nullable = false) private Integer pId; @Column(name = "p_name", length = 45) private String pName; @Column(name = "p_age") private Integer pAge; @Override public String toString () { return "Person{" + "pId=" + pId + ", pName='" + pName + '\'' + ", pAge=" + pAge + '}' ; } }
测试演示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 package com.example;import com.example.dao.PersonRepository;import com.example.model.Person;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.data.domain.Page;import org.springframework.data.domain.PageRequest;import org.springframework.data.domain.Pageable;import org.springframework.data.domain.Sort;import org.springframework.test.context.junit4.SpringRunner;import org.springframework.transaction.annotation.Transactional;import java.util.Iterator;import java.util.List;@RunWith(SpringRunner.class) @SpringBootTest @Transactional public class JpaSingleDatasourceApplicationTests { @Autowired private PersonRepository personRepository; @Test public void findByPName () { String name = "王五" ; List<Person> list = personRepository.findByPName(name); System.out.println(list.size()); for (Person person : list){ System.out.println(person); } } @Test public void findByPNameAndPAge () { String name = "王五" ; int age = 18 ; List<Person> list = personRepository.findByPNameAndPAge(name,age); System.out.println(list.size()); for (Person person : list){ System.out.println(person); } } @Test public void findByPNameOrPAge () { String name = "王五" ; int age = 25 ; List<Person> list = personRepository.findByPNameOrPAge(name,age); System.out.println(list.size()); for (Person person : list){ System.out.println(person); } } @Test public void findTop2ByPName () { String name = "王五" ; List<Person> list = personRepository.findTop2ByPName(name); System.out.println(list.size()); for (Person person : list){ System.out.println(person); } } @Test public void withNameAndAgeQuery () { String name = "王五" ; int age = 18 ; List<Person> list = personRepository.withNameAndAgeQuery(name,age); System.out.println(list.size()); for (Person person : list){ System.out.println(person); } } @Test public void withNameAndAgeQuery2 () { String name = "王五" ; int age = 18 ; List<Person> list = personRepository.withNameAndAgeQuery2(name,age); System.out.println(list.size()); for (Person person : list){ System.out.println(person); } } @Test public void deletePersonById () { int id = 1 ; int result = personRepository.deletePersonById(id); System.out.println("result = " + result); } @Test public void updatePersonName () { int id = 1 ; String name = "哈哈" ; int result = personRepository.updatePersonName(name,id); System.out.println("result = " + result); } @Test public void insertPersonByParam () { int age = 10 ; String name = "哈哈" ; int result = personRepository.insertPersonByParam(name,age); System.out.println("result = " + result); } @Test public void findByPNameNot () { String name = "哈哈" ; Sort sort = new Sort (Sort.Direction.DESC, "pId" ); Pageable pageable = new PageRequest (0 , 3 , sort); Page<Person> pages = personRepository.findByPNameNot(name,pageable); System.out.println("pages.getTotalElements()" + pages.getTotalElements()); System.out.println("pages.getTotalPages()" + pages.getTotalPages()); Iterator<Person> it=pages.iterator(); while (it.hasNext()){ System.out.println("value:" +((Person)it.next())); } } @Test public void withNameQueryPage () { String name = "王五" ; Sort sort = new Sort (Sort.Direction.DESC, "pId" ); Pageable pageable = new PageRequest (1 , 3 , sort); Page<Person> pages = personRepository.withNameQueryPage(name,pageable); System.out.println("pages.getTotalElements()" + pages.getTotalElements()); System.out.println("pages.getTotalPages()" + pages.getTotalPages()); Iterator<Person> it=pages.iterator(); while (it.hasNext()){ System.out.println("value:" +((Person)it.next())); } } }
Spring Boot项目配置Spring Data JPA的方法
如果不想依赖于spring-boot-starter-data-jpa ,我们依然可以通过配置类来实现Spring Boot对Spring Data JPA的支持。
pom 替换依赖
这里说明一下,实际上我们可以不用替换掉spring-boot-starter-data-jpa 的依赖,替换掉的好处仅仅是减少对不需要的jar的依赖。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.37</version > </dependency > <dependency > <groupId > javax.transaction</groupId > <artifactId > jta</artifactId > <version > 1.1</version > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.8.9</version > </dependency > <dependency > <groupId > org.hibernate</groupId > <artifactId > hibernate-core</artifactId > <version > 4.3.5.Final</version > </dependency > <dependency > <groupId > org.hibernate</groupId > <artifactId > hibernate-entitymanager</artifactId > <version > 4.3.5.Final</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-orm</artifactId > <version > 4.3.3.RELEASE</version > </dependency > <dependency > <groupId > org.springframework.data</groupId > <artifactId > spring-data-jpa</artifactId > <version > 1.10.5.RELEASE</version > </dependency >
自定义配置类:DataSourceConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 package com.example;import org.hibernate.jpa.HibernatePersistenceProvider;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.jpa.repository.config.EnableJpaRepositories;import org.springframework.jdbc.datasource.DriverManagerDataSource;import org.springframework.orm.jpa.JpaTransactionManager;import org.springframework.orm.jpa.JpaVendorAdapter;import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;import org.springframework.orm.jpa.vendor.Database;import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;import org.springframework.transaction.PlatformTransactionManager;import org.springframework.transaction.annotation.EnableTransactionManagement;import javax.sql.DataSource;@Configuration @EnableTransactionManagement(proxyTargetClass = true) @EnableJpaRepositories(basePackages = "com.example.dao", entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager") public class DataSourceConfig { @Value("${spring.datasource.driver-class-name}") String driverClass; @Value("${spring.datasource.url}") String url; @Value("${spring.datasource.username}") String userName; @Value("${spring.datasource.password}") String passWord; @Bean(name = "dataSource") public DataSource dataSource () { DriverManagerDataSource dataSource = new DriverManagerDataSource (); dataSource.setDriverClassName(driverClass); dataSource.setUrl(url); dataSource.setUsername(userName); dataSource.setPassword(passWord); return dataSource; } @Bean(name = "transactionManager") public PlatformTransactionManager transactionManager () { JpaTransactionManager jpaTransactionManager = new JpaTransactionManager (); jpaTransactionManager.setDataSource(dataSource()); jpaTransactionManager.setEntityManagerFactory(entityManagerFactory().getObject()); return jpaTransactionManager; } @Bean public JpaVendorAdapter jpaVendorAdapter () { HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter (); adapter.setShowSql(true ); adapter.setDatabase(Database.MYSQL); adapter.setGenerateDdl(true ); return adapter; } @Bean(name = "entityManagerFactory") public LocalContainerEntityManagerFactoryBean entityManagerFactory () { LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean (); entityManager.setDataSource(dataSource()); entityManager.setJpaVendorAdapter(jpaVendorAdapter()); entityManager.setPackagesToScan("com.example.model" ); entityManager.setPersistenceProviderClass(HibernatePersistenceProvider.class); return entityManager; } }
项目启动类中要关闭jpa的自动配置:
1 2 3 4 5 6 7 @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class,JpaRepositoriesAutoConfiguration.class, HibernateJpaAutoConfiguration.class}) public class JpaSingleDatasourceApplication { public static void main (String[] args) { SpringApplication.run(JpaSingleDatasourceApplication.class, args); } }
Spring Data JPA与Atomikos整合实现多数据源事务管理
spring-data-jpa虽说默认使用的是Hibernate,但是其与Atomikos整合方式与Hibernate略有不同。
pom
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 <dependency > <groupId > com.atomikos</groupId > <artifactId > transactions-jdbc</artifactId > <version > 4.0.4</version > </dependency > <dependency > <groupId > com.atomikos</groupId > <artifactId > transactions-jta</artifactId > <version > 4.0.4</version > </dependency > <dependency > <groupId > com.atomikos</groupId > <artifactId > transactions</artifactId > <version > 4.0.4</version > </dependency > <dependency > <groupId > com.atomikos</groupId > <artifactId > atomikos-util</artifactId > <version > 4.0.4</version > </dependency > <dependency > <groupId > javax.transaction</groupId > <artifactId > jta</artifactId > <version > 1.1</version > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.8.9</version > </dependency > <dependency > <groupId > org.hibernate</groupId > <artifactId > hibernate-core</artifactId > <version > 4.3.5.Final</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-orm</artifactId > <version > 4.3.3.RELEASE</version > </dependency > <dependency > <groupId > org.springframework.data</groupId > <artifactId > spring-data-jpa</artifactId > <version > 1.10.5.RELEASE</version > </dependency > <dependency > <groupId > org.hibernate</groupId > <artifactId > hibernate-entitymanager</artifactId > <version > 4.3.5.Final</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.37</version > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > </dependency >
application.properties
1 2 3 4 5 6 7 8 9 10 11 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/springboot1?useUnicode=true&characterEncoding=utf-8 spring.datasource.username=root spring.datasource.password=newpwd spring.datasource.driver-class-name2=com.mysql.jdbc.Driver spring.datasource.url2=jdbc:mysql://localhost:3306/springboot2?useUnicode=true&characterEncoding=utf-8 spring.datasource.username2=root spring.datasource.password2=newpwd
MainConfig :用于注册Atomikos事务管理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 package com.example;import com.atomikos.icatch.jta.UserTransactionImp;import com.atomikos.icatch.jta.UserTransactionManager;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.DependsOn;import org.springframework.transaction.PlatformTransactionManager;import org.springframework.transaction.jta.JtaTransactionManager;import javax.transaction.TransactionManager;import javax.transaction.UserTransaction;@Configuration public class MainConfig { @Bean(name = "userTransaction") public UserTransaction userTransaction () throws Throwable { UserTransactionImp userTransactionImp = new UserTransactionImp (); userTransactionImp.setTransactionTimeout(10000 ); return userTransactionImp; } @Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close") public TransactionManager atomikosTransactionManager () throws Throwable { UserTransactionManager userTransactionManager = new UserTransactionManager (); userTransactionManager.setForceShutdown(true ); return userTransactionManager; } @Bean(name = "transactionManager") @DependsOn({ "userTransaction", "atomikosTransactionManager" }) public PlatformTransactionManager transactionManager () throws Throwable { UserTransaction userTransaction = userTransaction(); TransactionManager atomikosTransactionManager = atomikosTransactionManager(); JtaTransactionManager jtaTransactionManager = new JtaTransactionManager (userTransaction, atomikosTransactionManager); jtaTransactionManager.setAllowCustomIsolationLevels(true ); return jtaTransactionManager; } @Bean(name = "atomikosJtaPlatfom") public AtomikosJtaPlatfom atomikosJtaPlatfom () { AtomikosJtaPlatfom atomikosJtaPlatfom = new AtomikosJtaPlatfom (); try { atomikosJtaPlatfom.setTm(atomikosTransactionManager()); atomikosJtaPlatfom.setUt(userTransaction()); } catch (Throwable throwable) { throwable.printStackTrace(); } return atomikosJtaPlatfom; } }
配置JPA的LocalContainerEntityManagerFactoryBean时候,如果要使其能够支持JTA事务,则在配置其JpaProperties 时需要为其指定如下参数:
hibernate.transaction.jta.platform
hibernate.current_session_context_class
hibernate.transaction.factory_class
后面我们配置LocalContainerEntityManagerFactoryBean的时候会看到相应的配置,
这里要说的是,hibernate.transaction.jta.platform
需要指定org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform
的实现类,其主要功能就是要绑定javax.transaction.TransactionManager 和javax.transaction.UserTransaction 。
spring-data-jpa没有提供该实现类,但是hibernate提供了许多实现类,spring boot也提供了一个实现类–SpringJtaPlatform ,
但是这些实现类都是通过构造函数绑定javax.transaction.TransactionManager 和javax.transaction.UserTransaction ,而没有提供缺省的构造方法,这就导致通过属性指定hibernate.transaction.jta.platform
时,spring不能初始化该实现类(可能是我还没有搞明白吧)。
所以,可以自己创建一个实现类,并通过set方法来绑定javax.transaction.TransactionManager 和javax.transaction.UserTransaction 。
这就是AtomikosJtaPlatfom
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.example;import org.hibernate.engine.transaction.jta.platform.internal.AbstractJtaPlatform;import javax.transaction.TransactionManager;import javax.transaction.UserTransaction;public class AtomikosJtaPlatfom extends AbstractJtaPlatform { private static UserTransaction ut; private static TransactionManager tm; @Override protected TransactionManager locateTransactionManager () { return tm; } @Override protected UserTransaction locateUserTransaction () { return ut; } public UserTransaction getUt () { return ut; } public void setUt (UserTransaction ut) { AtomikosJtaPlatfom.ut = ut; } public TransactionManager getTm () { return tm; } public void setTm (TransactionManager tm) { AtomikosJtaPlatfom.tm = tm; } }
接下来需要在配置类中注册LocalContainerEntityManagerFactoryBean ,
由于*@EnableJpaRepositories*注解不能在同一个配置类上声明两次,所以就按数据源进行分别设置:
JpaConfigDs1 :数据源1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 package com.example;import com.atomikos.jdbc.AtomikosDataSourceBean;import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.DependsOn;import org.springframework.data.jpa.repository.config.EnableJpaRepositories;import org.springframework.orm.jpa.JpaVendorAdapter;import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;import org.springframework.orm.jpa.vendor.Database;import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;import org.springframework.transaction.annotation.EnableTransactionManagement;import javax.sql.DataSource;import java.util.Properties;@Configuration @EnableTransactionManagement(proxyTargetClass = true) @EnableJpaRepositoryies(basePackages = "com.example.dao.ds1", entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager") public class JpaConfigDs1 { @Value("${spring.datasource.driver-class-name}") String driverClass; @Value("${spring.datasource.url}") String url; @Value("${spring.datasource.username}") String userName; @Value("${spring.datasource.password}") String passWord; @Bean(name = "dataSource", initMethod = "init", destroyMethod = "close") public DataSource dataSource () { System.out.println("dataSource init" ); MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource (); mysqlXaDataSource.setUrl(url); mysqlXaDataSource.setPassword(passWord); mysqlXaDataSource.setUser(userName); mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true ); AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean (); xaDataSource.setXaDataSource(mysqlXaDataSource); xaDataSource.setUniqueResourceName("dataSource" ); xaDataSource.setMinPoolSize(10 ); xaDataSource.setPoolSize(10 ); xaDataSource.setMaxPoolSize(30 ); xaDataSource.setBorrowConnectionTimeout(60 ); xaDataSource.setReapTimeout(20 ); xaDataSource.setMaxIdleTime(60 ); xaDataSource.setMaintenanceInterval(60 ); return xaDataSource; } @Bean(name = "jpaVendorAdapter") public JpaVendorAdapter jpaVendorAdapter () { System.out.println("jpaVendorAdapter init" ); HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter (); adapter.setShowSql(true ); adapter.setDatabase(Database.MYSQL); adapter.setDatabasePlatform("org.hibernate.dialect.MySQLDialect" ); adapter.setGenerateDdl(true ); return adapter; } @Bean(name = "entityManagerFactory") @DependsOn({"atomikosJtaPlatfom"}) public LocalContainerEntityManagerFactoryBean entityManagerFactory () { System.out.println("entityManagerFactory init" ); LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean (); entityManager.setJpaVendorAdapter(jpaVendorAdapter()); entityManager.setPackagesToScan("com.example.model.ds1" ); entityManager.setJtaDataSource(dataSource()); Properties properties = new Properties (); properties.put("hibernate.dialect" , "org.hibernate.dialect.MySQLDialect" ); properties.put("hibernate.show_sql" , "true" ); properties.put("hibernate.format_sql" , "true" ); properties.put("hibernate.current_session_context_class" , "jta" ); properties.put("hibernate.transaction.factory_class" , "org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory" ); properties.put("hibernate.transaction.jta.platform" ,"com.example.AtomikosJtaPlatfom" ); entityManager.setJpaProperties(properties); return entityManager; } }
JpaConfigDs2 :数据源2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 package com.example;import com.atomikos.jdbc.AtomikosDataSourceBean;import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.DependsOn;import org.springframework.data.jpa.repository.config.EnableJpaRepositories;import org.springframework.orm.jpa.JpaVendorAdapter;import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;import org.springframework.orm.jpa.vendor.Database;import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;import org.springframework.transaction.annotation.EnableTransactionManagement;import javax.sql.DataSource;import java.util.Properties;@Configuration @EnableTransactionManagement(proxyTargetClass = true) @EnableJpaRepositories(basePackages = "com.example.dao.ds2", entityManagerFactoryRef = "entityManagerFactory2", transactionManagerRef = "transactionManager") public class JpaConfigDs2 { @Value("${spring.datasource.driver-class-name2}") String driverClass; @Value("${spring.datasource.url2}") String url; @Value("${spring.datasource.username2}") String userName; @Value("${spring.datasource.password2}") String passWord; @Bean(name = "dataSource2", initMethod = "init", destroyMethod = "close") public DataSource dataSource () { System.out.println("dataSource2 init" ); MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource (); mysqlXaDataSource.setUrl(url); mysqlXaDataSource.setPassword(passWord); mysqlXaDataSource.setUser(userName); mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true ); AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean (); xaDataSource.setXaDataSource(mysqlXaDataSource); xaDataSource.setUniqueResourceName("dataSource2" ); xaDataSource.setMinPoolSize(10 ); xaDataSource.setPoolSize(10 ); xaDataSource.setMaxPoolSize(30 ); xaDataSource.setBorrowConnectionTimeout(60 ); xaDataSource.setReapTimeout(20 ); xaDataSource.setMaxIdleTime(60 ); xaDataSource.setMaintenanceInterval(60 ); return xaDataSource; } @Bean(name = "jpaVendorAdapter2") public JpaVendorAdapter jpaVendorAdapter () { System.out.println("jpaVendorAdapter2 init" ); HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter (); adapter.setShowSql(true ); adapter.setDatabase(Database.MYSQL); adapter.setDatabasePlatform("org.hibernate.dialect.MySQLDialect" ); adapter.setGenerateDdl(true ); return adapter; } @Bean(name = "entityManagerFactory2") @DependsOn({"atomikosJtaPlatfom"}) public LocalContainerEntityManagerFactoryBean entityManagerFactory () { System.out.println("entityManagerFactory2 init" ); LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean (); entityManager.setJpaVendorAdapter(jpaVendorAdapter()); entityManager.setPackagesToScan("com.example.model.ds2" ); entityManager.setJtaDataSource(dataSource()); Properties properties = new Properties (); properties.put("hibernate.transaction.jta.platform" ,"com.example.AtomikosJtaPlatfom" ); properties.put("hibernate.dialect" , "org.hibernate.dialect.MySQLDialect" ); properties.put("hibernate.show_sql" , "true" ); properties.put("hibernate.format_sql" , "true" ); properties.put("hibernate.current_session_context_class" , "jta" ); properties.put("hibernate.transaction.factory_class" , "org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory" ); entityManager.setJpaProperties(properties); return entityManager; } }
其它方面与单数据源使用JPA没有区别,这里就不罗列代码了。
扩展JPA的方法
上面我们介绍过,一般情况下我们的Repository接口继承JpaRepository,所以可以默认使用JpaRepository提供的所有方法,如果提供的方法不满足需求时,可以在自己的Repository中通过命名规则或者@Query注解等实现方法的扩展。那么,我们如果希望将一些自己扩展公共的方法放在父类中,以便我们所有的Repository都能拥有该扩展功能,该如何实现呢?
本例只举例说明,实现的功能为接收查询条件的分页查询,查询时按传递实体对象的属性进行处理,如果是字符串就按模糊匹配,否则就按精确匹配。
定义父类接口–BaseJpaRepository
1 2 3 4 5 @NoRepositoryBean public interface BaseJpaRepository <T, ID extends Serializable > extends JpaRepository <T, ID>,JpaSpecificationExecutor<T> { Page<T> findByAuto (T example, Pageable pageable) ; }
创建实现类–BaseJpaRepositoryImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class BaseJpaRepositoryImpl <T, ID extends Serializable > extends SimpleJpaRepository <T, ID> implements BaseJpaRepository <T, ID> { private final EntityManager entityManager; public BaseJpaRepositoryImpl (Class<T> domainClass, EntityManager entityManager) { super (domainClass, entityManager); this .entityManager = entityManager; } @Override public Page<T> findByAuto (T example, Pageable pageable) { return findAll(BaseSpecs.byAuto(entityManager,example),pageable); } }
BaseSpecs的byAuto方法负责封装查询对象Specification,按传递实体对象的属性进行处理,如果是字符串就按模糊匹配,否则就按精确匹配。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public class BaseSpecs { public static <T> Specification<T> byAuto (final EntityManager entityManager, final T example) { final Class<T> type = (Class<T>) example.getClass(); return new Specification <T>() { @Override public Predicate toPredicate (Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { List<Predicate> predicateList = new ArrayList <>(); EntityType<T> entityType = entityManager.getMetamodel().entity(type); for (Attribute<T,?> attribute : entityType.getDeclaredAttributes()){ Object attrValue = getValue(example,attribute); if (attrValue != null ){ if (attribute.getJavaType() == String.class){ if (!StringUtils.isEmpty(attrValue)){ predicateList.add(criteriaBuilder.like(root.get(attribute(entityType,attribute.getName(),String.class)),pattern((String)attrValue))); } }else { predicateList.add(criteriaBuilder.equal(root.get(attribute(entityType,attribute.getName(),attrValue.getClass())),attrValue)); } } } return predicateList.isEmpty()?criteriaBuilder.conjunction():criteriaBuilder.and(toArray(predicateList)); } private <T> Object getValue (T example,Attribute<T,?> attr) { return ReflectionUtils.getField((Field)attr.getJavaMember(),example); } private <E,T> SingularAttribute<T,E> attribute (EntityType<T> entityType,String fieldName,Class<E> fieldClass) { return entityType.getDeclaredSingularAttribute(fieldName,fieldClass); } private Predicate[] toArray(List<Predicate> predicateList){ Predicate[] array = predicateList.toArray(new Predicate [predicateList.size()]); return array; } }; } static private String pattern (String str) { return "%" + str + "%" ; } }
说明
当我们的Repository实现的是JpaRepository的时候,Spring-data-jpa 会为我们动态使用JpaRepository的实现类SimpleJpaRepository ,这也是为什么我们只需要创建接口而不需要提供实现类。
这里,我们创建了新的父类接口BaseJpaRepository ,并为其提供了实现类BaseJpaRepositoryImpl ,所以我们要告诉Spring-data-jpa 要使用我们自己的实现类,而不能去使用SimpleJpaRepository ,所以我们要改写JpaRepositoryFactoryBean ;
创建一个BaseRepositoryFactoryBean 继承于JpaRepositoryFactoryBean :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class BaseRepositoryFactoryBean <T extends JpaRepository <S, ID>, S, ID extends Serializable > extends JpaRepositoryFactoryBean <T,S,ID> { @Override protected RepositoryFactorySupport createRepositoryFactory (EntityManager entityManager) { return new BaseRepositoryFactory (entityManager); } } class BaseRepositoryFactory extends JpaRepositoryFactory { public BaseRepositoryFactory (EntityManager entityManager) { super (entityManager); } @Override protected <T, ID extends Serializable > SimpleJpaRepository<?, ?> getTargetRepository(RepositoryInformation information, EntityManager entityManager) { BaseJpaRepositoryImpl customRepository = new BaseJpaRepositoryImpl <T,ID>((Class<T>)information.getDomainType(),entityManager); return customRepository; } @Override protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) return BaseJpaRepositoryImpl.class; } }
并且在*@EnableJpaRepositories*注解中进行指定:
1 2 3 4 @EnableJpaRepositories(basePackages = "com.example.dao", entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager",repositoryFactoryBeanClass=BaseRepositoryFactoryBean.class) public class JpaConfig { }
自定义Repository继承BaseJpaRepository
1 2 3 4 public interface PersonRepository extends BaseJpaRepository <Person, Integer> { }
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @RunWith(SpringRunner.class) @SpringBootTest @Transactional public class JpaExtendApplicationTests { @Autowired private PersonRepository personRepository; @Test public void findByAuto () { Person person = new Person (); person.setpName("王五" ); person.setpAge(18 ); Sort sort = new Sort (Sort.Direction.DESC, "pId" ); Pageable pageable = new PageRequest (0 , 3 , sort); Page<Person> list = personRepository.findByAuto(person,pageable); for (Person p:list){ System.out.println(p); } } }
本文示例代码下载地址:https://github.com/hanqunfeng/SpringBootStudy