SprintBoot中JPA的使用
前言
第一次使用Sprint JPA的時候,感覺這東西簡直就是神器,幾乎不需要寫什么關于數據庫訪問的代碼一個基本的CURD的功能就出來了。下面我們就用一個例子來講述一下JPA使用的基本操作。
新建項目,增加依賴
在 Intellij IDEA 里面新建一個空的 SpringBoot 項目。具體步驟參考
SpringBoot 的第一次邂逅。根據本樣例的需求,我們要添加下面三個依賴
準備數據庫環境
為這個項目,我們專門新建一個 springboot_jpa 的數據庫,并且給 springboot 用戶授權
create database springboot_jpa;grant all privileges on springboot_jpa.* to 'springboot'@'%' identified by 'springboot';flush privileges;項目配置
#通用數據源配置 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://10.110.2.56:3306/springboot_jpa?charset=utf8mb4&useSSL=false spring.datasource.username=springboot spring.datasource.password=springboot # Hikari 數據源專用配置 spring.datasource.hikari.maximum-pool-size=20 spring.datasource.hikari.minimum-idle=5 # JPA 相關配置 spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect spring.jpa.show-sql=true spring.jpa.hibernate.ddl-auto=create這里
前面的數據源配置和前文《SpringBoot 中使用 JDBC Templet》中的一樣。后面的幾個配置需要解釋一下
建立第一個數據實體類
數據庫實體類是一個 POJO Bean 對象。這里我們先建立一個 UserDO 的數據庫實體。數據庫實體的源碼如下
package com.yanggaochao.springboot.learn.springbootjpalearn.security.domain.dao;import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table;/*** 用戶實體類* @author 楊高超* @since 2018-03-12*/ @Entity @Table(name = "AUTH_USER") public class UserDO {@Idprivate Long id;@Column(length = 32)private String name;@Column(length = 32)private String account;@Column(length = 64)private String pwd;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 String getAccount() {return account;}public void setAccount(String account) {this.account = account;}public String getPwd() {return pwd;}public void setPwd(String pwd) {this.pwd = pwd;} }其中:
以上配置全部正確,則這個時候運行這個項目,我們就可以看到日志中如下的內容:
Hibernate: drop table if exists auth_user Hibernate: create table auth_user (id bigint not null, account varchar(32), name varchar(32), pwd varchar(64), primary key (id)) engine=InnoDB系統自動將數據表給我們建好了。在數據庫中查看表及表結構
? ? ? ? ? ? ? ? ? ? ? ? ? ?springboot_jpa 表及表結構
以上過程和我們前使用 Hibernate 的過程基本類似,無論是數據庫實體的聲明還是表的自動創建。下面我們才正式進入 Spring Data JPA 的世界,來看一看他有什么驚艷的表現
實現一個持久層服務
在 Spring Data JPA 的世界里,實現一個持久層的服務是一個非常簡單的事情。以上面的UserDO實體對象為例,我們要實現一個增加、刪除、修改、查詢功能的持久層服務,那么我只需要聲明一個接口,這個接口繼承org.springframework.data.repository.Repository<T, ID>接口或者他的子接口就行。這里為了功能的完備,我們繼承了org.springframework.data.jpa.repository.JpaRepository<T, ID> 接口。其中T是數據庫實體類,ID是數據庫實體類的主鍵。
然后再簡單的在這個接口上增加一個 @Repository 注解就結束了。
package com.yanggaochao.springboot.learn.springbootjpalearn.security.dao;import com.yanggaochao.springboot.learn.springbootjpalearn.security.domain.dao.UserDO; import org.springframework.data.jpa.repository,JpaRepository; import org.springframework.stereotype.Repository;/*** 用戶服務數據接口類* * @author 楊高超* @since 2018-03-12*/@Repository package com.yanggaochao.springboot.learn.springbootjpalearn.security.dao;import com.yanggaochao.springboot.learn.springbootjpalearn.security.domain.dao.UserDO; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository;/*** 用戶服務數據接口類** @author 楊高超* @since 2018-03-12*/@Repository public interface UserDao extends JpaRepository<UserDO, Long> { }一行代碼也不用寫。那么針對 UserDO 這個實體類,我們已經擁有下下面的功能
?
UserDao 保存實體功能
UserDao 保存實體刪除功能
UserDao 查詢實體刪除功能
例如,我們用下面的代碼就將一些用戶實體保存到數據庫中了。
package com.yanggaochao.springboot.learn.springbootjpalearn;import com.yanggaochao.springboot.learn.springbootjpalearn.security.dao.UserDao; import com.yanggaochao.springboot.learn.springbootjpalearn.security.domain.dao.UserDO; import org.junit.After; import org.junit.Before; 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.test.context.junit4.SpringRunner;import java.util.Optional;@RunWith(SpringRunner.class) @SpringBootTest public class UserDOTest {@Autowiredprivate UserDao userDao;@Beforepublic void before() {UserDO userDO = new UserDO();userDO.setId(1L);userDO.setName("風清揚");userDO.setAccount("fengqy");userDO.setPwd("123456");userDao.save(userDO);userDO = new UserDO();userDO.setId(3L);userDO.setName("東方不敗");userDO.setAccount("bubai");userDO.setPwd("123456");userDao.save(userDO);userDO.setId(5L);userDO.setName("向問天");userDO.setAccount("wentian");userDO.setPwd("123456");userDao.save(userDO);}@Testpublic void testAdd() {UserDO userDO = new UserDO();userDO.setId(2L);userDO.setName("任我行");userDO.setAccount("renwox");userDO.setPwd("123456");userDao.save(userDO);userDO = new UserDO();userDO.setId(4L);userDO.setName("令狐沖");userDO.setAccount("linghuc");userDO.setPwd("123456");userDao.save(userDO);}@Afterpublic void after() {userDao.deleteById(1L);userDao.deleteById(3L);userDao.deleteById(5L);}}這個是采用 Junit 來執行測試用例。@Before 注解在測試用例之前執行準備的代碼。這里先插入三個用戶信息。 執行執行這個測試,完成后,查看數據庫就可以看到數據庫中有了 5 條記錄:
數據庫記錄
我們還可以通過測試用例驗證通過標識查找對象功能,查詢所有數據功能的正確性,查詢功能甚至可以進行排序和分頁
@Testpublic void testLocate() {Optional<UserDO> userDOOptional = userDao.fingById(1L);if (userDOOptional.isPresent()) {UserDO userDO = userDOOptional.get()System.out.println("name = " + userDO.getName());System.out.println("account = " + userDO.getAccount());}}@Testpublic void testFindAll() {List<UserDO> userDOList = userDao.findAll(new Sort(Sort.Direction.DESC,"account"));for (UserDO userDO : userDOList) {System.out.println("name = " + userDO.getName());System.out.println("account = " + userDO.getAccount());}}可以看到,我們所做的全部事情僅僅是在 SpingBoot 工程里面增加數據庫配置信息,聲明一個 UserDO 的數據庫實體對象,然后聲明了一個持久層的接口,改接口繼承自 org.springframework.data.jpa.repository.JpaRepository<T, ID> 接口。然后,系統就自動擁有了豐富的增加、刪除、修改、查詢功能。查詢功能甚至還擁有了排序和分頁的功能。
這就是 JPA 的強大之處。除了這些接口外,用戶還會有其他的一些需求, JPA 也一樣可以滿足你的需求。
擴展查詢
從上面的截圖 “UserDao 查詢實體刪除功能” 中,我們可以看到,查詢功能是不盡人意的,很多我們想要的查詢功能還沒有。不過放心。JPA 有非常方便和優雅的方式來解決
根據屬性來查詢
如果想要根據實體的某個屬性來進行查詢我們可以在 UserDao 接口中進行接口聲明。例如,如果我們想根據實體的 account 這個屬性來進行查詢(在登錄功能的時候可能會用到)。我們在 com.yanggaochao.springboot.learn.springbootjpalearn.security.dao.UserDao 中增加一個接口聲明就可以了
?
然后增加一個測試用例
?
@Testpublic void testFindByAccount() {UserDO userDO = userDao.findByAccount("wentian");if (userDO != null) {System.out.println("name = " + userDO.getName());System.out.println("account = " + userDO.getAccount());}}運行之后,會在日志中打印出
name = 向問天 account = wentian這種方式非常強大,不經能夠支持單個屬性,還能支持多個屬性組合。例如如果我們想查找賬號和密碼同時滿足查詢條件的接口。那么我們在 UserDao 接口中聲明
UserDO findByAccountAndPwd(String account, String pwd);再例如,我們要查詢 id 大于某個條件的用戶列表,則可以聲明如下的接口
?
List<UserDO> findAllByIdGreaterThan(Long id);這個語句結構可以用下面的表來說明
JPA 關鍵字說明
自定義查詢
如果上述的情況還無法滿足需要。那么我們就可以通過通過 import org.springframework.data.jpa.repository.Query 注解來解決這個問題。例如我們想查詢名稱等于某兩個名字的所有用戶列表,則聲明如下的接口即可
?
@Query("SELECT O FROM UserDO O WHERE O.name = :name1 OR O.name = :name2 ") List<UserDO> findTwoName(@Param("name1") String name1, @Param("name2") String name2);這里是用 PQL 的語法來定義一個查詢。其中兩個參數名字有語句中的 : 后面的支付來決定
如果你習慣編寫 SQL 語句來完成查詢,還可以在用下面的方式實現
?
@Query(nativeQuery = true, value = "SELECT * FROM AUTH_USER WHERE name = :name1 OR name = :name2 ") List<UserDO> findSQL(@Param("name1") String name1, @Param("name2") String name2);這里在 @Query 注解中增加一個 nativeQuery = true 的屬性,就可以采用原生 SQL 語句的方式來編寫查詢。
聯合主鍵
從 org.springframework.data.jpa.repository.JpaRepository<T, ID> 接口定義來看,數據實體的主鍵是一個單獨的對象,那么如果一個數據庫的表的主鍵是兩個或者兩個以上字段聯合組成的怎么解決呢。
我們擴充一下前面的場景。假如我們有一個角色 Role 對象,有兩個屬性 一個 id ,一個 name ,對應了 auth_role 數據表,同時有一個角色用戶關系對象 RoleUser,說明角色和用戶對應關系,有兩個屬性 roleId,userId 對應 auth_role_user 表。那么我們需要聲明一個 RoleDO 對象如下
?
package com.yanggaochao.springboot.learn.springbootjpalearn.security.domain.dao;import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table;/*** 角色實體類** @author 楊高超* @since 2018-03-12*/ @Entity @Table(name = "AUTH_ROLE") public class RoleDO {@Idprivate Long id;@Column(length = 32)private String name;@Column(length = 64)private String note;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 String getNote() {return note;}public void setNote(String note) {this.note = note;} }對于有多個屬性作為聯合主鍵的情況,我們一般要新建一個單獨的主鍵類,他的屬性和數據庫實體主鍵的字段一樣,要實現 java.io.Serializable 接口,類聲明如下
?
package com.yanggaochao.springboot.learn.springbootjpalearn.security.domain.dao;import java.io.Serializable;/*** 聯合主鍵對象** @author 楊高超* @since 2018-03-12*/ public class RoleUserId implements Serializable {private Long roleId;private Long userId; }同樣的,我們聲明一個 RoleUserDO 對象如下
?
package com.yanggaochao.springboot.learn.springbootjpalearn.security.domain.dao;import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.IdClass; import javax.persistence.Table; import java.io.Serializable;/*** 角色用戶關系實體類** @author 楊高超* @since 2018-03-12*/ @Entity @IdClass(RoleUserId.class) @Table(name = "AUTH_ROLE_USER") public class RoleUserDO {@Idprivate Long roleId;@Idprivate Long userId;public Long getRoleId() {return roleId;}public void setRoleId(Long roleId) {this.roleId = roleId;}public Long getUserId() {return userId;}public void setUserId(Long userId) {this.userId = userId;} }這里因為數據實體類和數據實體主鍵類的屬性一樣,所以我們可以刪除掉這個數據實體主鍵類,然后將數據實體類的主鍵類聲明為自己即可。當然,自己也要實現 java.io.Serializable 接口。
這樣,我們如果要查詢某個角色下的所有用戶列表,就可以聲明如下的接口
?
@Query("SELECT U FROM UserDO U ,RoleUserDO RU WHERE U.id = RU.userId AND RU.roleId = :roleId") List<UserDO> findUsersByRole(@Param("roleId") Long roleId);當然了,這種情況下,我們會看到系統自動建立了 AUTH_ROLE 和 AUTH_ROLE_USER 表。他們的表結構如下所示
auth_role 和 auth_role_user 表結構
注意這里 auth_role_user 表中,屬性名 userId 轉換為了 user_id, roleId 轉換為了 role_id.
如果我們要用 SQL 語句的方式實現上面的功能,那么我們就把這個接口聲明修改為下面的形式。
?
@Query("SELECT U.* FROM AUTH_USER U ,AUTH_ROLE_USER RU WHERE U.id = RU.user_id AND RU.role_id = :roleId") List<UserDO> findUsersByRole(@Param("roleId") Long roleId);后記
這個樣例基本上講述了 JPA 使用過程中的一些細節。我們可以看出。使用 JPA 來完成關于關系數據庫增刪改查的功能是非常的方便快捷的。所有代碼已經上傳到 github 的倉庫 springboot-jpa-learn 上了
?
總結
以上是生活随笔為你收集整理的SprintBoot中JPA的使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安装sqlserver时“试图执行未经授
- 下一篇: 《TOMCAT权威指南》摘抄