在上篇文章Spring Boot(二):Web 綜合開發(fā)中簡單介紹了一下 Spring Boot Jpa 的基礎(chǔ)性使用,這篇文章將更加全面的介紹 Spring Boot Jpa 常見用法以及注意事項(xiàng)。
使用 Spring Boot Jpa 開發(fā)時(shí),發(fā)現(xiàn)國內(nèi)對 Spring Boot Jpa 全面介紹的文章比較少案例也比較零碎,因此寫文章總結(jié)一下。本人也正在翻譯Spring Data JPA 參考指南,有興趣的同學(xué)歡迎聯(lián)系我,一起加入翻譯中!
Spring Boot Jpa 介紹
首先了解 Jpa 是什么?
Jpa (Java Persistence API) 是 Sun 官方提出的 Java 持久化規(guī)范。它為 Java 開發(fā)人員提供了一種對象/關(guān)聯(lián)映射工具來管理 Java 應(yīng)用中的關(guān)系數(shù)據(jù)。它的出現(xiàn)主要是為了簡化現(xiàn)有的持久化開發(fā)工作和整合 ORM 技術(shù),結(jié)束現(xiàn)在 Hibernate,TopLink,JDO 等 ORM 框架各自為營的局面。
值得注意的是,Jpa是在充分吸收了現(xiàn)有 Hibernate,TopLink,JDO 等 ORM 框架的基礎(chǔ)上發(fā)展而來的,具有易于使用,伸縮性強(qiáng)等優(yōu)點(diǎn)。從目前的開發(fā)社區(qū)的反應(yīng)上看,Jpa 受到了極大的支持和贊揚(yáng),其中就包括了 Spring 與 EJB3. 0的開發(fā)團(tuán)隊(duì)。
注意: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 Boot Jpa
Spring Boot Jpa 是 Spring 基于 ORM 框架、Jpa 規(guī)范的基礎(chǔ)上封裝的一套 Jpa 應(yīng)用框架,可使開發(fā)者用極簡的代碼即可實(shí)現(xiàn)對數(shù)據(jù)的訪問和操作。它提供了包括增刪改查等在內(nèi)的常用功能,且易于擴(kuò)展!學(xué)習(xí)并使用 Spring Data Jpa 可以極大提高開發(fā)效率!
Spring Boot Jpa 讓我們解脫了 DAO 層的操作,基本上所有 CRUD 都可以依賴于它來實(shí)現(xiàn)
基本查詢
基本查詢也分為兩種,一種是Spring Data 默認(rèn)實(shí)現(xiàn),一種是根據(jù)查詢的方法來自動(dòng)解析成 SQL。
預(yù)先生成方法
pring Boot Jpa 默認(rèn)預(yù)先生成了一些基本的CURD的方法,例如:增、刪、改等等
1 繼承 JpaRepository
public interface UserRepository extends JpaRepository<User, Long> {
}
2 使用默認(rèn)方法
@Test
public void testBaseQuery() throws Exception {
User user=new User();
userRepository.findAll();
userRepository.findOne(1l);
userRepository.save(user);
userRepository.delete(user);
userRepository.count();
userRepository.exists(1l);
// ...
}
就不解釋了根據(jù)方法名就看出意思來.
自定義簡單查詢
自定義的簡單查詢就是根據(jù)方法名來自動(dòng)生成 SQL,主要的語法是findXXBy,readAXXBy,queryXXBy,countXXBy, getXXBy后面跟屬性名稱:
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);
具體的關(guān)鍵字,使用方法和生產(chǎn)成SQL如下表所示
| 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 | 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 ages) | … where x.age in ?1 |
| NotIn | findByAgeNotIn(Collection 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) |
復(fù)雜查詢
在實(shí)際的開發(fā)中我們需要用到分頁、刪選、連表等查詢的時(shí)候就需要特殊的方法或者自定義 SQL
分頁查詢
分頁查詢在實(shí)際使用中非常普遍了,Spring Boot Jpa 已經(jīng)幫我們實(shí)現(xiàn)了分頁的功能,在查詢的方法中,需要傳入?yún)?shù)·Pageable· ,當(dāng)查詢中有多個(gè)參數(shù)的時(shí)候·Pageable·建議做為最后一個(gè)參數(shù)傳入.
Page<User> findALL(Pageable pageable);
Page<User> findByUserName(String userName,Pageable pageable);
Pageable 是 Spring 封裝的分頁實(shí)現(xiàn)類,使用的時(shí)候需要傳入頁數(shù)、每頁條數(shù)和排序規(guī)則
@Test
public void testPageQuery() throws Exception {
int page=1,size=10;
Sort sort = new Sort(Direction.DESC, "id");
Pageable pageable = new PageRequest(page, size, sort);
userRepository.findALL(pageable);
userRepository.findByUserName("testName", pageable);
}
限制查詢
有時(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);
自定義SQL查詢
其實(shí) Spring Data 覺大部分的 SQL 都可以根據(jù)方法名定義的方式來實(shí)現(xiàn),但是由于某些原因我們想使用自定義的 SQL 來查詢,Spring Data 也是完美支持的;在 SQL 的查詢方法上面使用@Query注解,如涉及到刪除和修改在需要加上@Modifying.也可以根據(jù)需要添加 @Transactional對事物的支持,查詢超時(shí)的設(shè)置等。
@Modifying
@Query("update User u set u.userName = ?1 where u.id = ?2")
int modifyByIdAndUserId(String userName, Long id);
@Transactional
@Modifying
@Query("delete from User where id = ?1")
void deleteByUserId(Long id);
@Transactional(timeout = 10)
@Query("select u from User u where u.emailAddress = ?1")
User findByEmailAddress(String emailAddress);
多表查詢
多表查詢 Spring Boot Jpa 中有兩種實(shí)現(xiàn)方式,第一種是利用 Hibernate 的級聯(lián)查詢來實(shí)現(xiàn),第二種是創(chuàng)建一個(gè)結(jié)果集的接口來接收連表查詢后的結(jié)果,這里主要第二種方式。
首先需要定義一個(gè)結(jié)果集的接口類。
public interface HotelSummary {
City getCity();
String getName();
Double getAverageRating();
default Integer getAverageRatingRounded() {
return getAverageRating() == null ? null : (int) Math.round(getAverageRating());
}
}
查詢的方法返回類型設(shè)置為新創(chuàng)建的接口
@Query("select h.city as city, h.name as name, avg(r.rating) as averageRating "
- "from Hotel h left outer join h.reviews r where h.city = ?1 group by h")
Page<HotelSummary> findByCity(City city, Pageable pageable);
@Query("select h.name as name, avg(r.rating) as averageRating "
- "from Hotel h left outer join h.reviews r group by h")
Page<HotelSummary> findByCity(Pageable pageable);
使用
Page<HotelSummary> hotels = this.hotelRepository.findByCity(new PageRequest(0, 10, Direction.ASC, "name"));
for(HotelSummary summay:hotels){
System.out.println("Name" +summay.getName());
}
在運(yùn)行中 Spring 會(huì)給接口(HotelSummary)自動(dòng)生產(chǎn)一個(gè)代理類來接收返回的結(jié)果,代碼匯總使用 getXX的形式來獲取
多數(shù)據(jù)源的支持
同源數(shù)據(jù)庫的多源支持
日常項(xiàng)目中因?yàn)槭褂玫姆植际介_發(fā)模式,不同的服務(wù)有不同的數(shù)據(jù)源,常常需要在一個(gè)項(xiàng)目中使用多個(gè)數(shù)據(jù)源,因此需要配置 Spring Boot Jpa 對多數(shù)據(jù)源的使用,一般分一下為三步:
- 1 配置多數(shù)據(jù)源
- 2 不同源的實(shí)體類放入不同包路徑
- 3 聲明不同的包路徑下使用不同的數(shù)據(jù)源、事務(wù)支持
異構(gòu)數(shù)據(jù)庫多源支持
比如我們的項(xiàng)目中,即需要對 mysql 的支持,也需要對 Mongodb 的查詢等。
實(shí)體類聲明@Entity 關(guān)系型數(shù)據(jù)庫支持類型、聲明@Document 為 Mongodb 支持類型,不同的數(shù)據(jù)源使用不同的實(shí)體就可以了
interface PersonRepository extends Repository<Person, Long> {
…
}
@Entity
public class Person {
…
}
interface UserRepository extends Repository<User, Long> {
…
}
@Document
public class User {
…
}
但是,如果 User 用戶既使用 Mysql 也使用 Mongodb 呢,也可以做混合使用
interface JpaPersonRepository extends Repository<Person, Long> {
…
}
interface MongoDBPersonRepository extends Repository<Person, Long> {
…
}
@Entity
@Document
public class Person {
…
}
多數(shù)據(jù)源的支持
同源數(shù)據(jù)庫的多源支持
日常項(xiàng)目中因?yàn)槭褂玫姆植际介_發(fā)模式,不同的服務(wù)有不同的數(shù)據(jù)源,常常需要在一個(gè)項(xiàng)目中使用多個(gè)數(shù)據(jù)源,因此需要配置 Spring Boot Jpa 對多數(shù)據(jù)源的使用,一般分一下為三步:
1 配置多數(shù)據(jù)源
2 不同源的實(shí)體類放入不同包路徑
3 聲明不同的包路徑下使用不同的數(shù)據(jù)源、事務(wù)支持
異構(gòu)數(shù)據(jù)庫多源支持
比如我們的項(xiàng)目中,即需要對 mysql 的支持,也需要對 Mongodb 的查詢等。
實(shí)體類聲明@Entity 關(guān)系型數(shù)據(jù)庫支持類型、聲明@Document 為 Mongodb 支持類型,不同的數(shù)據(jù)源使用不同的實(shí)體就可以了
interface PersonRepository extends Repository<Person, Long> {
…
}
@Entity
public class Person {
…
}
interface UserRepository extends Repository<User, Long> {
…
}
@Document
public class User {
…
}
但是,如果 User 用戶既使用 Mysql 也使用 Mongodb 呢,也可以做混合使用
interface JpaPersonRepository extends Repository<Person, Long> {
…
}
interface MongoDBPersonRepository extends Repository<Person, Long> {
…
}
@Entity
@Document
public class Person {
…
}
也可以通過對不同的包路徑進(jìn)行聲明,比如 A 包路徑下使用 mysql,B 包路徑下使用 MongoDB
@EnableJpaRepositories(basePackages = "com.neo.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.neo.repositories.mongo")
interface Configuration { }
也可以通過對不同的包路徑進(jìn)行聲明,比如 A 包路徑下使用 mysql,B 包路徑下使用 MongoDB
@EnableJpaRepositories(basePackages = "com.neo.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.neo.repositories.mongo")
interface Configuration { }
其它
使用枚舉
使用枚舉的時(shí)候,我們希望數(shù)據(jù)庫中存儲(chǔ)的是枚舉對應(yīng)的 String 類型,而不是枚舉的索引值,需要在屬性上面添加@Enumerated(EnumType.STRING) 注解
@Enumerated(EnumType.STRING)
@Column(nullable = true)
private UserType type;
不需要和數(shù)據(jù)庫映射的屬性
正常情況下我們在實(shí)體類上加入注解@Entity,就會(huì)讓實(shí)體類和表相關(guān)連如果其中某個(gè)屬性我們不需要和數(shù)據(jù)庫來關(guān)聯(lián)只是在展示的時(shí)候做計(jì)算,只需要加上@Transient屬性既可。
@Transient
private String userName;
源碼案例
文章內(nèi)容已經(jīng)升級到 Spring Boot 2.x
參考
Spring Data JPA - Reference Documentation
轉(zhuǎn)載自:http://www.ityouknow.com/springboot/2016/08/20/spring-boot-jpa.html