Spring Data JPA學習

来源:https://www.cnblogs.com/l-y-h/archive/2019/12/31/12127923.html
-Advertisement-
Play Games

一、Spring Data JPA 1、簡介 (1)官網地址: https://spring.io/projects/spring-data-jpa參考文檔: https://docs.spring.io/spring-data/jpa/docs/2.2.3.RELEASE/reference/ht ...


一、Spring Data JPA

1、簡介

(1)官網地址:
  https://spring.io/projects/spring-data-jpa
參考文檔:
  https://docs.spring.io/spring-data/jpa/docs/2.2.3.RELEASE/reference/html/#preface

(2)基本介紹:
  Spring Data JPA 是 Spring 基於 ORM 框架、JPA 規範封裝的一套 JPA 框架。使開發者通過極簡的代碼實現對資料庫的訪問和操作。
註:
  ORM 框架:指的是 Object Relational Mapping,即對象關係映射。採用元數據來描述對象和關係映射的細節。
  元數據:一般採用 XML 文件的形式。常見 ORM 框架如:mybatis、Hibernate。
  JPA:指的是 Java Persistence API,即 Java 持久層 API。通過 xml 或註解的映射關係將運行期的實體對象持久化到資料庫中。

2、sping boot 項目中使用

(1)在 pom.xml 文件中引入依賴

【pom.xml】

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

 

(2)在 application.properties 中配置

【application.properties】

# jpa 配置
# 配置資料庫為 mysql
spring.jpa.database=mysql
# 在控制台列印 sql 語句
spring.jpa.show-sql=true
# 每次運行程式,沒有表格會新建表格,表內有數據不會清空,只會更新
spring.jpa.hibernate.ddl-auto=update
# 每次運行該程式,沒有表格會新建表格,表內有數據會清空
#spring.jpa.hibernate.ddl-auto=create

 

二、基本註解

1、@Entity

@Entity 寫在類上,用於指明一個類與資料庫表相對應。
    屬性:
        name,可選,用於自定義映射的表名。若沒有,則預設以類名為表名。

【舉例1:預設類名為表名】
import javax.persistence.Entity;

@Entity
public class Blog {
}

【舉例2:自定義表名】
import javax.persistence.Entity;

@Entity(name="t_blog")
public class Blog {
}

 

2、@Table

@Table 寫在類上,一般與 @Entity 連用,用於指定數據表的相關信息。
    屬性:
        name, 對應數據表名。
        catalog, 可選,對應關係資料庫中的catalog。
        schema,可選,對應關係資料庫中的schema。

【舉例:】
import javax.persistence.Entity;
import javax.persistence.Table;

@Entity(name = "blog")
@Table(name = "t_blog")
public class Blog {
}

註:若 @Entity 與 @Table 同時定義了 name 屬性,那以 @Table 為主。

 

3、@Id、@GeneratedValue

@Id 寫在類中的變數上,用於指定當前變數為主鍵 Id。一般與 @GeneratedValue 連用。

@GeneratedValue 與 @Id 連用,用於設置主鍵生成策略(自增主鍵,依賴資料庫)。
註:
    @GeneratedValue(strategy = GenerationType.AUTO) 主鍵增長方式由資料庫自動選擇,當數據
庫選擇AUTO方式時就會自動生成hibernate_sequence表。
    
    @GeneratedValue(strategy = GenerationType.IDENTITY) 要求資料庫選擇自增方式,oracle不
支持此種方式,mysql支持。
    
    @GeneratedValue(strategy = GenerationType.SEQUENCE) 採用資料庫提供的sequence機制生
成主鍵,mysql不支持。


【舉例:】
package com.lyh.blog.bean;

import lombok.Data;

import javax.persistence.*;

@Entity
@Table(name = "t_blog")
@Data
public class Blog {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
}

 

 

 

4、@Column

@Column 寫在類的變數上,用於指定當前變數映射到數據表中的列的屬性(列名,是否唯一,是否允許為空,是否允許更新等)。
屬性:
    name: 列名。 
    unique: 是否唯一 
    nullable: 是否允許為空 
    insertable: 是否允許插入 
    updatable: 是否允許更新 
    length: 定義長度
    
【舉例:】
import lombok.Data;

import javax.persistence.*;

