学习应用 Spring Data JPA
JPA 简介
JPA(Java Persistence API)是 Java 长久化 API,是 Sun 公司提出的基于 ORM 的 Java 长久化标准。
ORM(Object Relational Mapping)的全称是对象关系映射,支流 Java ORM 框架有 Mybatis,Hibernate 等。
Spring Data JPA
Spring Data JPA 是 Spring Data 框架的一个模块,可简化 JPA 的实现。此外,Spring Data JPA 还能够帮忙咱们简化长久层的开发。对于简略查问,Spring Data JPA 能够依据办法名称进行解析,并主动生成查问语句进行查问;对于简单查问,Spring Data JPA 同样反对原生的 SQL。
Spring Data JPA 实际
创立 SpringBoot 我的项目,利用 JPA 实现简略 CRUD。
1. 引入依赖
POM 文件如下:
<?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.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</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>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
2. 配置 JPA 和 MySQL
application.yml 配置文件如下:
spring:
datasource:
url: jdbc:mysql://localhost:3306/jpa_test?serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update # 表内有数据时不会清空, 只会更新
# 控制台显示 SQL
show-sql: true
3. 编写实体类
本文以 User 类为例:
package com.example.entity;
import lombok.Data;
import javax.persistence.*;
/**
* @Author join
* @Date 2021/10/13
*/
@Data
@Entity
@Table(name = "user")
public class User extends BaseEntity{
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "name", columnDefinition = "varchar(20) not null")
private String name;
}
User 类中蕴含 id 和 name 两个属性,并且 User 类还继承了 BaseEntity,当我的项目中存在多个实体类时,咱们无妨将数据表中独特的字段封装在 BaseEntity 中,比方 create_time、update_time 等。
package com.example.entity;
import lombok.Data;
import javax.persistence.*;
import java.util.Date;
/**
* @Author join
* @Date 2021/10/13
*/
@Data
@MappedSuperclass
public class BaseEntity {@Column(name = "create_time")
@Temporal(TemporalType.TIMESTAMP)
private Date createTime;
@Column(name = "update_time")
@Temporal(TemporalType.TIMESTAMP)
private Date updateTime;
@PrePersist
public void prePersist() {Date now = new Date();
if (createTime == null) {createTime = now;}
if (updateTime == null) {updateTime = now;}
}
@PreUpdate
public void preUpdate() {updateTime = new Date();
}
@PreRemove
public void preRemove() {updateTime = new Date();
}
}
注解解释:
- @Data:lombok 注解,可主动生成 get()、set()、toString() 等办法;
- @Entity:JPA 注解,申明该类为一个实体类,必须与 @Id 搭配应用;
- @Table:JPA 注解,示意该实体类的对象会映射到数据库的 user 表(name 指定表名);
- @Id:JPA 注解,申明主键;
- @GeneratedValue:JPA 注解,示意主键的生成策略,IDENTITY 示意应用自增 id;
- @Column:JPA 注解,申明实体对象的属性映射到数据表中的哪一个字段,name 指定字段名,columnDefinition 指定字段的定义。
- @MappedSuperclass:JPA 注解,利用于实体类的父类,该注解作用的类不会映射到数据表,但其属性都将映射到子类的数据表。
- @PrePersist:被 @PrePersist 润饰的办法在将实体长久化到数据库之前被调用;
- @PreUpdate:被 @PreUpdate 润饰的办法在实体的某个属性产生变动时被调用,如更新实体的 update_time;
- @PreRemove:被 @PreRemove 润饰的办法在实体从数据库删除前被调用。
4. 定义 repository
编写 UserRepository:
package com.example.repository;
import com.example.entity.User;
import lombok.NonNull;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
/**
* @Author join
* @Date 2021/10/13
*/
public interface UserRepository extends JpaRepository<User, Integer> {
@NonNull
Optional<User> findByName(@NonNull String name);
}
UserRepository 只需继承 JpaRepository,便可主动生成根本的 CRUD 办法,如 findById(),save() 等。上述代码中,咱们还自定义了一个办法 findByName(),JpaRepository 可依据办法名称主动实现对应的逻辑。
留神理解 JpaRepository 的命名标准。
5. 定义 controller
编写 UserController:
package com.example.controller;
import com.example.entity.User;
import com.example.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.Optional;
/**
* @Author join
* @Date 2021/10/13
*/
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserRepository userRepository;
@RequestMapping(path = "addUser", method = RequestMethod.POST)
@ResponseBody
public void addUser(@RequestBody User user) {userRepository.save(user);
}
@RequestMapping(path = "deleteUser", method = RequestMethod.POST)
@ResponseBody
public void deleteUser(@RequestBody User user) {userRepository.delete(user);
}
@RequestMapping(path = "/getById/{id}", method = RequestMethod.GET)
@ResponseBody
public User queryUserById(@PathVariable("id") int id) {return userRepository.findById(id).orElse(null);
}
@RequestMapping(path = "/getByName/{name}", method = RequestMethod.GET)
@ResponseBody
public User queryUserByName(@PathVariable("name") String name) {Optional<User> optional = userRepository.findByName(name);
return optional.orElse(null);
}
}
上述代码中,咱们实现了四个办法,别离用来创立、删除和查问用户。
6. 测试
启动我的项目,利用 Navicat 查看 ‘jpa_test’ 库下的数据表,发现 JPA 主动为咱们创立了 user 表:
表中蕴含 User 类中定义的 id、name 以及 BaseEntity 类中定义的 create_time、update_time 等字段。
增加 user
利用 Postman 发送 post 申请,增加 user:
查问 user
利用 id 查问:
利用 name 查问:
7. JPA 解析原理
JPA 遵循约定大概配置(Convention over configuration)的准则,且遵循 Spring 以及 JPQL 定义的命名规定。Spring 提供了依据命名规定进行查问构建的机制,该机制会从办法名中过滤出一些关键字,如 find…By, read…By, query…By, count…By 和 get…By 等。零碎会依据关键字将办法名解析成两个子句,第一个 By 是辨别这两个子语句的关键字。By 之前的子句是查问子句,指明返回的对象。残余局部为条件子句,如果办法名为 findBy…,那么返回的就是定义 Respository 时指定的对象。下表中是一些关键字的应用阐明:
Keyword | Sample | JPQL snippet |
---|---|---|
And | findByLastnameAndFirstname | … where x.lastname = ?1 and |
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) |