Spring Boot(六)——Spring Date Jpa
Spring Date Jpa介紹
什麼是JPA?
JPA是Java Persistence API的簡稱,中文名Java持久層API,是JDK5.0註解或XML描述物件-關係表的對映關係,並將執行期的實體物件持久化到資料庫中。
Sun引入新的JPAORM規範出於兩個原因:其一,簡化現有JavaEE和JavaSE應用開發工作;其二,Sun希望整合ORM技術,結束現在Hibernate,TopLink,JDO等ORM框架各自為營的局面,實現天下歸一。值得注意的是,JPA是在充分吸收了現有Hibernate,TopLink,JDO等ORM框架的基礎上發展而來的,具有易於使用,伸縮性強等優點。
JPA是一套規範,不是一套產品。也就說JPA規範中提供的只是一些介面,顯然介面不能直接拿來使用。雖然應用程式可以面向介面程式設計,但JPA底層一定需要某種JPA實現,否則JPA依然無法使用。
Spring Date Jpa
JPA誕生的緣由是為了整合第三方ORM框架,Spring為了能夠更好的完善持久化這一塊,於是就有了Spring-data-**這一系列包。包括:Spring-data-jpa,Spring-data-template,Spring-data-mongodb,Spring-data-redis。所以,Spring Data JPA 是 Spring 基於 ORM 框架、JPA 規範的基礎上封裝的一套JPA應用框架,可使開發者用極簡的程式碼即可實現對資料的訪問和操作。它提供了包括增刪改查等在內的常用功能,且易於擴充套件!學習並使用 Spring Data JPA 可以極大提高開發效率!
基本配置和操作
1、在第一節搭建好的基本環境pom.xml新增相關的依賴,這裡用到的資料庫是mysql,所以也要新增mysql的依賴:
<!--JPA模組--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
2、建立相關實體類(需要相關的sql語句,請到文章最下方的github):
使用者實體:User
package com.lyh.demo.entity;
import com.lyh.demo.entity.enums.Sex;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.io.Serializable;
@Entity
public class User implements Serializable{
private static final long serialVersionUID = 1L;
@Id
private long id;
private String userName;
private String password;
private int age;
private Sex sex;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Sex getSex() {
return sex;
}
public void setSex(Sex sex) {
this.sex = sex;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", userName='" + userName + '\'' +
", password='" + password + '\'' +
", age=" + age +
", sex=" + sex +
'}';
}
}
性別列舉:Sex
package com.lyh.demo.entity.enums;
public enum Sex {
男,女
}
3、常用的CRUB(crud是指在做計算處理時的增加(Create)、讀取查詢(Retrieve)、更新(Update)和刪除(Delete)幾個單詞的首字母簡寫)操作:
新建包com.lyh.demo.repository後建立UserRepository介面,繼承JpaRepository
public interface UserRepository extends JpaRepository<User,Long> {}
使用JpaRepository自帶的方法,新建包com.lyh.demo.controller後新建類JpaController:
public class JpaController {
@Autowired
UserRepository userRepository;
@Autowired
GameScoreRepository gameScoreRepository;
@RequestMapping("/findAll")
public void findAll(){
//使用預設方法findAll()獲取所用資料
List<User> userList = userRepository.findAll();
if(userList !=null){
for (User user : userList){
System.out.println(user.toString());
}
}
/*
User user =userRepository.findOne(1L);
user =userRepository.findOne(1L);
這些預設方法可根據方法名可以看出方法的作用,這裡就不一一說明了
userRepository.save(user);
userRepository.delete(user);
userRepository.deleteAll();
userRepository.count();
userRepository.exists(1L);*/
}
}
瀏覽器訪問:後可以看到終端輸出sql語句和內容:
Hibernate: select user0_.id as id1_1_, user0_.age as age2_1_, user0_.password as password3_1_, user0_.sex as sex4_1_, user0_.user_name as user_nam5_1_ from user user0_
User{id=0, userName='null', password='null', age=0, sex=女}
User{id=1, userName='小明', password='123456', age=20, sex=男}
User{id=2, userName='小明', password='1234567', age=21, sex=男}
User{id=3, userName='小紅', password='12345678', age=22, sex=女}
User{id=4, userName='小李', password='12345678', age=25, sex=男}
User{id=5, userName='小陳', password='12345678', age=20, sex=男}
User{id=6, userName='小錢', password='12345678', age=20, sex=女}
User{id=7, userName='小林', password='12345678', age=20, sex=男}
User{id=8, userName='小趙', password='12345678', age=20, sex=女}
4、自定義方法查詢
可以通過方法名自定義簡單的查詢,如List<User> findByUserName(String userName),根據使用者姓名查詢。自定義查詢方法可以參考下面的UserRepository介面的方法和關鍵字對應的Sql語句表。
UserRepository介面
public interface UserRepository extends JpaRepository<User,Long> {
List<User> findByUserName(String userName);
User findByUserNameAndPassword(String userName, String password);
User findTopByOrderByAgeDesc();
}
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 | findByFirstname,findByFirstnameIs, | … 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) |
5、分頁查詢
使用Spring封裝的分頁實現類。在查詢的方法中,需要傳入引數Pageable。有多個引數的時候,Pageable最好最後一個引數傳入。
UserRepository介面新增相應的方法:
Page<User> findALL(Pageable pageable);
Page<User> findByUserName(String userName,Pageable pageable);
在Controller層使用:@RequestMapping("/page")
public void page() {
//分頁的頁碼,從0開始
int page=0;
//每頁有多少行資料,2就表示兩條
int size=2;
String userName="小明";
//排序規則
Sort sort=new Sort(Sort.Direction.DESC,"id");
//新增頁面、行數和排序規則
Pageable pageable=new PageRequest(page,size,sort);
//根據分頁規則查詢所有資料
Page<User> userPage1=userRepository.findAll(pageable);
if(userPage1!=null){
System.out.println("findAll:");
for (User user :userPage1){
System.out.println(user.toString());
}
}
//根據分頁規則查詢指定使用者的資料
Page<User> userPage2=userRepository.findByUserName(userName,pageable);
if(userPage2!=null){
System.out.println("findByUserName:");
for (User user :userPage2){
System.out.println(user.toString());
}
}
}
瀏覽器訪問地址後,終端輸出:Hibernate: select user0_.id as id1_1_, user0_.age as age2_1_, user0_.password as password3_1_, user0_.sex as sex4_1_, user0_.user_name as user_nam5_1_ from user user0_ order by user0_.id desc limit ?
Hibernate: select count(user0_.id) as col_0_0_ from user user0_
findAll:
User{id=8, userName='小趙', password='12345678', age=20, sex=女}
User{id=7, userName='小林', password='12345678', age=20, sex=男}
Hibernate: select user0_.id as id1_1_, user0_.age as age2_1_, user0_.password as password3_1_, user0_.sex as sex4_1_, user0_.user_name as user_nam5_1_ from user user0_ where user0_.user_name=? order by user0_.id desc limit ?
Hibernate: select count(user0_.id) as col_0_0_ from user user0_ where user0_.user_name=?
findByUserName:
User{id=2, userName='小明', password='1234567', age=21, sex=男}
User{id=1, userName='小明', password='123456', age=20, sex=男}
6、限制查詢
查詢方法的結果可以通過關鍵字first或者top來限制,它們可以交替使用。在top/firest後新增數字來表示返回最大的結果數。如果沒有數字,則預設假定1作為結果大小。
在UserRepository介面新增方法:
List<User> findFirst2ByAge(int age, Sort sort);
List<User> findTop3ByAge(int age, Pageable pageable);
Page<User> queryFirst3ByAge(int age, Pageable pageable);
在Controller層使用這些方法:
@RequestMapping("/limit")
public void limit(){
int page=0;
int size=3;
int age=20;
Sort sort=new Sort(Sort.Direction.DESC,"id");
Pageable pageable=new PageRequest(page,size);
//根據排序規則和年齡獲取前2行
List<User> userList1 =userRepository.findFirst2ByAge(age,sort);
if(userList1 !=null){
System.out.println("findFirst2ByAge:");
for (User user : userList1){
System.out.println(user.toString());
}
}
//分頁年齡獲取前三行
List<User> userList2 =userRepository.findTop3ByAge(age,pageable);
if(userList2 !=null){
System.out.println("findTop3ByAge:");
for (User user : userList2){
System.out.println(user.toString());
}
}
//這裡的query官方文件沒有介紹,測試發現和find功能一樣
Page<User> userPage1=userRepository.queryFirst3ByAge(age,pageable);
if(userPage1!=null){
System.out.println("queryFirst3ByAge:");
for (User user :userPage1){
System.out.println(user.toString());
}
}
可以參考下官方文件介紹的一些方法:
User findFirstByOrderByLastnameAsc();
User findTopByOrderByAgeDesc();
Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);
Slice<User> findTop3ByLastname(String lastname, Pageable pageable);
List<User> findFirst10ByLastname(String lastname, Sort sort);
List<User> findTop10ByLastname(String lastname, Pageable pageable);
自定義SQL語句查詢,有時候根據業務的需求,可能需要自定義SQL語句。這時候Spring Data Jpa 提供了一系列註釋方法:
@Query註解寫的是SQL語句。
@Modifying如涉及到刪除和修改在需要加上。
@Transactional 對事物的支援,查詢超時的設定等
在UserRepository介面新增方法:
@Query("update User set userName=?2 where id=?1")
@Transactional(timeout = 100)
@Modifying()
int updateUserNameById(long id,String userName);
7、多表查詢
多表查詢在spring data jpa中有兩種實現方式
第一種:利用hibernate的級聯查詢來實現;
第二種:建立一個結果集的介面來接收連表查詢後的結果;
這裡介紹下第二種方式。
業務需求:顯示使用者遊戲分數的記錄。
首先先建立用來儲存遊戲分數的實體類GameScore:
@Entity
public class GameScore {
@Id
private long id;
private String name;
private int score;
private long userId;
private Timestamp createTime;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
public long getUserId() {
return userId;
}
public void setUserId(long userId) {
this.userId = userId;
}
public Timestamp getCreateTime() {
return createTime;
}
public void setCreateTime(Timestamp createTime) {
this.createTime = createTime;
}
}
多表查詢結果集的介面類,將查詢的結果對映到相應的getXxxx()方法中:
public interface GameRank {
String getUserName();
String getName();
int getScore();
}
然後建立查詢方法,新建GameScoreRepository介面後新增自定義Sql語句的查詢方法,程式碼如下:
public interface GameScoreRepository extends JpaRepository<GameScore,Long>{
@Query("select u.userName as userName,gs.name as name,gs.score as score from GameScore gs,User u where gs.userId=u.id and gs.name=?1")
List<GameRank> findRankByName(String name);
}
最後controllerl層新增使用方法:@RequestMapping("/findRankByName")
public void findRankByName(){
List<GameRank> gameRankList=gameScoreRepository.findRankByName("2048小遊戲");
for(GameRank gameRank:gameRankList){
System.out.println("使用者名稱:"+gameRank.getName()+" 遊戲名:"+gameRank.getUserName()+" 得分"+gameRank.getScore());
}
}
瀏覽器訪問相應的地址後終端輸出:Hibernate: select user1_.user_name as col_0_0_, gamescore0_.name as col_1_0_, gamescore0_.score as col_2_0_ from game_score gamescore0_ cross join user user1_ where gamescore0_.user_id=user1_.id and gamescore0_.name=?
使用者名稱:2048小遊戲 遊戲名:小明 得分512
使用者名稱:2048小遊戲 遊戲名:小明 得分1024
使用者名稱:2048小遊戲 遊戲名:小明 得分2048