@Entity
@Table(name = "t_blog")
@Data
public class Blog {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "name", length = 36, unique = false, nullable = false, insertable = true, updatable = true)
    private String name;
}

 

 

 

5、@Temporal

@Temporal 用於將 java.util 下的時間日期類型轉換 並存於資料庫中(日期、時間、時間戳)。
屬性:
    TemporalType.DATE       java.sql.Date日期型,精確到年月日,例如“2019-12-17”
    TemporalType.TIME       java.sql.Time時間型,精確到時分秒,例如“2019-12-17 00:00:00”
    TemporalType.TIMESTAMP  java.sql.Timestamp時間戳,精確到納秒,例如“2019-12-17 00:00:00.000000001”

【舉例:】
package com.lyh.blog.bean;

import lombok.Data;

import javax.persistence.*;
import java.util.Date;

@Entity
@Table(name = "t_blog")
@Data
public class Blog {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "name", length = 36, unique = false, nullable = false, insertable = true, updatable = true)
    private String name;

    @Temporal(TemporalType.TIMESTAMP)
    private Date createTime;

    @Temporal(TemporalType.DATE)
    private Date updateTime;
}

 

 

 

6、級聯(cascade)

對於 @OneToOne、@ManyToMany、@OneToMany等映射關係,涉及到級聯的操作。
    CascadeType[] cascade() default {};
    定義級聯用於 給當前設置的實體 操作 另一個關聯的實體的許可權。
    
【級聯的類型:】
package javax.persistence;
public enum CascadeType {
    ALL,
    PERSIST,
    MERGE,
    REMOVE,
    REFRESH,
    DETACH;

    private CascadeType() {
    }
}

CascadeType.ALL         擁有所有級聯操作的許可權。
CascadeType.PERSIST     當前實體類進行保存操作時,同時保存其關聯的實體。
CascadeType.MERGE       當前實體數據合併時,會影響其關聯的實體。
CascadeType.REMOVE      刪除當前實體,與其相關聯的實體也會被刪除。
CascadeType.REFRESH     刷新當前實體,與其相關聯的實體也會被刷新。
CascadeType.DETACH      去除外鍵關聯,當刪一個實體時,存在外鍵無法刪除,使用此級聯可以去除外鍵。

 

7、mappedby

 只有 @OneToOne, @OneToMany, @ManyToMany上才有 mappedBy 屬性,@ManyToOne不存在該屬性。
 
 該屬性的作用:
     設置關聯關係。單向關聯關係不需要設置,雙向關係必須設置,避免雙方都建立外鍵欄位。
     
對於 一對多 的關係,外鍵總是建立在多的一方(用到@JoinColumn),而 mappedBy 存在相反的一方。
比如:
    部門(department)與 員工(Employee)
    一個部門對應多個員工。一個員工屬於一個部門。
    即部門與員工間的關係是 一對多 的關係。
【舉例:】
public class Department {
    @OneToMany(mappedBy = "bookCategory", cascade = CascadeType.ALL)
    private List<Employee> employee;
}

public class Employee {
    @ManyToOne
    private Department department;
}

 

8、@OneToOne

@OneToOne 用於描述兩個數據表間 一對一的關聯關係。
【屬性:】
    cascade,  用於定義級聯屬性
    fetch,    用於定義 懶載入(LAZY,不查詢就不載入)、熱載入(EAGER,預設)
    mappedBy, 用於定義 被維護的表(相關聯的表)
    optional, 用於定義 是否允許對象為 null

 

三、JPA 實現 CRUD(以單個實體類為例)

1、搭建環境(以Spring Boot 2.0為例)

 

 

 (1)添加依賴信息

【pom.xml】

<?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.2.RELEASE</version>
      <relativePath/> <!-- lookup parent from repository -->
   </parent>
   <groupId>com.lyh.demo</groupId>
   <artifactId>jpa</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <name>jpa</name>
   <description>JPA 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-devtools</artifactId>
         <scope>runtime</scope>
         <optional>true</optional>
      </dependency>
      <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
         <version>8.0.18</version>
      </dependency>
      <dependency>
         <groupId>org.projectlombok</groupId>
         <artifactId>lombok</artifactId>
         <optional>true</optional>
      </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>

 

(2)配置連接

【application.properties】

