SpringData JPA基本/高級(jí)/多數(shù)據(jù)源的使用詳解
一、Spring Data JPA基本用法
Spring Data JPA 是 Spring Boot 體系中約定優(yōu)于配置的最佳實(shí)現(xiàn),大大簡(jiǎn)化了項(xiàng)目中數(shù)據(jù)庫(kù)的操作
1、概念
JPA由來
ORM 框架能夠?qū)?Java 對(duì)象映射到關(guān)系數(shù)據(jù)庫(kù)中,能夠直接持久化復(fù)雜的 Java 對(duì)象。ORM 框架的出現(xiàn),可以讓開發(fā)者從數(shù)據(jù)庫(kù)編程中解脫出來,把更多的精力放在了業(yè)務(wù)模型與業(yè)務(wù)邏輯上。目前比較流行的 ORM 框架有 Hibernate、MyBatis、TopLink、Spring JDBC 等。
在 JPA 規(guī)范之前,由于沒有官方的標(biāo)準(zhǔn),使得各 ORM 框架之間的 API 差別很大,使用了某種 ORM 框架的系統(tǒng)會(huì)嚴(yán)重受制于該 ORM 的標(biāo)準(zhǔn)。基于此,Sun 引入新的 JPA ORM,主要的原因有:其一,簡(jiǎn)化現(xiàn)有 Java EE 和 Java SE 應(yīng)用開發(fā)工作;其二,Sun 希望整合 ORM 技術(shù),實(shí)現(xiàn)統(tǒng)一的 API 調(diào)用接口。
JPA是什么
JPA(Java Persistence API)是 Sun 官方提出的 Java 持久化規(guī)范。它為 Java 開發(fā)人員提供了一種對(duì)象 / 關(guān)聯(lián)映射工具來管理 Java 應(yīng)用中的關(guān)系數(shù)據(jù)。它的出現(xiàn)主要是為了簡(jiǎn)化現(xiàn)有的持久化開發(fā)工作和整合 ORM 技術(shù),結(jié)束現(xiàn)在 Hibernate、TopLink、JDO 等 ORM 框架各自為營(yíng)的局面。
注意:JPA 是一套規(guī)范,不是一套產(chǎn)品,那么像 Hibernate、TopLink、JDO 它們是一套產(chǎn)品,如果說這些產(chǎn)品實(shí)現(xiàn)了這個(gè) JPA 規(guī)范,那么我們就可以稱他們?yōu)?JPA 的實(shí)現(xiàn)產(chǎn)品。
Spring Data JPA
Spring Data JPA 是 Spring 基于 ORM 框架、JPA 規(guī)范的基礎(chǔ)上封裝的一套 JPA 應(yīng)用框架,可以讓開發(fā)者用極簡(jiǎn)的代碼即可實(shí)現(xiàn)對(duì)數(shù)據(jù)的訪問和操作。它提供了包括增、刪、改、查等在內(nèi)的常用功能,且易于擴(kuò)展,學(xué)習(xí)并使用 Spring Data JPA 可以極大提高開發(fā)效率。Spring Data JPA 其實(shí)就是 Spring 基于 Hibernate 之上構(gòu)建的 JPA 使用解決方案,方便在 Spring Boot 項(xiàng)目中使用 JPA 技術(shù)。
Spring Data JPA 讓我們解脫了 DAO 層的操作,基本上所有 CRUD 都可以依賴于它實(shí)現(xiàn)。
2、快速上手
1.添加依賴
<dependency> ? ? <groupId>org.springframework.boot</groupId> ? ? <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> ?<dependency> ? ? <groupId>mysql</groupId> ? ? <artifactId>mysql-connector-java</artifactId> </dependency>
2.添加配置文件
spring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.jpa.properties.hibernate.hbm2ddl.auto=create spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect #SQL 輸出 spring.jpa.show-sql=true #format 一下 SQL 進(jìn)行輸出 spring.jpa.properties.hibernate.format_sql=true
hibernate.hbm2ddl.auto 參數(shù)的作用主要用于:自動(dòng)創(chuàng)建、更新、驗(yàn)證數(shù)據(jù)庫(kù)表結(jié)構(gòu),有四個(gè)值。
create:每次加載 Hibernate 時(shí)都會(huì)刪除上一次生成的表,然后根據(jù) model 類再重新來生成新表,哪怕兩次沒有任何改變也要這樣執(zhí)行,這就是導(dǎo)致數(shù)據(jù)庫(kù)表數(shù)據(jù)丟失的一個(gè)重要原因。create-drop:每次加載 Hibernate 時(shí)根據(jù) model 類生成表,但是 sessionFactory 一關(guān)閉,表就自動(dòng)刪除。update:最常用的屬性,第一次加載 Hibernate 時(shí)根據(jù) model 類會(huì)自動(dòng)建立起表的結(jié)構(gòu)(前提是先建立好數(shù)據(jù)庫(kù)),以后加載 Hibernate 時(shí)根據(jù) model 類自動(dòng)更新表結(jié)構(gòu),即使表結(jié)構(gòu)改變了,但表中的行仍然存在,不會(huì)刪除以前的行。要注意的是當(dāng)部署到服務(wù)器后,表結(jié)構(gòu)是不會(huì)被馬上建立起來的,是要等應(yīng)用第一次運(yùn)行起來后才會(huì)。validate:每次加載 Hibernate 時(shí),驗(yàn)證創(chuàng)建數(shù)據(jù)庫(kù)表結(jié)構(gòu),只會(huì)和數(shù)據(jù)庫(kù)中的表進(jìn)行比較,不會(huì)創(chuàng)建新表,但是會(huì)插入新值。
其他
dialect主要是指定生成表名的存儲(chǔ)引擎為 InnoDBshow-sql是否在日志中打印出自動(dòng)生成的 SQL,方便調(diào)試的時(shí)候查看
3.實(shí)體類
@Entity
public class User {
? ? @Id
? ? @GeneratedValue
? ? private Long id;
? ? @Column(nullable = false, unique = true)
? ? private String userName;
? ? @Column(nullable = false)
? ? private String passWord;
? ? @Column(nullable = false, unique = true)
? ? private String email;
? ? @Column(nullable = true, unique = true)
? ? private String nickName;
? ? @Column(nullable = false)
? ? private String regTime;
? ? //省略 getter settet 方法、構(gòu)造方法
}@Entity(name="EntityName")必須,用來標(biāo)注一個(gè)數(shù)據(jù)庫(kù)對(duì)應(yīng)的實(shí)體,數(shù)據(jù)庫(kù)中創(chuàng)建的表名默認(rèn)和類名一致。其中,name 為可選,對(duì)應(yīng)數(shù)據(jù)庫(kù)中一個(gè)表,使用此注解標(biāo)記 Pojo 是一個(gè) JPA 實(shí)體。@Table(name="",catalog="",schema="")可選,用來標(biāo)注一個(gè)數(shù)據(jù)庫(kù)對(duì)應(yīng)的實(shí)體,數(shù)據(jù)庫(kù)中創(chuàng)建的表名默認(rèn)和類名一致。通常和@Entity配合使用,只能標(biāo)注在實(shí)體的 class 定義處,表示實(shí)體對(duì)應(yīng)的數(shù)據(jù)庫(kù)表的信息。@Id必須,@Id定義了映射到數(shù)據(jù)庫(kù)表的主鍵的屬性,一個(gè)實(shí)體只能有一個(gè)屬性被映射為主鍵。@GeneratedValue(strategy=GenerationType,generator="")可選,strategy: 表示主鍵生成策略,有 AUTO、INDENTITY、SEQUENCE 和 TABLE 4 種,分別表示讓 ORM 框架自動(dòng)選擇,generator: 表示主鍵生成器的名稱。@Column(name = "user_code", nullable = false, length=32)可選,@Column 描述了數(shù)據(jù)庫(kù)表中該字段的詳細(xì)定義,這對(duì)于根據(jù) JPA 注解生成數(shù)據(jù)庫(kù)表結(jié)構(gòu)的工具。name: 表示數(shù)據(jù)庫(kù)表中該字段的名稱,默認(rèn)情形屬性名稱一致;nullable: 表示該字段是否允許為 null,默認(rèn)為 true;unique: 表示該字段是否是唯一標(biāo)識(shí),默認(rèn)為 false;length: 表示該字段的大小,僅對(duì) String 類型的字段有效。@Transient可選,@Transient 表示該屬性并非一個(gè)到數(shù)據(jù)庫(kù)表的字段的映射,ORM 框架將忽略該屬性。@Enumerated可選,使用枚舉的時(shí)候,我們希望數(shù)據(jù)庫(kù)中存儲(chǔ)的是枚舉對(duì)應(yīng)的 String 類型,而不是枚舉的索引值,需要在屬性上面添加 @Enumerated(EnumType.STRING) 注解。
4.Repository構(gòu)建
創(chuàng)建的 Repository 只要繼承 JpaRepository 即可,就會(huì)幫我們自動(dòng)生成很多內(nèi)置方法。另外還有一個(gè)功能非常實(shí)用,可以根據(jù)方法名自動(dòng)生產(chǎn) SQL,比如 findByUserName 會(huì)自動(dòng)生產(chǎn)一個(gè)以 userName 為參數(shù)的查詢方法,比如 findAll 會(huì)自動(dòng)查詢表里面的所有數(shù)據(jù)等。
public interface UserRepository extends JpaRepository<User,Long> {
? ? User findByUserName(String userName);
? ? User findByUserNameOrEmail(String username,String email);
}我們只需要在對(duì)應(yīng)的 Repository 中創(chuàng)建好方法,使用的時(shí)候直接將接口注入到類中調(diào)用即可。在 IDEA 中打開類 UserRepository,在這個(gè)類的大括號(hào)內(nèi)的區(qū)域右鍵單擊,選擇 Diagrams | Show Diagram 選項(xiàng),即可打開類圖,如下:

