Spring Boot 整合 Spring Data JPA

文章目录

  • JPA 简介
  • Spring Data JPA
  • Spring Data JPA 实践
  • 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。

引入依赖

POM 文件如下:


<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.0modelVersion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.2.5.RELEASEversion>
        <relativePath/> 
    parent>
    <groupId>com.examplegroupId>
    <artifactId>demoartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <name>demoname>
    <description>Demo project for Spring Bootdescription>
    <properties>
        <java.version>1.8java.version>
    properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-jpaartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>

        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombokgroupId>
                            <artifactId>lombokartifactId>
                        exclude>
                    excludes>
                configuration>
            plugin>
        plugins>
    build>

project>

配置 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 

编写实体类

本文以 User 类为例:

package com.example.entity;

import lombok.Data;

import javax.persistence.*;

/**
 * @Author john
 * @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 john
 * @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 修饰的方法在实体从数据库删除前被调用。

定义 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 john
 * @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 的命名规范。

定义 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 john
 * @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);
    }
}

上述代码中,我们实现了四个方法,分别用来创建、删除和查询用户。

测试

启动项目,利用 Navicat 查看 ‘jpa_test’ 库下的数据表,发现 JPA 自动为我们创建了 user 表:

Spring Boot 整合 Spring Data JPA_第1张图片
表中包含 User 类中定义的 id、name 以及 BaseEntity 类中定义的 create_time、update_time 等字段。

添加 user

利用 Postman 发送 post 请求,添加 user:

Spring Boot 整合 Spring Data JPA_第2张图片
查询 user

利用 id 查询:

Spring Boot 整合 Spring Data JPA_第3张图片
利用 name 查询:

Spring Boot 整合 Spring Data JPA_第4张图片

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)

欢迎批评指正!!!

你可能感兴趣的:(技术学习,java,后端,spring,boot,数据库)