# 資料庫連接配置
spring.datasource.url=jdbc:mysql://localhost:3306/lyh?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# jpa 配置
spring.jpa.database=mysql
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update

 

2、編寫實體類以及映射關係

【com.lyh.demo.jpa.bean.Employee】
package com.lyh.demo.jpa.bean;

import lombok.Data;

import javax.persistence.*;
import java.util.Date;

@Entity
@Table(name = "emp")
@Data
@Proxy(lazy = false)
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "name", length = 32)
    private String name;

    @Column(name = "age")
    private Integer age;

    @Temporal(TemporalType.TIMESTAMP)
    private Date createDate;
}

 

3、編寫Dao層

  不需要編寫實現類。只需要繼承兩個介面(JpaRepository、JpaSpecificationExecutor)。

package com.lyh.demo.jpa.dao;

import com.lyh.demo.jpa.bean.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Component;

/**
 * JpaRepository<操作的實體類型, 實體類中主鍵的類型>, 封裝了 CRUD 基本操作。
 * JpaSpecificationExecutor<操作的實體類型>,封裝了複雜的操作,比如 分頁。
 */
@Component
public interface EmployeeDao extends JpaRepository<Employee, Integer>, JpaSpecificationExecutor<Employee> {
}

 

4、編寫測試類

【com.lyh.demo.jpa.JpaApplicationTests】

package com.lyh.demo.jpa;

import com.lyh.demo.jpa.bean.Employee;
import com.lyh.demo.jpa.dao.EmployeeDao;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Date;

@SpringBootTest
class JpaApplicationTests {
   @Autowired
   private EmployeeDao employeeDao;

   /**
    * 使用 save 方法時,若沒有 id,則直接進行 添加操作。
    */
   @Test
   void testSave() {
      Employee employee = new Employee();
      employee.setName("tom");
      employee.setAge(22);
      employee.setCreateDate(new Date());
      employeeDao.save(employee);
   }

   /**
    * 使用 save 方法,若存在 id,會先進行一次查詢操作,若存在數據,則更新數據,否則保存數據。
    */
   @Test
   void testUpdate() {
      Employee employee = new Employee();
      employee.setId(10);
      employee.setName("tom");
      employee.setAge((int)(Math.random() * 100 + 1));
      employee.setCreateDate(new Date());
      employeeDao.save(employee);
   }

   /**
    * 根據 id 查詢某條數據
    */
   @Test
   void testFindOne() {
      System.out.println(employeeDao.getOne(1));
   }

   /**
    * 查詢所有的數據
    */
   @Test
   void testFindAll() {
      System.out.println(employeeDao.findAll());
   }

   /**
    * 根據id刪除數據
    */
   @Test
   void testDelete() {
      employeeDao.deleteById(1);
   }
}

測試 save 插入

 

 

 

測試 save 更新。

 

 

 

 

 

 

測試 查詢。

 

 

 

 

 

 

測試刪除。

 

 

 

5、遇到的坑

(1)執行測試(getOne())的時候報錯:
  org.hibernate.LazyInitializationException: could not initialize proxy [com.lyh.demo.jpa.bean.Employee#1] - no Session

 

 

 

原因:

  getOne() 內部採用懶載入的方式執行,什麼時候用,什麼時候才會去觸發獲取值。

解決辦法一:
  在實體類前加上 @Proxy(lazy = false) 用於取消懶載入

【即】
package com.lyh.demo.jpa.bean;

import lombok.Data;
import org.hibernate.annotations.Proxy;

import javax.persistence.*;
import java.util.Date;

@Entity
@Table(name = "emp")
@Data
@Proxy(lazy = false)
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "name", length = 32)
    private String name;

    @Column(name = "age")
    private Integer age;

    @Temporal(TemporalType.TIMESTAMP)
    private Date createDate;
}

 

解決方法二:
  在方法執行前,加上 @Transactional。

【即】

/**
 * 根據 id 查詢某條數據
 */
@Test
@Transactional
void testFindOne() {
   System.out.println(employeeDao.getOne(2));
}

 

四、JPA 編寫sql語句 -- jpql

1、簡介

  Java Persistence Query Language,可以理解為 JPA 使用的 sql 語句,用於操作實體類以及實體類的屬性。

2、使用