通過上圖我們發(fā)現(xiàn) JpaRepository 繼承 PagingAndSortingRepository 和 QueryByExampleExecutor,PagingAndSortingRepository 類主要負(fù)責(zé)排序和分頁內(nèi)容,QueryByExampleExecutor 提供了很多示例的查詢方法,如下:
public interface QueryByExampleExecutor<T> {?
? ? <S extends T> S findOne(Example<S> example); ? //根據(jù)“實(shí)例”查找一個(gè)對(duì)象
? ? <S extends T> Iterable<S> findAll(Example<S> example); ? ? //根據(jù)“實(shí)例”查找一批對(duì)象
? ? <S extends T> Iterable<S> findAll(Example<S> example,Sort sort); ?//根據(jù)“實(shí)例”查找一批對(duì)象,且排序
? ? <S extends T> Page<S> findAll(Example<S> example,Pageable pageable); ?//根據(jù)“實(shí)例”查找一批對(duì)象,且排序和分頁
? ? <S extends T> long count(Example<S> example); ?//根據(jù)“實(shí)例”查找,返回符合條件的對(duì)象個(gè)數(shù)
? ? <S extends T> boolean exists(Example<S> example); ?//根據(jù)“實(shí)例”判斷是否有符合條件的對(duì)象
}因此,繼承 JpaRepository 的會(huì)自動(dòng)擁有上述這些方法和排序、分頁功能。查看源碼我們發(fā)現(xiàn) PagingAndSortingRepository 又繼承了 CrudRepository。CrudRepository 的源碼如下:
@NoRepositoryBean
public interface CrudRepository<T,ID> extends Repository<T,ID> {
? ? <S extends T> S save(S entity);
? ? <S extends T> Iterable<S> saveAll(Iterable<S> entities);
? ? Optional<T> findById(ID id);
? ? boolean existsById(ID id);
? ? Iterable<T> findAll();
? ? Iterable<T> findAllById(Iterable<ID> ids);
? ? long count();
? ? void deleteById(ID id);
? ? void delete(T entity);
? ? void deleteAll(Iterable<? extends T> entities);
? ? void deleteAll();
}從 CrudRepository 的源碼可以看出 CrudRepository 內(nèi)置了我們最常用的增、刪、改、查的方法,方便我們?nèi)ナ褂?,因?yàn)?JpaRepository 繼承了 PagingAndSortingRepository,PagingAndSortingRepository 繼承了 CrudRepository,所以繼承 JpaRepository 的類也默認(rèn)擁有了上述方法。
因此使用 JPA 操作數(shù)據(jù)庫(kù)時(shí),只需要構(gòu)建的 Repository 繼承了 JpaRepository,就會(huì)擁有了很多常用的數(shù)據(jù)庫(kù)操作方法。
5.測(cè)試
創(chuàng)建好 UserRepository 之后,當(dāng)業(yè)務(wù)代碼中需要使用時(shí)直接將此接口注入到對(duì)應(yīng)的類中,在 Spring Boot 啟動(dòng)時(shí),會(huì)自動(dòng)根據(jù)注解內(nèi)容創(chuàng)建實(shí)現(xiàn)類并注入到目標(biāo)類中。
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserRepositoryTests {
? ? @Resource
? ? private UserRepository userRepository;
? ? @Test
? ? public void test() ?{
? ? ? ? Date date = new Date();
? ? ? ? DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.LONG); ? ? ? ?
? ? ? ? String formattedDate = dateFormat.format(date);
? ? ? ? userRepository.save(new User("aa","aa@126.com","aa","aa123456",formattedDate));
? ? ? ? userRepository.save(new User("bb","bb@126.com","bb","bb123456",formattedDate));
? ? ? ? userRepository.save(new User("cc","cc@126.com","cc","cc123456",formattedDate));
? ? ? ? Assert.assertEquals(9,userRepository.findAll().size());
? ? ? ? Assert.assertEquals("bb",userRepository.findByUserNameOrEmail("bb","cc@126.com").getNickName());
? ? ? ? userRepository.delete(userRepository.findByUserName("aa1"));
? ? }
}6.基本查詢
我們可以將 Spring Data JPA 查詢分為兩種,一種是 Spring Data JPA 默認(rèn)實(shí)現(xiàn)的,另一種是需要根據(jù)查詢的情況來自行構(gòu)建。
預(yù)生成方法
預(yù)生成方法就是我們上面看到的那些方法,因?yàn)槔^承了 JpaRepository 而擁有了父類的這些內(nèi)容。
(1)繼承 JpaRepository
public interface UserRepository extends JpaRepository<User,Long> {
}(2)使用默認(rèn)方法
//所有父類擁有的方法都可以直接調(diào)用,根據(jù)方法名也可以看出它的含義。
@Test
public void testBaseQuery() {
userRepository.findAll();
userRepository.findById(1l);
userRepository.save(user);
userRepository.delete(user);
userRepository.count();
userRepository.existsById(1l);
// ...
?? ?}7.自定義查詢
Spring Data JPA 可以根據(jù)接口方法名來實(shí)現(xiàn)數(shù)據(jù)庫(kù)操作,主要的語法是 findXXBy、readAXXBy、queryXXBy、countXXBy、getXXBy 后面跟屬性名稱,利用這個(gè)功能僅需要在定義的 Repository 中添加對(duì)應(yīng)的方法名即可,使用時(shí) Spring Boot 會(huì)自動(dòng)幫我們實(shí)現(xiàn),示例如下。
根據(jù)用戶名查詢用戶:
User findByUserName(String userName);
也可以加一些關(guān)鍵字 And、or:
User findByUserNameOrEmail(String username,String email);
修改、刪除、統(tǒng)計(jì)也是類似語法:
Long deleteById(Long id); Long countByUserName(String userName)
基本上 SQL 體系中的關(guān)鍵詞都可以使用,如 Like 、IgnoreCase、OrderBy:
List<User> findByEmailLike(String email); User findByUserNameIgnoreCase(String userName); List<User> findByUserNameOrderByEmailDesc(String email);
可以根據(jù)查詢的條件不斷地添加和拼接,Spring Boot 都可以正確解析和執(zhí)行,其他使用示例可以參考下表。
| Keyword | Sample | JPQL snippet |
|---|---|---|
| And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
| Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
| Is,Equals | findByFirstname,findByFirstnameIs,findByFirstnameEquals | … where x.firstname = ?1 |
| Between | findByStartDateBetween | … where x.startDate between ?1 and ?2 |
| LessThan | findByAgeLessThan | … where x.age < ?1 |
| LessThanEqual | findByAgeLessThanEqual | … where x.age <= ?1 |
| GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
| GreaterThanEqual | findByAgeGreaterThanEqual | … where x.age >= ?1 |
| After | findByStartDateAfter | … where x.startDate > ?1 |
| Before | findByStartDateBefore | … where x.startDate < ?1 |
| IsNull | findByAgeIsNull | … where x.age is null |
| IsNotNull,NotNull | findByAge(Is)NotNull | … where x.age not null |
| Like | findByFirstnameLike | … where x.firstname like ?1 |
| NotLike | findByFirstnameNotLike | … where x.firstname not like ?1 |
| StartingWith | findByFirstnameStartingWith | … where x.firstname like ?1(parameter bound with appended %) |
| EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1(parameter bound with prepended %) |
| Containing | findByFirstnameContaining | … where x.firstname like ?1(parameter bound wrapped in %) |
| OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
| Not | findByLastnameNot | … where x.lastname <> ?1 |
| In | findByAgeIn(Collection<Age> ages) | … where x.age in ?1 |
| NotIn | findByAgeNotIn(Collection<Age> age) | … where x.age not in ?1 |
| True | findByActiveTrue() | … where x.active = true |
| False | findByActiveFalse() | … where x.active = false |
| IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstame) = UPPER(?1) |
| Count | countByFirstName | select count(*) from ... where x.firstName = ?1 |
| Exists | existsByFirstName | like the dao.exists(Example),judge by attribution of firstName:select keyindex0_.id as col_0_0_ from key_index keyindex0_ where keyindex0_.name=? limit ? |
3、小結(jié)
第一部分講解了使用 JPA 大大解放了我們對(duì)數(shù)據(jù)庫(kù)的操作,經(jīng)常使用的 SQL 大部分都已經(jīng)被預(yù)生成,直接使用即可。另外 JPA 還有一個(gè)特點(diǎn),那就是再也不用關(guān)心數(shù)據(jù)庫(kù)的表結(jié)構(gòu)了,需要更改的時(shí)候只需要修改對(duì)應(yīng) Model 的屬性即可。在微服務(wù)架構(gòu)中,因?yàn)榉?wù)拆分得越來越小,微服務(wù)內(nèi)部只關(guān)心自己的業(yè)務(wù),需要復(fù)雜查詢的場(chǎng)景會(huì)越來越少,在微服務(wù)架構(gòu)中更推薦使用 JPA 技術(shù)。
二、Spring Data JPA高級(jí)用法
第一部分介紹了 Spring Data JPA 的使用方式和基本查詢,常用的增、刪、改、查需求 Spring Data JPA 已經(jīng)實(shí)現(xiàn)了。但對(duì)于復(fù)雜的數(shù)據(jù)庫(kù)場(chǎng)景,動(dòng)態(tài)生成方法不能滿足,對(duì)此 Spring Data JPA 提供了其他的解決方案。
1、自定義 SQL 查詢
使用 Spring Data 大部分的 SQL 都可以根據(jù)方法名定義的方式來實(shí)現(xiàn),但是由于某些原因必須使用自定義的 SQL 來查詢,Spring Data 也可以完美支持。
在 SQL 的查詢方法上面使用 @Query 注解,在注解內(nèi)寫 Hql 來查詢內(nèi)容。
@Query("select u from User u")
Page<User> findALL(Pageable pageable);當(dāng)然如果感覺使用原生 SQL 更習(xí)慣,它也是支持的,需要再添加一個(gè)參數(shù) nativeQuery = true。
@Query("select * from user u where u.nick_name = ?1", nativeQuery = true)
Page<User> findByNickName(String nickName, Pageable pageable);@Query 上面的 1 代表的是方法參數(shù)里面的順序,如果有多個(gè)參數(shù)也可以按照這個(gè)方式添加 1、2、3....。除了按照這種方式傳參外,還可以使用 @Param 來支持。
@Query("select u from User u where u.nickName = :nickName")
Page<User> findByNickName(@Param("nickName") String nickName, Pageable pageable);如涉及到刪除和修改需要加上 @Modifying,也可以根據(jù)需要添加 @Transactional 對(duì)事務(wù)的支持、操作超時(shí)設(shè)置等。
@Transactional(timeout = 10)
@Modifying
@Query("update User set userName = ?1 where id = ?2")
int modifyById(String ?userName, Long id);
@Transactional
@Modifying
@Query("delete from User where id = ?1")
void deleteById(Long id);使用已命名的查詢
除了使用 @Query 注解外,還可以預(yù)先定義好一些查詢,并為其命名,然后再 Repository 中添加相同命名的方法,定義命名的 Query:
@Entity
@NamedQueries({
? ? ? ? @NamedQuery(name = "User.findByPassWord", query = "select u from User u where u.passWord = ?1"),
? ? ? ? @NamedQuery(name = "User.findByNickName", query = "select u from User u where u.nickName = ?1"),
})
public class User {
? ? ……
}通過 @NamedQueries 注解可以定義多個(gè)命名 Query,@NamedQuery 的 name 屬性定義了 Query 的名稱,注意加上 Entity 名稱 . 作為前綴,query 屬性定義查詢語句,定義對(duì)應(yīng)的方法:
List<User> findByPassWord(String passWord); List<User> findByNickName(String nickName);
Query 查找策略
到此,我們有了三種方法來定義 Query:(1)通過方法名自動(dòng)創(chuàng)建 Query,(2)通過 @Query 注解實(shí)現(xiàn)自定義 Query,(3)通過 @NamedQuery 注解來定義 Query。那么,Spring Data JPA 如何來查找這些 Query 呢?
通過配置 @EnableJpaRepositories 的 queryLookupStrategy 屬性來配置 Query 查找策略,有如下定義。
CREATE:嘗試從查詢方法名構(gòu)造特定于存儲(chǔ)的查詢。一般的方法是從方法名中刪除一組已知的前綴,并解析方法的其余部分。USE_DECLARED_QUERY:嘗試查找已聲明的查詢,如果找不到,則拋出異常。查詢可以通過某個(gè)地方的注釋定義,也可以通過其他方式聲明。CREATE_IF_NOT_FOUND(默認(rèn)):CREATE 和 USE_DECLARED_QUERY 的組合,它首先查找一個(gè)已聲明的查詢,如果沒有找到已聲明的查詢,它將創(chuàng)建一個(gè)自定義方法基于名稱的查詢。它允許通過方法名進(jìn)行快速查詢定義,還可以根據(jù)需要引入聲明的查詢來定制這些查詢調(diào)優(yōu)。
2、分頁查詢Spring Data JPA 已經(jīng)幫我們內(nèi)置了分頁功能
在查詢的方法中,需要傳入?yún)?shù) Pageable,當(dāng)查詢中有多個(gè)參數(shù)的時(shí)候 Pageable 建議作為最后一個(gè)參數(shù)傳入。
@Query("select u from User u")
Page<User> findALL(Pageable pageable);
Page<User> findByNickName(String nickName, Pageable pageable);Pageable 是 Spring 封裝的分頁實(shí)現(xiàn)類,使用的時(shí)候需要傳入頁數(shù)、每頁條數(shù)和排序規(guī)則,Page 是 Spring 封裝的分頁對(duì)象,封裝了總頁數(shù)、分頁數(shù)據(jù)等。返回對(duì)象除使用 Page 外,還可以使用 Slice 作為返回值。
Slice<User> findByNickNameAndEmail(String nickName, String email,Pageable pageable);
Page 和 Slice 的區(qū)別如下:
Page 接口繼承自 Slice 接口,而 Slice 繼承自 Iterable 接口。
Page 接口擴(kuò)展了 Slice 接口,添加了獲取總頁數(shù)和元素總數(shù)量的方法,因此,返回 Page 接口時(shí),必須執(zhí)行兩條 SQL,一條復(fù)雜查詢分頁數(shù)據(jù),另一條負(fù)責(zé)統(tǒng)計(jì)數(shù)據(jù)數(shù)量。
返回 Slice 結(jié)果時(shí),查詢的 SQL 只會(huì)有查詢分頁數(shù)據(jù)這一條,不統(tǒng)計(jì)數(shù)據(jù)數(shù)量。
用途不一樣:Slice 不需要知道總頁數(shù)、總數(shù)據(jù)量,只需要知道是否有下一頁、上一頁,是否是首頁、尾頁等,比如前端滑動(dòng)加載一頁可用;而 Page 知道總頁數(shù)、總數(shù)據(jù)量,可以用于展示具體的頁數(shù)信息,比如后臺(tái)分頁查詢。
@Test
public void testPageQuery() ?{
int page=1,size=2;
Sort sort = new Sort(Sort.Direction.DESC, "id");
Pageable pageable = PageRequest.of(page, size, sort);
userRepository.findALL(pageable);
userRepository.findByNickName("aa", pageable);
?? ?}Sort,控制分頁數(shù)據(jù)的排序,可以選擇升序和降序。PageRequest,控制分頁的輔助類,可以設(shè)置頁碼、每頁的數(shù)據(jù)條數(shù)、排序等。
限制查詢:有時(shí)候我們只需要查詢前 N 個(gè)元素,或者只取前一個(gè)實(shí)體。
User findFirstByOrderByLastnameAsc(); User findTopByOrderByAgeDesc(); Page<User> queryFirst10ByLastname(String lastname, Pageable pageable); List<User> findFirst10ByLastname(String lastname, Sort sort); List<User> findTop10ByLastname(String lastname, Pageable pageable);
3、復(fù)雜查詢
我們可以通過 AND 或者 OR 等連接詞來不斷拼接屬性來構(gòu)建多條件查詢,但如果參數(shù)大于 6 個(gè)時(shí),方法名就會(huì)變得非常的長(zhǎng),并且還不能解決動(dòng)態(tài)多條件查詢的場(chǎng)景。到這里就需要給大家介紹另外一個(gè)利器 JpaSpecificationExecutor 了。
JpaSpecificationExecutor 是 JPA 2.0 提供的 Criteria API 的使用封裝,可以用于動(dòng)態(tài)生成 Query 來滿足我們業(yè)務(wù)中的各種復(fù)雜場(chǎng)景。Spring Data JPA 為我們提供了 JpaSpecificationExecutor 接口,只要簡(jiǎn)單實(shí)現(xiàn) toPredicate 方法就可以實(shí)現(xiàn)復(fù)雜的查詢。
我們來看一下 JpaSpecificationExecutor 的源碼:
public interface JpaSpecificationExecutor<T> {
? ?//根據(jù) Specification 條件查詢單個(gè)對(duì)象,注意的是,如果條件能查出來多個(gè)會(huì)報(bào)錯(cuò)
? ?T findOne(@Nullable Specification<T> spec);
? ?//根據(jù) Specification 條件查詢 List 結(jié)果
? ?List<T> findAll(@Nullable Specification<T> spec);
? ?//根據(jù) Specification 條件,分頁查詢
? ?Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable);
? ?//根據(jù) Specification 條件,帶排序的查詢結(jié)果
? ?List<T> findAll(@Nullable Specification<T> spec, Sort sort);
? ?//根據(jù) Specification 條件,查詢數(shù)量
? ?long count(@Nullable Specification<T> spec);
}JpaSpecificationExecutor 的源碼很簡(jiǎn)單,根據(jù) Specification 的查詢條件返回 List、Page 或者 count 數(shù)據(jù)。在使用 JpaSpecificationExecutor 構(gòu)建復(fù)雜查詢場(chǎng)景之前,我們需要了解幾個(gè)概念:
Root root,代表了可以查詢和操作的實(shí)體對(duì)象的根,開一個(gè)通過 get("屬性名") 來獲取對(duì)應(yīng)的值。CriteriaQuery query,代表一個(gè) specific 的頂層查詢對(duì)象,它包含著查詢的各個(gè)部分,比如 select 、from、where、group by、order by 等。CriteriaBuilder cb,來構(gòu)建 CritiaQuery 的構(gòu)建器對(duì)象,其實(shí)就相當(dāng)于條件或者是條件組合,并以 Predicate 的形式返回。
使用案例: 首先定義一個(gè) UserDetail 對(duì)象,作為演示的數(shù)據(jù)模型。
@Entity
public class UserDetail {
? ? @Id
? ? @GeneratedValue
? ? private Long id;
? ? @Column(nullable = false, unique = true)
? ? private Long userId;
? ? private Integer age;
? ? private String realName;
? ? private String status;
? ? private String hobby;
? ? private String introduction;
? ? private String lastLoginIp;
}創(chuàng)建 UserDetail 對(duì)應(yīng)的 Repository:
public interface UserDetailRepository extends JpaSpecificationExecutor<UserDetail>,JpaRepository<UserDetail, Long> ?{
}定義一個(gè)查詢 Page<UserDetail> 的接口:
public interface UserDetailService {
? ? public Page<UserDetail> findByCondition(UserDetailParam detailParam, Pageable pageable);
}在 UserDetailServiceImpl 中,我們來演示 JpaSpecificationExecutor 的具體使用。
@Service
public class UserDetailServiceImpl implements ?UserDetailService{
? ? @Resource
? ? private UserDetailRepository userDetailRepository;
? ? @Override
? ? public Page<UserDetail> findByCondition(UserDetailParam detailParam, Pageable pageable){
? ? ? ? return userDetailRepository.findAll((root, query, cb) -> {
? ? ? ? ? ? List<Predicate> predicates = new ArrayList<Predicate>();
? ? ? ? ? ? //equal 示例
? ? ? ? ? ? if (!StringUtils.isNullOrEmpty(detailParam.getIntroduction())){
? ? ? ? ? ? ? ? predicates.add(cb.equal(root.get("introduction"),detailParam.getIntroduction()));
? ? ? ? ? ? }
? ? ? ? ? ? //like 示例
? ? ? ? ? ? if (!StringUtils.isNullOrEmpty(detailParam.getRealName())){
? ? ? ? ? ? ? ? predicates.add(cb.like(root.get("realName"),"%"+detailParam.getRealName()+"%"));
? ? ? ? ? ? }
? ? ? ? ? ? //between 示例
? ? ? ? ? ? if (detailParam.getMinAge()!=null && detailParam.getMaxAge()!=null) {
? ? ? ? ? ? ? ? Predicate agePredicate = cb.between(root.get("age"), detailParam.getMinAge(), detailParam.getMaxAge());
? ? ? ? ? ? ? ? predicates.add(agePredicate);
? ? ? ? ? ? }
? ? ? ? ? ? //greaterThan 大于等于示例
? ? ? ? ? ? if (detailParam.getMinAge()!=null){
? ? ? ? ? ? ? ? predicates.add(cb.greaterThan(root.get("age"),detailParam.getMinAge()));
? ? ? ? ? ? }
? ? ? ? ? ? return query.where(predicates.toArray(new Predicate[predicates.size()])).getRestriction();
? ? ? ? }, pageable);
? ? }
}上面的示例是根據(jù)不同條件來動(dòng)態(tài)查詢 UserDetail 分頁數(shù)據(jù),UserDetailParam 是參數(shù)的封裝,示例中使用了常用的大于、like、等于等示例,根據(jù)這個(gè)思路我們可以不斷擴(kuò)展完成更復(fù)雜的動(dòng)態(tài) SQL 查詢。
@RunWith(SpringRunner.class)
@SpringBootTest
public class JpaSpecificationTests {
? ? @Resource
? ? private UserDetailService userDetailService;
? ? @Test
? ? public void testFindByCondition() ?{
? ? ? ? int page=0,size=10;
? ? ? ? Sort sort = new Sort(Sort.Direction.DESC, "id");
? ? ? ? Pageable pageable = PageRequest.of(page, size, sort);
? ? ? ? UserDetailParam param=new UserDetailParam();
? ? ? ? param.setIntroduction("程序員");
? ? ? ? param.setMinAge(10);
? ? ? ? param.setMaxAge(30);
? ? ? ? Page<UserDetail> page1=userDetailService.findByCondition(param,pageable);
? ? ? ? for (UserDetail userDetail:page1){
? ? ? ? ? ? System.out.println("userDetail: "+userDetail.toString());
? ? ? ? }
? ? }
}4、多表查詢
多表查詢?cè)?Spring Data JPA 中有兩種實(shí)現(xiàn)方式,第一種是利用 Hibernate 的級(jí)聯(lián)查詢來實(shí)現(xiàn),第二種是創(chuàng)建一個(gè)結(jié)果集的接口來接收連表查詢后的結(jié)果,這里主要介紹第二種方式。
我們還是使用上面的 UserDetail 作為數(shù)據(jù)模型來使用,定義一個(gè)結(jié)果集的接口類,接口類的內(nèi)容來自于用戶表和用戶詳情表。
public interface UserInfo {
? ? String getUserName();
? ? String getEmail();
? ? String getAddress();
? ? String getHobby();
}在運(yùn)行中 Spring 會(huì)給接口(UserInfo)自動(dòng)生產(chǎn)一個(gè)代理類來接收返回的結(jié)果,代碼中使用 getXX 的形式來獲取。
在 UserDetailRepository 中添加查詢的方法,返回類型設(shè)置為 UserInfo,特別注意這里的 SQL 是 HQL,需要寫類的名和屬性,這塊很容易出錯(cuò)
@Query("select u.userName as userName, u.email as email, d.introduction as introduction , d.hobby as hobby from User u , UserDetail d " +
? ? ? ? ? ? "where u.id=d.userId ?and ?d.hobby = ?1 ")
List<UserInfo> findUserInfo(String hobby);測(cè)試驗(yàn)證
@Test
public void testUserInfo() ?{
? ? List<UserInfo> userInfos=userDetailRepository.findUserInfo("釣魚");
? ? for (UserInfo userInfo:userInfos){
? ? ? ? System.out.println("userInfo: "+userInfo.getUserName()+"-"+userInfo.getEmail()+"-"+userInfo.getHobby()+"-"+userInfo.getIntroduction());
? ? }
}測(cè)試后返回
userInfo: aa-aa@126.com-釣魚-程序員
5、小結(jié)
Spring Data JPA 使用動(dòng)態(tài)注入的原理,根據(jù)方法名動(dòng)態(tài)生成方法的實(shí)現(xiàn),因此根據(jù)方法名實(shí)現(xiàn)數(shù)據(jù)查詢,即可滿足日常絕大部分使用場(chǎng)景。除了這種查詢方式之外,Spring Data JPA 還支持多種自定義查詢來滿足更多復(fù)雜場(chǎng)景的使用,兩種方式相結(jié)合可以靈活滿足項(xiàng)目對(duì) Orm 層的需求。
通過學(xué)習(xí) Spring Data JPA 也可以看出 Spring Boot 的設(shè)計(jì)思想,80% 的需求通過默認(rèn)、簡(jiǎn)單的方式實(shí)現(xiàn),滿足大部分使用場(chǎng)景,對(duì)于另外 20% 復(fù)雜的場(chǎng)景,提供另外的技術(shù)手段來解決。Spring Data JPA 中根據(jù)方法名動(dòng)態(tài)實(shí)現(xiàn) SQL,組件環(huán)境自動(dòng)配置等細(xì)節(jié),都是將 Spring Boot 約定優(yōu)于配置的思想體現(xiàn)的淋淋盡致。
三、Spring Data JPA多數(shù)據(jù)源使用
1、前言
項(xiàng)目中使用多個(gè)數(shù)據(jù)源在以往工作中比較常見,微服務(wù)架構(gòu)中不建議一個(gè)項(xiàng)目使用多個(gè)數(shù)據(jù)源。在微服務(wù)架構(gòu)下,一個(gè)微服務(wù)擁有自己獨(dú)立的一個(gè)數(shù)據(jù)庫(kù),如果此微服務(wù)要使用其他數(shù)據(jù)庫(kù)的數(shù)據(jù),需要調(diào)用對(duì)應(yīng)庫(kù)的微服務(wù)接口來調(diào)用,而不是在一個(gè)項(xiàng)目中連接使用多個(gè)數(shù)據(jù)庫(kù),這樣微服務(wù)更獨(dú)立、更容易水平擴(kuò)展。
雖然在微服務(wù)架構(gòu)下,不提倡一個(gè)項(xiàng)目擁有多個(gè)數(shù)據(jù)源,但在 Spring Boot 體系中,項(xiàng)目實(shí)現(xiàn)多數(shù)據(jù)源調(diào)用卻是一件很容易的事情,本節(jié)課將介紹 Spring Data JPA 多數(shù)據(jù)源的使用。
Spring Data JPA 使用多數(shù)據(jù)源的整體思路是,配置不同的數(shù)據(jù)源,在啟動(dòng)時(shí)分別加載多個(gè)數(shù)據(jù)源配置,并且注入到不同的 repository 中。這樣不同的 repository 包就有不同的數(shù)據(jù)源,使用時(shí)注入對(duì)應(yīng)包下的 repository,就會(huì)使用對(duì)應(yīng)數(shù)據(jù)源的操作。
項(xiàng)目結(jié)構(gòu)如下:

