Spring Data Jpa介紹
JPA(Java persisten API),全稱為Java持久化API,是JAVAEE中的一套規范API。它推出的目的是對ORM框架進行大統一,它提供一套接口,讓廠商們(如hibernate)對JPA提供實現。JPA與hibernate的關系就像JDBC與Mysql驅動、Oracle驅動一樣的關系,只是它更加高度抽象,可以稱之為ORM框架的接口。
Spring Data JPA 是 Spring 基于 Spring Data 框架、在JPA 規范的基礎上開發的一個框架,使用 Spring Data JPA 可以極大地簡化JPA 的寫法,可以在幾乎不用寫實現的情況下實現對數據庫的訪問和操作,除了CRUD外,還包括分頁和排序等一些常用的功能。
Spring Data Jpa實戰
-
創建項目
maven依賴詳情如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>training.springboot</groupId>
<artifactId>jpa</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>jpa</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置文件詳情如下
# 數據庫相關配置
spring.datasource.username=root
spring.datasource.password=yzhroot
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=CTT
# JPA相關配置
# 數據庫類型
spring.jpa.database=mysql
# 日志輸出sql
spring.jpa.show-sql=true
# 建表語句設置
spring.jpa.hibernate.ddl-auto=update
# 解決spring-mvc與hibernate事務問題
spring.jpa.open-in-view=true
-
實戰場景
這次練習主要涉及到三個實體,用戶,部門以及角色,其中用戶和部門是多對一,用戶和角色是多對多關系。
-
編寫實體類
/**
* 用戶實體類
*/
@Entity(name = "user")
public class User {
//設置id為主鍵,生成策略是自增
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
private String username;
//設置數據庫列名是create_date,格式為 yyyy-MM-dd
@Temporal(value = TemporalType.DATE)
@Column(name = "create_date")
private Date createDate;
//防止JSON序列化時循環引用
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "department_id")
@JsonBackReference
private Department department;
//多對多
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "user_and_role",
joinColumns = {@JoinColumn(name = "user_id",referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "id")})
private List<Role> roles;
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 Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", createDate=" + createDate +
", department=" + department +
", roles=" + roles +
'}';
}
}
/**
* 角色實體類
*/
@Entity(name = "role")
public class Role {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany
@JoinTable(name = "user_and_role",
joinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "user_id",referencedColumnName = "id")})
@JsonBackReference
private List<User> users;
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 List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
}
/**
* 部門實體類
*/
@Entity(name = "department")
public class Department {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
private String name;
//設置一對多的映射表
@OneToMany(mappedBy="department",cascade = CascadeType.ALL)
private List<User> users;
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 List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
}
創建實體時涉及到了幾個Jpa的常用注解,下面來簡單解釋一下。
@Entity:表明這是一個實體類,name屬性的值代表與這個實體類映射的數據庫表。
@Id:表明這個屬性是數據庫中的主鍵。
-
@GeneratedValue:用來聲明主鍵是自動生成的,strategy的常見的值有四個;分別是TABLE、AUTO、IDENTITY、SEQUENCE。
- TABLE:使用一個特定的數據庫表格來保存主鍵。
- AUTO:主鍵由程序控制,是默認值。
- IDENTITY:主鍵由數據庫自動生成(主要是支持自動增長的數據庫,如mysql)
- SEQUENCE:根據底層數據庫的序列來生成主鍵,條件是數據庫支持序列。 這個值要與generator一起使用,generator 指定生成主鍵使用的生成器(可能是orcale中自己編寫的序列)。
@OneToMany:用于標識一對多關系,mappedBy屬性是One方的屬性名稱,cascade 屬性是代表著級聯操作。
ManyToMany:用于標識多對多關系。
@JoinTable:用于標識中間表,并需要制定另一個joinColumns標識本表主鍵在中間表的名稱,inverseJoinColumns標識另一個Many方的主鍵在中間表的名稱。
@JsonBackReference:防止Json轉換后循環調用。
-
@Temporal:標明屬性在數據庫中是date類型,TemporalType的值有三種;
- TemporalType.DATE:實體類會封裝成日期“yyyy-MM-dd”的 Date類型。
- TemporalType.TIME:實體類會封裝成時間“hh-MM-ss”的 Date類型。
- TemporalType.TIMESTAMP:實體類會封裝成完整的時間“yyyy-MM-dd hh:MM:ss”的 Date類型。
-
編寫實體類的CRUD
/**
* 部門實體類的CRUD操作
*/
@Repository
public interface DepartmentDao extends JpaRepository<Department,Long> {
}
/**
* 角色實體類的CRUD操作
*/
@Repository
public interface RoleDao extends JpaRepository<Role,Long> {
}
/**
* 用戶實體類的CRUD操作
*/
@Repository
public interface UserDao extends JpaRepository<User,Long> {
}
可以看到我們只需編寫一個接口繼承JpaRepository接口便可以基本的CRUD操作,下面來簡單介紹下原理。
JpaRepository繼承自PagingAndSortingRepository和QueryByExampleExecutor,而
PagingAndSortingRepository繼承自CrudRepository。
- CrudRepository
public interface CrudRepository<T, ID> extends Repository<T, ID> {
<S extends T> S save(S var1); //插入或更新一條記錄
<S extends T> Iterable<S> saveAll(Iterable<S> var1);//插入或更新多條記錄
Optional<T> findById(ID var1);//根據ID查詢一個實體
boolean existsById(ID var1);//根據ID判斷一個實體是否存在
Iterable<T> findAll();//查詢所有實體
Iterable<T> findAllById(Iterable<ID> var1);//根據ID批量查詢實體
long count();//查詢所有實體的數量
void deleteById(ID var1); //根據ID刪除一個實體
void delete(T var1);//根據一個實體的信息,刪除這個實體
void deleteAll(Iterable<? extends T> var1); //批量刪除實體
void deleteAll(); //刪除全部實體
}
- PagingAndSortingRepository
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort var1);//根據規則排序
Page<T> findAll(Pageable var1);//分頁查詢
}
- JpaRepository
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
List<T> findAll(); // 查詢所有實體
List<T> findAll(Sort sort); // 查詢所有實體并排序
List<T> findAll(Iterable<ID> ids); // 根據ID集合查詢實體
<S extends T> List<S> save(Iterable<S> entities); // 保存并返回(修改后的)實體集合
void flush(); // 提交事務
<S extends T> S saveAndFlush(S entity); // 保存實體并立即提交事務
void deleteInBatch(Iterable<T> entities); // 批量刪除實體集合
void deleteAllInBatch();// 批量刪除所有實體
T getOne(ID id); // 根據ID查詢實體
@Override
<S extends T> List<S> findAll(Example<S> example); // 查詢與指定Example匹配的所有實體
@Override
<S extends T> List<S> findAll(Example<S> example, Sort sort);// 查詢與指定Example匹配的所有實體并排序
}
簡單來說,SpringDataJpa在運行時,會以動態代理的形式生成實現這些方法的代理對象,然后就可以實現我們的增刪改查功能了。
另外,JPA還支持約定命名法,以下是約定規則(下圖來自https://blog.csdn.net/Phapha1996/article/details/78712597
):
-
測試
由于篇幅有限,只簡單寫出幾種測試,完整代碼見底部鏈接
@SpringBootTest
class JpaApplicationTests {
@Autowired
private UserDao userDao;
@Autowired
private DepartmentDao departmentDao;
//添加一個USER
@Test
public void testAddUser(){
User user = new User();
user.setCreateDate(new Date());
user.setUsername("小明");
userDao.save(user);
}
//添加一個部門,級聯操作
@Test
public void testAddDepartment(){
Department department = new Department();
department.setName("研發部");
department.setId((long) 1);
User user = new User();
user.setId((long) 2);
user.setUsername("小紅2");
user.setCreateDate(new Date());
List<User> users = new ArrayList<>();
users.add(user);
department.setUsers(users);
departmentDao.save(department);
}
//查詢所有USER
@Test
public void testQueryAllUser(){
System.out.println(userDao.findAll());
}
}