(1)在 Dao 介面中定義相關方法,並通過 @Query 註解來定義 sql 語句。
  需要更新數據時,需要使用 @Modifying 註解。測試的時候,需要使用 @Transactional 註解。
  若方法參數為實體類對象,則通過 :#{#實體類名.實體類屬性名} 獲取。且方法參數需要使用 @Param聲明。

【com.lyh.demo.jpa.dao.EmployeeDao】

package com.lyh.demo.jpa.dao;

import com.lyh.demo.jpa.bean.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * JpaRepository<操作的實體類型, 實體類中主鍵的類型>, 封裝了 CRUD 基本操作。
 * JpaSpecificationExecutor<操作的實體類型>,封裝了複雜的操作,比如 分頁。
 * 其中,使用到了方法命名規則寫法。
 */
@Component
public interface EmployeeDao extends JpaRepository<Employee, Integer>, JpaSpecificationExecutor<Employee> {

    public List<Employee> getEmployeeByAge(Integer age);

    @Query("from Employee where age = ?1")
    public List<Employee> getEmployeeByAge1(Integer age);

    public List<Employee> getEmployeeByAgeAndName(Integer age, String name);

    @Query("from Employee where name = ?2 and age = ?1")
    public List<Employee> getEmployeeByAgeAndName1(Integer age, String name);

    @Query("update Employee set age = :#{#employee.age} where name = :#{#employee.name}")
    @Modifying
    public void updateEmpAgeByName(@Param("employee") Employee employee);
}

 

(2)測試

【com.lyh.demo.jpa.JpaApplicationTestJSQLs】

package com.lyh.demo.jpa;

import com.lyh.demo.jpa.bean.Employee;
import com.lyh.demo.jpa.dao.EmployeeDao;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;

import javax.transaction.Transactional;

@SpringBootTest
class JpaApplicationTestJSQLs {
   @Autowired
   private EmployeeDao employeeDao;

   @Test
   void testGetEmployeeByAge() {
      System.out.println(employeeDao.getEmployeeByAge(40));
   }

   @Test
   void testGetEmployeeByAge1() {
      System.out.println(employeeDao.getEmployeeByAge1(40));
   }

   @Test
   void testGetEmployeeByAgeAndName() {
      System.out.println(employeeDao.getEmployeeByAgeAndName(40, "tom"));
   }

   @Test
   void testGetEmployeeByAgeAndName1() {
      System.out.println(employeeDao.getEmployeeByAgeAndName1(41, "tom"));
   }

   @Test
   @Transactional
   void testUpdateEmpAgeByName() {
      Employee employee = new Employee();
      employee.setName("tom");
      employee.setAge(11);
      employeeDao.updateEmpAgeByName(employee);
   }
}

 

測試 getEmployeeByAge

 

 

 

測試 getEmployeeByAge1,與getEmployeeByAge 的區別在於 getEmployeeByAge1 是自定義查詢方法。

 

 

 

測試 getEmployeeByAgeAndName

 

 

 

測試 getEmployeeByAgeAndName1,同樣屬於自定義查詢方法。

 

 

 

測試 updateEmpAgeByName,採用對象傳參的方式。進行更新操作 需要使用 @Modifying 註解。

 

 

 

3、遇到的坑

(1)報錯:(JDBC style parameters (?) are not supported for JPA queries.)
  org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'employeeDao': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: JDBC style parameters (?) are not supported for JPA queries.

 

 

 

解決: 在占位符上指定匹配的參數位置(從1開始)

【com.lyh.demo.jpa.dao.EmployeeDao】

@Query("from Employee where age = ?1")
public List<Employee> getEmployeeByAge1(Integer age);

 

(2)使用 實體類對象 作為參數進行 jpql 查詢,獲取實體類某個參數報錯。
  解決辦法:使用 :#{#employee.age} 獲取參數。

@Query("update Employee set age = :#{#employee.age} where name = :#{#employee.name}")
@Modifying
public void updateEmpAgeByName(@Param("employee") Employee employee);

 

(3)對於 update、delete 操作,需要使用 @Transactional 、 @Modifying 註解,否則會報錯。


五、JPA 編寫 sql 語句 -- sql、方法規則命名查詢