onfig 啟動(dòng)時(shí)加載、配置多數(shù)據(jù)源;
model 存放數(shù)據(jù)操作的實(shí)體類;
repository 目錄下有兩個(gè)包路徑 test1 和 test2 ,分別代表兩個(gè)不同數(shù)據(jù)源下的倉(cāng)庫(kù),這兩個(gè)包下的 repository 可以相同也可以不同。
2、多數(shù)據(jù)源的支持
配置 Spring Data JPA 對(duì)多數(shù)據(jù)源的使用,一般分為以下幾步:
- 創(chuàng)建數(shù)據(jù)庫(kù) test1 和 test2
- 配置多數(shù)據(jù)源
- 不同源的 repository 放入不同包路徑
- 聲明不同的包路徑下使用不同的數(shù)據(jù)源、事務(wù)支持
- 不同的包路徑下創(chuàng)建對(duì)應(yīng)的 repository
- 測(cè)試使用
上面的一些步驟我們?cè)谇懊鎯烧n中已經(jīng)講過了,這里只補(bǔ)充不同的內(nèi)容。
spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/test1?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true spring.datasource.primary.username=root spring.datasource.primary.password=root spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/test2?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true spring.datasource.secondary.username=root spring.datasource.secondary.password=root spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver # 設(shè)置將項(xiàng)目中的 SQL 格式化后打印出來,方便在開發(fā)過程中調(diào)試跟蹤。 #SQL 輸出 spring.jpa.show-sql=true spring.jpa.properties.hibernate.hbm2ddl.auto=create spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect #format 一下 SQL 進(jìn)行輸出 spring.jpa.properties.hibernate.format_sql=true
創(chuàng)建 DataSourceConfig 添加 @Configuration 注解,在項(xiàng)目啟動(dòng)時(shí)運(yùn)行初始化數(shù)據(jù)庫(kù)資源。
@Configuration
public class DataSourceConfig {
}在 DataSourceConfig 類中加載配置文件,利用 ConfigurationProperties 自動(dòng)裝配的特性加載兩個(gè)數(shù)據(jù)源。
加載第一個(gè)數(shù)據(jù)源,數(shù)據(jù)源配置以 spring.datasource.primary 開頭,注意當(dāng)有多個(gè)數(shù)據(jù)源時(shí),需要將其中一個(gè)標(biāo)注為 @Primary,作為默認(rèn)的數(shù)據(jù)源使用。
@Bean(name = "primaryDataSource")
@Primary
@ConfigurationProperties("spring.datasource.primary")
public DataSource firstDataSource() {
? ? return DataSourceBuilder.create().build();
}加載第二個(gè)數(shù)據(jù)源,數(shù)據(jù)源配置以 spring.datasource.secondary 為開頭。
@Bean(name = "secondaryDataSource")
@ConfigurationProperties("spring.datasource.secondary")
public DataSource secondDataSource() {
? ? return DataSourceBuilder.create().build();
}加載 JPA 的相關(guān)配置信息,JpaProperties 是 JPA 的一些屬性配置信息,構(gòu)建 LocalEntityManagerFactoryBean 需要參數(shù)信息注入到方法中。
@Autowired
private JpaProperties jpaProperties;
@Autowired
private HibernateProperties hibernateProperties;
@Bean(name = "vendorProperties")
public Map<String, Object> getVendorProperties() {
? ? return hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings());
}第一個(gè)數(shù)據(jù)源的加載配置過程
首先來看第一個(gè)數(shù)據(jù)源的加載配置過程,創(chuàng)建 PrimaryConfig 類,將上面創(chuàng)建好的第一個(gè)數(shù)據(jù)源注入到類中,添加 @Configuration 和 @EnableTransactionManagement 注解,第一個(gè)代表啟動(dòng)時(shí)加載,第二個(gè)注
解表示啟用事務(wù),同時(shí)將第一個(gè)數(shù)據(jù)源和 JPA 配置信息注入到類中。
@Configuration
@EnableTransactionManagement
public class PrimaryConfig {
? ? @Autowired
? ? @Qualifier("primaryDataSource")
? ? private DataSource primaryDataSource;
? ? @Autowired
? ? @Qualifier("vendorProperties")
? ? private Map<String, Object> vendorProperties;
}LocalEntityManagerFactoryBean 負(fù)責(zé)創(chuàng)建一個(gè)適合于僅使用 JPA 進(jìn)行數(shù)據(jù)訪問的環(huán)境的 EntityManager,構(gòu)建的時(shí)候需要指明提示實(shí)體類的包路徑、數(shù)據(jù)源和 JPA 配置信息。
@Bean(name = "entityManagerFactoryPrimary")
@Primary
public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary (EntityManagerFactoryBuilder builder) {
? ? return builder
? ? ? ? ? ? .dataSource(primaryDataSource)
? ? ? ? ? ? .properties(vendorProperties)
? ? ? ? ? ? .packages("com.neo.model") //設(shè)置實(shí)體類所在位置
? ? ? ? ? ? .persistenceUnit("primaryPersistenceUnit")
? ? ? ? ? ? .build();
}利用上面的 entityManagerFactoryPrimary() 方法構(gòu)建好最終的 EntityManager。
@Bean(name = "entityManagerPrimary")
@Primary
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
? ? return entityManagerFactoryPrimary(builder).getObject().createEntityManager();
}EntityManager 是 JPA 中用于增、刪、改、查的接口,它的作用相當(dāng)于一座橋梁,連接內(nèi)存中的 Java 對(duì)象和數(shù)據(jù)庫(kù)的數(shù)據(jù)存儲(chǔ)。使用 EntityManager 中的相關(guān)接口對(duì)數(shù)據(jù)庫(kù)實(shí)體進(jìn)行操作的時(shí)候, EntityManager
會(huì)跟蹤實(shí)體對(duì)象的狀態(tài),并決定在特定時(shí)刻將對(duì)實(shí)體的操作映射到數(shù)據(jù)庫(kù)操作上面,同時(shí)給數(shù)據(jù)源添加上 JPA 事務(wù)。
@Bean(name = "transactionManagerPrimary")
@Primary
PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {
? ? return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
}最后一步最為關(guān)鍵,將我們?cè)陬愔信渲煤玫?EntityManager 和事務(wù)信息注入到對(duì)應(yīng)數(shù)據(jù)源的 repository 目錄下,這樣此目錄下的 repository 就會(huì)擁有對(duì)應(yīng)數(shù)據(jù)源和事務(wù)的信息。
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
? ? ? ? entityManagerFactoryRef="entityManagerFactoryPrimary",
? ? ? ? transactionManagerRef="transactionManagerPrimary",
? ? ? ? basePackages= { "com.neo.repository.test1" })//設(shè)置dao(repo)所在位置
public class PrimaryConfig {}其中,basePackages 支持設(shè)置多個(gè)包路徑,例如,basePackages= { "com.neo.repository.test1","com.neo.repository.test3" }
第二個(gè)數(shù)據(jù)源的加載配置過程
第二個(gè)數(shù)據(jù)源配置和第一個(gè)數(shù)據(jù)源配置類似,只是方法上去掉了注解:@Primary,第二個(gè)數(shù)據(jù)源數(shù)據(jù)源加載配置類 SecondaryConfig 完整代碼如下:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
? ? ? ? entityManagerFactoryRef="entityManagerFactorySecondary",
? ? ? ? transactionManagerRef="transactionManagerSecondary",
? ? ? ? basePackages= { "com.neo.repository.test2" })
public class SecondaryConfig {
? ? @Autowired
? ? @Qualifier("secondaryDataSource")
? ? private DataSource secondaryDataSource;
? ? @Autowired
? ? @Qualifier("vendorProperties")
? ? private Map<String, Object> vendorProperties;
? ? @Bean(name = "entityManagerFactorySecondary")
? ? public LocalContainerEntityManagerFactoryBean entityManagerFactorySecondary (EntityManagerFactoryBuilder builder) {
? ? ? ? return builder
? ? ? ? ? ? ? ? .dataSource(secondaryDataSource)
? ? ? ? ? ? ? ? .properties(vendorProperties)
? ? ? ? ? ? ? ? .packages("com.neo.model")
? ? ? ? ? ? ? ? .persistenceUnit("secondaryPersistenceUnit")
? ? ? ? ? ? ? ? .build();
? ? }
? ? @Bean(name = "entityManagerSecondary")
? ? public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
? ? ? ? return entityManagerFactorySecondary(builder).getObject().createEntityManager();
? ? }
? ? @Bean(name = "transactionManagerSecondary")
? ? PlatformTransactionManager transactionManagerSecondary(EntityManagerFactoryBuilder builder) {
? ? ? ? return new JpaTransactionManager(entityManagerFactorySecondary(builder).getObject());
? ? }
}到此多數(shù)據(jù)源的配置就完成了,項(xiàng)目中使用哪個(gè)數(shù)據(jù)源的操作,就注入對(duì)應(yīng)包下的 repository 進(jìn)行操作即可,接下來我們對(duì)上面配置好的數(shù)據(jù)源進(jìn)行測(cè)試。
創(chuàng)建 UserRepositoryTests 測(cè)試類,將兩個(gè)包下的 repository 都注入到測(cè)試類中:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserRepositoryTests {
? ? @Resource
? ? private UserTest1Repository userTest1Repository;
? ? @Resource
? ? private UserTest2Repository userTest2Repository;
}首先測(cè)試兩個(gè)數(shù)據(jù)庫(kù)中都存入數(shù)據(jù),數(shù)據(jù)源1插入 2 條用戶信息,數(shù)據(jù)源2插入 1 條用戶信息。
@Test
public void testSave() throws Exception {
? ? Date date = new Date();
? ? DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
? ? String formattedDate = dateFormat.format(date);
? ? userTest1Repository.save(new User("aa", "aa123456","aa@126.com", "aa", ?formattedDate));
? ? userTest1Repository.save(new User("bb", "bb123456","bb@126.com", "bb", ?formattedDate));
? ? userTest2Repository.save(new User("cc", "cc123456","cc@126.com", "cc", ?formattedDate));
}執(zhí)行完測(cè)試用例后查看數(shù)據(jù)庫(kù),發(fā)現(xiàn) test1 庫(kù)有兩條數(shù)據(jù),test2 有一條,證明兩個(gè)數(shù)據(jù)源均保存數(shù)據(jù)正常。下面繼續(xù)測(cè)試刪除功能,使用兩個(gè)數(shù)據(jù)源的 repository 將用戶信息全部刪除。
@Test
public void testDelete() throws Exception {
? ? userTest1Repository.deleteAll();
? ? userTest2Repository.deleteAll();
}執(zhí)行完測(cè)試用例后,發(fā)現(xiàn) test1 庫(kù)和 test2 庫(kù)用戶表的信息已經(jīng)被清空,證明多數(shù)據(jù)源刪除成功。
3、小結(jié)
Spring Data JPA 通過在啟動(dòng)時(shí)加載不同的數(shù)據(jù)源,并將不同的數(shù)據(jù)源注入到不同的 repository 包下,從而實(shí)現(xiàn)項(xiàng)目多數(shù)據(jù)源操作,在項(xiàng)目中使用多數(shù)據(jù)源時(shí),需要用到哪個(gè)數(shù)據(jù)源,只需要將對(duì)應(yīng)包下的 repository 注入操作即可。本課示例中以兩個(gè)數(shù)據(jù)源作為演示,但其實(shí)三個(gè)或者更多數(shù)據(jù)源配置、操作,都可以按照上面方法進(jìn)行配置使用。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- SpringBoot+Spring?Data?JPA整合H2數(shù)據(jù)庫(kù)的示例代碼
- 解決Spring Data Jpa 實(shí)體類自動(dòng)創(chuàng)建數(shù)據(jù)庫(kù)表失敗問題
- Springboot使用Spring Data JPA實(shí)現(xiàn)數(shù)據(jù)庫(kù)操作
- Spring Data JPA進(jìn)行數(shù)據(jù)分頁與排序的方法
- springboot使用spring-data-jpa操作MySQL數(shù)據(jù)庫(kù)
- Spring boot中使用Spring-data-jpa方便快捷的訪問數(shù)據(jù)庫(kù)(推薦)
- 詳解基于Spring Boot與Spring Data JPA的多數(shù)據(jù)源配置
- Spring?Data?JPA框架的核心概念與Repository接口詳解
相關(guān)文章
SpringBoot 返回Json實(shí)體類屬性大小寫的解決
這篇文章主要介紹了SpringBoot 返回Json實(shí)體類屬性大小寫的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
Springboot內(nèi)嵌tomcat應(yīng)用原理深入分析
懂得SpringBoot的童鞋應(yīng)該很清楚,不管應(yīng)用程序是屬于何種類型,都是一個(gè)Main方法走遍天下,對(duì)于web應(yīng)用,只需要引入spring-boot-starter-web中這個(gè)依賴,應(yīng)用程序就好像直接給我們來了個(gè)tomcat一樣,對(duì)于嵌入式Tomcat,其實(shí)也非常簡(jiǎn)單,就是調(diào)用Tomcat提供的外部類2022-09-09
Springboot利于第三方服務(wù)進(jìn)行ip定位獲取省份城市
本文主要介紹了Springboot利于第三方服務(wù)進(jìn)行ip定位獲取省份城市,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
java基礎(chǔ)學(xué)習(xí)JVM中GC的算法
這篇文章主要介紹了java基礎(chǔ)學(xué)習(xí)JVM中GC的算法,通過圖文加深對(duì)GC算法思路的理解。2017-11-11
Spring+SpringMVC+MyBatis深入學(xué)習(xí)及搭建(二)之MyBatis原始Dao開發(fā)和mapper代理開發(fā)
這篇文章主要介紹了Spring+SpringMVC+MyBatis深入學(xué)習(xí)及搭建(二)之MyBatis原始Dao開發(fā)和mapper代理開發(fā),需要的朋友可以參考下2017-05-05
javaWEB中前后臺(tái)亂碼問題的解決方法總結(jié)
下面小編就為大家?guī)硪黄猨avaWEB中前后臺(tái)亂碼問題的解決方法總結(jié)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-08-08
Java調(diào)用JavaScript實(shí)現(xiàn)字符串計(jì)算器代碼示例
這篇文章主要介紹了Java調(diào)用JavaScript實(shí)現(xiàn)字符串計(jì)算器代碼示例,具有一定參考價(jià)值,需要的朋友可以了解下。2017-12-12