1、sql 語法規則

  寫法類似於 jpql,需要使用 @Query 註解,但是需要使用 nativeQuery = true。
  若 nativeQuery = false,則使用 jpql。
  若 nativeQuery = true,則使用 sql。

 

 

 

(1)配置

【application.properties】

# 資料庫連接配置
spring.datasource.url=jdbc:mysql://localhost:3306/lyh?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# jpa 配置
spring.jpa.database=mysql
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect

 

(2)dao層

【com/lyh/demo/jpa/dao/EmployeeDao.java】

package com.lyh.demo.jpa.dao;

import com.lyh.demo.jpa.bean.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public interface EmployeeDao extends JpaRepository<Employee, Integer>, JpaSpecificationExecutor<Employee> {

    @Query(value = "select * from emp", nativeQuery = true)
    public List<Employee> getEmployee();
}

 

(3)bean
  實體類。

【com/lyh/demo/jpa/bean/Employee.java】

package com.lyh.demo.jpa.bean;

import lombok.Data;

import javax.persistence.*;
import java.util.Date;

@Entity
@Table(name = "emp")
@Data
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "name", length = 32)
    private String name;

    @Column(name = "age")
    private Integer age;

    @Temporal(TemporalType.TIMESTAMP)
    private Date createTime;
}

 

(4)test

【com/lyh/demo/jpa/JpaApplicationTests.java】

package com.lyh.demo.jpa;

import com.lyh.demo.jpa.bean.Employee;
import com.lyh.demo.jpa.dao.EmployeeDao;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Date;
import java.util.List;

@SpringBootTest
class JpaApplicationTests {

    @Autowired
    private EmployeeDao employeeDao;

    @Test
    void testSave() {
        Employee employee = new Employee();
        employee.setName("tom");
        employee.setAge(22);
        employee.setCreateTime(new Date());
        employeeDao.save(employee);
    }

    @Test
    void testGetEmployee() {
        List<Employee> employeeList = employeeDao.getEmployee();
        for (Employee employee: employeeList) {
            System.out.println(employee);
        }
    }
}

 

測試截圖:
  執行 兩次 testSave() 方法,添加幾條測試數據。

 

 

 

測試 testGetEmployee() 方法,測試 sql 語句。

 

 

 

2、方法命名規則查詢

  是對 jpql 的進一步封裝。只需要根據 SpringDataJPA 提供的方法名規則去定義方法名,從而不需要配置 jpql 語句,會自動根據方法名去解析成 sql 語句。
(1)關鍵字定義:
  https://docs.spring.io/spring-data/jpa/docs/2.2.3.RELEASE/reference/html/#repository-query-keywords
詳細文檔:
  https://blog.csdn.net/qq_32448349/article/details/89445216

 

 

 

(2)舉例:

findEmployeesByAgeAndName 等價於 select * from emp where age = ? and name = ?
根據屬性名稱進行查詢。

findEmployeesByNameLike 等價於 select * from emp where name like ?
根據屬性進行模糊查詢

 

(3)測試:

【com/lyh/demo/jpa/dao/EmployeeDao.java】

package com.lyh.demo.jpa.dao;

import com.lyh.demo.jpa.bean.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public interface EmployeeDao extends JpaRepository<Employee, Integer>, JpaSpecificationExecutor<Employee> {

    @Query(value = "select * from emp", nativeQuery = true)
    public List<Employee> getEmployee();

    public List<Employee> findEmployeesByAgeAndName(Integer age, String name);

    public List<Employee> findEmployeesByNameLike(String name);
}



【com/lyh/demo/jpa/JpaApplicationTests.java】

package com.lyh.demo.jpa;

import com.lyh.demo.jpa.bean.Employee;
import com.lyh.demo.jpa.dao.EmployeeDao;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Date;
import java.util.List;

@SpringBootTest
class JpaApplicationTests {

    @Autowired
    private EmployeeDao employeeDao;

    @Test
    void testSave() {
        Employee employee = new Employee();
        employee.setName("tom");
        employee.setAge(22);
        employee.setCreateTime(new Date());
        employeeDao.save(employee);
    }

    @Test
    void testGetEmployee() {
        List<Employee> employeeList = employeeDao.getEmployee();
        for (Employee employee: employeeList) {
            System.out.println(employee);
        }
    }

    @Test
    void testFindEmployeesByAgeAndName() {
        List<Employee> employeeList = employeeDao.findEmployeesByAgeAndName(22, "tom");
        for (Employee employee: employeeList) {
            System.out.println(employee);
        }
    }

    @Test
    void testFindEmployeesByNameLike() {
        List<Employee> employeeList = employeeDao.findEmployeesByNameLike("t%");
        for (Employee employee: employeeList) {
            System.out.println(employee);
        }
    }
}

基本查詢:

 

 

 

模糊查詢:

 

 

 

六、動態查詢(JpaSpecificationExecutor、Specification)

1、JpaSpecificationExecutor

  JpaSpecificationExecutor 是一個介面。查詢語句都定義在 Specification 中。

package org.springframework.data.jpa.repository;

import java.util.List;
import java.util.Optional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.Nullable;

public interface JpaSpecificationExecutor<T> {
    // 查詢單個對象
  Optional<T> findOne(@Nullable Specification<T> var1);

    // 查詢對象列表
  List<T> findAll(@Nullable Specification<T> var1);

    // 查詢對象列表,並返回分頁數據
  Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);

    // 查詢對象列表,併排序
  List<T> findAll(@Nullable Specification<T> var1, Sort var2);

    // 統計查詢的結果
  long count(@Nullable Specification<T> var1);
}

 

2、Specification

  定義 sql 語句。同樣是一個介面,需要自定義實現類。需要重寫 toPredicate() 方法。

// Root 指查詢的根對象,可以獲取任何屬性。
// CriteriaQuery 標準查詢,可以自定義查詢方式(一般不用)
// CriteriaBuilder 指查詢的構造器,封裝了很多查詢條件
Predicate toPredicate(Root<T> var1, CriteriaQuery<?> var2, CriteriaBuilder var3);

 

package org.springframework.data.jpa.domain;

import java.io.Serializable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.lang.Nullable;

public interface Specification<T> extends Serializable {
  long serialVersionUID = 1L;

  static <T> Specification<T> not(@Nullable Specification<T> spec) {
    return spec == null ? (root, query, builder) -> {
      return null;
    } : (root, query, builder) -> {
      return builder.not(spec.toPredicate(root, query, builder));
    };
  }

  @Nullable
  static <T> Specification<T> where(@Nullable Specification<T> spec) {
    return spec == null ? (root, query, builder) -> {
      return null;
    } : spec;
  }

  @Nullable
  default Specification<T> and(@Nullable Specification<T> other) {
    return SpecificationComposition.composed(this, other, (builder, left, rhs) -> {
      return builder.and(left, rhs);
    });
  }

  @Nullable
  default Specification<T> or(@Nullable Specification<T> other) {
    return SpecificationComposition.composed(this, other, (builder, left, rhs) -> {
      return builder.or(left, rhs);
    });
  }

  @Nullable
  Predicate toPredicate(Root<T> var1, CriteriaQuery<?> var2, CriteriaBuilder var3);
}

 

3、基本使用

(1)步驟:
  Step1:實現 Specification 介面(定義泛型,為查詢的對象類型),重寫 toPredicate() 方法。
  Step2:定義 CriteriaBuilder 查詢條件。

(2)普通查詢

package com.lyh.demo.jpa;

import com.lyh.demo.jpa.bean.Employee;
import com.lyh.demo.jpa.dao.EmployeeDao;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.jpa.domain.Specification;

import javax.persistence.criteria.*;
import java.util.Date;
import java.util.List;

@SpringBootTest
class JpaApplicationTests {

   @Autowired
   private EmployeeDao employeeDao;

   @Test
   void testSave() {
      Employee employee = new Employee();
      employee.setName("tom");
      employee.setAge(22);
      employee.setCreateTime(new Date());
      employeeDao.save(employee);
   }


   @Test
   void testSpecification() {
      // 定義內部類,泛型為 查詢的對象
      Specification<Employee> specification = new

         Specification<Employee>() {

            @Override
            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
               // 獲取比較的屬性
               Path<Object> name = root.get("name");
               // 構建查詢條件, select * from emp where name = "tom";
               Predicate predicate = criteriaBuilder.equal(name, "tom");
               return predicate;
            }


         };
      List<Employee> employeeList = employeeDao.findAll(specification);
      for (Employee employee : employeeList) {
         System.out.println(employee);
      }
   }
}

 

 

 

 

(3)多條件拼接、模糊查詢

package com.lyh.demo.jpa;

import com.lyh.demo.jpa.bean.Employee;
import com.lyh.demo.jpa.dao.EmployeeDao;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.jpa.domain.Specification;

import javax.persistence.criteria.*;
import java.util.Date;
import java.util.List;

@SpringBootTest
class JpaApplicationTests {

   @Autowired
   private EmployeeDao employeeDao;

   @Test
   void testSave() {
      Employee employee = new Employee();
      employee.setName("tom");
      employee.setAge(22);
      employee.setCreateTime(new Date());
      employeeDao.save(employee);
   }


   @Test
   void testSpecification() {
      // 定義內部類,泛型為 查詢的對象
      Specification<Employee> specification = new
         Specification<Employee>() {
            @Override
            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
               // 獲取比較的屬性
               Path<String> name = root.get("name");
               Path<Integer> age = root.get("age");

               // 構建查詢條件, select * from emp where name like "to%" and age >= 22;
               Predicate predicate1 = criteriaBuilder.like(name, "to%");
               Predicate predicate2 = criteriaBuilder.ge(age, 22);
               Predicate predicate = criteriaBuilder.and(predicate1, predicate2);
               return predicate;
            }
         };
      List<Employee> employeeList = employeeDao.findAll(specification);
      for (Employee employee : employeeList) {
         System.out.println(employee);
      }
   }
}

 

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 為內置對象添加原型方法 我們能否為系統的對象的原型中添加方法, 相當於在改變源碼 我希望字元串中有一個倒序字元串的方法 //我希望字元串中有一個倒序字元串的方法 String.prototype.myReverse = function () { for (var i = this.length - ...
  • ©Copyright 蕃薯耀 2019年12月31日 http://fanshuyao.iteye.com/ 使用方法: ©Copyright 蕃薯耀 2019年12月31日 http://fanshuyao.iteye.com/ ...
  • Iframe 高度自適應, js控制Iframe 高度自適應, iframe自適應高度 ©Copyright 蕃薯耀 2019年12月31日 http://fanshuyao.iteye.com/ 方法一:js控制Iframe 高度自適應 這個方法之前一直都在用,沒有問題,但最新發現有些情況不行(具 ...
  • JS內置對象【字元串】 // charAt() 返回字元 // charCodeAt() 返回字元的unicode編碼 var str="hello world"; console.log(str.charCodeAt(4)); // indexOf() 返回出現的位置 // 沒找到則返回-1 va ...
  • 12306 結合日曆時間,推出了“按小時為基準的定時、批量、放票的策略,先到者得”的系統邏輯。 這就導致,所有購票人,都要在搶在某個時間點,比如 10:00:00 , 十幾秒內,或者最好在零點幾秒內,完成購票操作。 ... “電腦隨機抽簽模式”的好處在於: 購票人可以不用急急忙忙地購票,... ...
  • 昨天我們聊了 架構是什麼 ?今天我們來看看架構的前世今生。客戶端-伺服器(C/S)、瀏覽器-伺服器(B/S)、面向服務架構(SOA)、微服務(Microservice)、無伺服器(Serverless)、函數計算(FaaS)等,我們都聽說過不少架構相關的名詞,但它們之間究竟是什麼關係,哪種架構更好?... ...
  • 架構師,老兵哥剛參加工作那些年業界還沒有這個職位,那時候跟技術相關的崗位就是開發工程師、測試工程師和系統工程師,後來隨著軟體規模不斷增長而產生的,尤其是在互聯網浪潮下用戶數和訪問量都是海量化的。在各種機緣巧合下,老兵哥結合個人喜好選擇了走架構師路徑,從懵懵懂懂邊做邊學,到現在總算摸出了些門道,回顧這... ...
  • 1.泛型類 普通的類 這樣的代碼是完全可以執行了,那為什麼還需要泛型類? 1.安全性 上面的代碼編譯是完全可以通過的,但是執行的時候就會出現ClassCastException異常 2.可讀性好,省去了反覆的強制類型轉換。 對於泛型類,java編譯器會將泛型代碼轉換成普通的非泛型代碼, 所以對於虛擬 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...