68、Spring Data JPA 的 方法名关键字查询(全自动,既不需要提供sql语句,也不需要提供方法体)

2023-09-15 15:27:51

1、方法名关键字查询(全自动,既不需要提供sql语句,也不需要提供方法体)
2、@Query查询(半自动:提供 SQL 或 JPQL 查询)
3、自定义查询(全手动)

★ 方法名关键字查询(全自动)

(1)继承 CrudRepository 接口 的 DAO 组件可按特定规则来定义查询方法,
     只要这些查询方法的 方法名 遵守特定的规则,
     Spring Data 将会自动为这些方法生成 查询语句、提供 方法实现体。

(2)方法名关键字的查询方法
     能以 find...By、read...By、query…By、count…By(查询记录的条数)、get…By开头,
     并在方法名中嵌入特定关键字即可,Spring Data就会自动生成相应的查询方法体。

▲ 关键字规则

 在方法名中将属性名、运算符都设计成关键字,比如如下:
 - findByName(String name),这表明根据name属性执行查询。
 - findByAgeGreaterThan(int age):表明查询age属性大于指定值的记录。

▲ 关键字方法中同样可定义Pagable、Sort参数,用于控制分页和排序。

 说明:如果要做分页或排序的查询,
 其实没必须要去继承PagingAndSortingRepository,继承CrudRepository也是可行的

▲ 需要说明的情况:

 一种情况需要说明,对比如下两个方法:
 - findByAddressAndZip:该方法要根据address和zip两个属性进行查询,
   它对应的JPQL片段为:... where x.address = ?1 and zip = ?2。

 - findByAddressZip:留意该方法名的Address和Zip之间既没有And,也没有Or,
   那就表明用的是“属性路径”方式,表明该方法要根据address属性的zip属性进行查询,
   它对应的JPQL片段为:... where x.address.zip = ?1。


如果你的方法名中的关键字写错了,往往就会报QueryCreateException。

代码演示:

数据库数据

在这里插入图片描述

创建对应的两个实体类。
Student实体类
这节用到的一些类和配置文件
在这里插入图片描述

Clazz 实体类
在这里插入图片描述

//根据班级名称查询对象–ClazzDao

方法
在这里插入图片描述
测试结果
在这里插入图片描述

//根据班级名称模糊查询–ClazzDao

方法:
在这里插入图片描述

测试结果
在这里插入图片描述

查询年龄大于指定参数的学生–StudentDao

方法:
在这里插入图片描述

测试结果:
在这里插入图片描述

根据年龄和班级名称查询学生–StudentDao

在这里插入图片描述

方法:
在这里插入图片描述

测试结果:

在这里插入图片描述

根据地址后缀进行分页查询,查询 address 带有 “洞” 的学生并进行分页 – StudentDao

在这里插入图片描述

结果:
在这里插入图片描述
在这里插入图片描述

完整代码

Student

package cn.ljh.app.domain;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;

/**
 * strategy:策略 , GenerationType:生成类型  , Column:列
 */

//修饰该类成为实体类
@Entity
@Getter
@Setter
//表示该类映射到数据库的student_inf表
@Table(name = "student_inf")
public class Student
{
    @Id //设置为主键
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 主键自增策略
    @Column(name = "student_id") //该属性名映射到数据库表的列名
    private Integer id;

    private String name;
    private int age;
    private String address;
    private char gender;

    //关联关系,多个学生对应一个教室
    //targetEntity:指定关联实体的类型,不指定也没问题,底层会通过反射去识别
    @ManyToOne(targetEntity = Clazz.class)
    //JoinColumn: name = "clazz_code"  ,映射外键列列名 ,这个"clazz_code "是指映射的 Clazz 类中的成员变量
    //referencedColumnName = "clazz_code" ,这个"clazz_code" 是指表的列名
    //referencedColumnName: --> name = "clazz_code"这个外键列名引用到对方的数据表(clazz_inf)的列名叫"clazz_code"
    @JoinColumn(name = "clazz_code",referencedColumnName = "clazz_code")
    private Clazz clazz;

    @Override
    public String toString()
    {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                ", gender=" + gender +
                '}';
    }
}

Clazz

package cn.ljh.app.domain;


import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;


@Entity //修饰该类为实体类
@Table(name = "clazz_inf") //该实体类映射到数据库的 clazz_inf 表
@Getter
@Setter
public class Clazz
{
    @Id//主键id
    @Column(name = "clazz_code") //表示这个属性在数据库表中的列名叫这个clazz_code
    @GeneratedValue(strategy = GenerationType.IDENTITY) //主键id的自增策略
    private Integer clazz_code; //班级号
    private String name;  //班级名称

    //关联关系,一个教室对应多个学生
    //targetEntity:指定关联实体的类型,不指定也没问题,底层会通过反射去识别
    //mappedBy 属性(clazz) 关联实体(Student)中,哪个属性(student.clazz)引用到当前实体(Student实体)
    @OneToMany(targetEntity = Student.class,mappedBy = "clazz")
    private Set<Student> students = new HashSet<>();

    @Override
    public String toString()
    {
        return "Clazz{" +
                "clazz_code=" + clazz_code +
                ", name='" + name + '\'' +
                '}';
    }
}

ClazzDao

package cn.ljh.app.dao;

import cn.ljh.app.domain.Clazz;
import org.springframework.data.repository.CrudRepository;

import java.util.List;


//CrudRepository 的第一个泛型参数是被操作的实体类型,第二个参数是实体的主键类型
//方法名关键字查询: Spring Data 会自动生成查询语句、生成查询方法体
public interface ClazzDao extends CrudRepository<Clazz, Integer>
{
    //根据班级名称查询对象
    Clazz findByName(String name);

    //根据班级名称模糊查询
    List<Clazz> findByNameLike(String namePattern);


}

StudentDao

package cn.ljh.app.dao;

import cn.ljh.app.domain.Student;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.CrudRepository;

import java.util.List;

//CrudRepository 的第一个泛型参数是被操作的实体类型,第二个参数是实体的主键类型
public interface StudentDao extends CrudRepository<Student,Integer>
{
    //查询年龄大于指定参数的学生
    List<Student> findByAgeGreaterThan(int startAge);

    //根据年龄和班级名称查询学生
    //Age 和 ClazzName 用 And 连接起来,表示两个查询条件,
    //ClazzName这两个单词中间没有And连接起来,表示是一个路径写法,表示是Clazz类的name属性
    List<Student> findByAgeAndClazzName(int age , String clazzName);

    //根据地址后缀进行分页查询,查询 address 带有 "洞" 的学生并进行分页
    Page<Student> findByAddressEndingWith(String addrSuffix, Pageable pageable);
    
}

ClazzDaoTest

package cn.ljh.app.dao;


import cn.ljh.app.domain.Clazz;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

//SpringBootTest.WebEnvironment.NONE : 表示不需要web环境
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class ClazzDaoTest
{
    @Autowired
    private ClazzDao clazzDao;

    //参数化测试
    @ParameterizedTest
    //参数只有一个的测试用 @ValueSource
    //根据班级名称查询对象
    @ValueSource(strings = {"超级A营","超级B班","aaa"})
    public void testFindByName(String name)
    {
        Clazz clazz = clazzDao.findByName(name);
        System.err.println(clazz);
    }

    @ParameterizedTest
    //根据班级名称模糊查询
    @ValueSource(strings = {"超级%","%营"})
    public void testFindByNameLike(String namePattern)
    {
        List<Clazz> clazzes = clazzDao.findByNameLike(namePattern);
        clazzes.forEach(System.err::println);
    }


}

StudentDaoTest

package cn.ljh.app.dao;


import cn.ljh.app.domain.Student;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;

import java.util.List;

//SpringBootTest.WebEnvironment.NONE : 表示不需要web环境
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class StudentDaoTest
{
    @Autowired
    private StudentDao studentDao;

    /**
     * @ValueSource: 每次只能传一个参数
     * @CsvSource:每次可以传多个参数
     */

    //需求:查询年龄大于指定参数的记录
    //参数化测试
    @ParameterizedTest
    @ValueSource(ints = {20, 200})
    public void testFindByAgeGreaterThan(int startAge)
    {
        List<Student> students = studentDao.findByAgeGreaterThan(startAge);
        students.forEach(System.err::println);
    }

    //根据年龄和班级名称查询学生
    //Age 和 ClazzName 用 And 连接起来,表示两个查询条件,
    //ClazzName这两个单词中间没有And连接起来,表示是一个路径写法,表示是Clazz类的name属性
    @ParameterizedTest
    //参数一个是int,一个是String,这个注解在传参的时候会自动进行类型转换
    @CsvSource(value = {"20,超级A营", "18,超级D班"})
    public void testFindByAgeAndClazzName(int age, String clazzName)
    {
        List<Student> students = studentDao.findByAgeAndClazzName(age, clazzName);
        students.forEach(System.err::println);
    }


    //pageNo: 要查询哪一页的页数 , pageSize: 每页显示的条数
    @ParameterizedTest
    @CsvSource({"洞,2,3","洞,1,4","洞,3,2"})
    public void testFindByAddressEndingWith(String addrSuffix, int pageNo, int pageSize)
    {
        //分页对象,此处的pageNo是从0开始的,0代表第一页,所以这里的 pageNo 要 -1
        Pageable pageable1 = PageRequest.of(pageNo - 1, pageSize);
        Page<Student> students = studentDao.findByAddressEndingWith(addrSuffix, pageable1);

        int number = students.getNumber() + 1;
        System.err.println("总页数:" + students.getTotalPages());
        System.err.println("总条数:" + students.getTotalElements());
        System.err.println("当前第:" + number + " 页");
        System.err.println("当前页有:" + students.getNumberOfElements() + " 条数据");
        students.forEach(System.err::println);
    }
}

application.properties

# 配置连接数据源,这些配置由 DataSourceProperties 类负责处理---
# SpringBoot 读取到这些配置信息后,会使用 AutoConfiguration 去容器中自动配置 DataSource Bean
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456

# 针对 HikariCP 进行详细配置---
# 指定连接池最大只能有20个连接
#spring.datasource.hikari.maximum-pool-size=20

# 配置 JPA 相关属性,由 JpaProperties 类负责处理---
# SpringBoot 读取到这些配置信息后,会使用 AutoConfiguration 去容器中自动配置 EntityManagerFactory Bean
# JPA 底层就是依赖这个 EntityManagerFactory

# 自动建表,只能true或者false
# spring.jpa.generate-ddl=true
# 这个也是自动建表,不过比spring.jpa.generate-ddl更严谨,作用:如果已有数据表,无需创建,否则创建数据表
spring.jpa.hibernate.ddl-auto=update
# 指定操作的数据库
spring.jpa.database=mysql
# 是否在执行的时候显示sql语句
spring.jpa.show-sql=true

db.sql

drop database springboot;
create database springboot;
use springboot;
-- 创建clazz_inf表
create table clazz_inf
(
    clazz_code int primary key auto_increment,
    name varchar(255)
);
-- 创建student_inf表
create table student_inf
(
    student_id int primary key auto_increment,
    name varchar(255),
    age int,
    address varchar(255),
    gender char(2),
    clazz_code int,
    foreign key(clazz_code) references clazz_inf(clazz_code)
);
-- 向clazz_inf表插入数据
insert into clazz_inf
values
(null, '疯狂Java训练营'),
(null, '疯狂Java就业班'),
(null, '疯狂Java基础班'),
(null, '疯狂Java提高班');

-- 向student_inf表插入数据
insert into student_inf
values
(null, '孙悟空', 500, '花果山水帘洞', '男', 1),
(null, '牛魔王', 800, '积雷山摩云洞', '男', 1),
(null, '猪八戒', 600, '福陵山云栈洞', '男', 2),
(null, '沙和尚', 580, '流沙河', '男', 3),
(null, '白鼠精', 23, '陷空山无底洞',  '女', 2),
(null, '蜘蛛精', 18, '盘丝岭盘丝洞', '女', 4),
(null, '玉面狐狸', 21, '积雷山摩云洞', '女', 3),
(null, '杏仙', 19, '荆棘岭木仙庵', '女', 4);

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.4.5</version>
    </parent>
    <groupId>cn.ljh</groupId>
    <artifactId>spring_data_jpa_keyword</artifactId>
    <version>1.0.0</version>
    <name>spring_data_jpa_keyword</name>
    <properties>
        <java.version>11</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>
            <scope>runtime</scope>
        </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>
        </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>


更多推荐

Springboot2.7集成websocket及相关问题

1、集成websocket完整代码导入maven依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>服务端代码(1)注入be

使用亚马逊云服务器在 G4 实例上运行 Android 应用程序

随着Android应用程序和游戏变得越来越丰富,其中有些甚至比PC上的软件更易于使用和娱乐,因此许多人希望能够在云上运行Android游戏或应用程序,而在EC2实例上运行Android的解决方案可以让开发人员更轻松地测试和运行Android应用程序。在这篇博客文章中,我们将展示如何使用NICEDCV在Anbox中运行A

如何通过文件自动备份软件进行自动化备份?

​为什么要使用文件自动备份软件有一位做客户资料保管登记的朋友,每天会在电脑上录入很多新的客户资料,并需要进行相关维护。比如删掉一些取消合作的客户,或者添加一些备注等等。对于像他这种工作性质的人来说,很需要一个可以进行文件自动备份的软件,无论电脑出现什么问题,可以将重要资料还原。其实每个人有对自己而言特别珍贵或重要的资料

一份企业业务流程自动化指南

自2020年以来,与低代码相关的种种趋势已经充分表明,更加高效的开发速度是企业数字化转型过程中的主要目标之一。Gartner曾预测,到2023年底,低代码开发技术市场将增长20%。此外,他们还预计,到2026年,非正式IT部门的开发人员将占低代码开发工具用户群至少80%的比例,而这一比例在2021年为60%。基于过去所

贪心算法-会议室问题

1、题目描述一些项目要占用一个会议室宣讲,会议室不能同时容纳两个项目。现在给你两个长度一样的数组,starts数组代码每个会议开始的时间,ends数组代表每个会议结束的时间。在给你一个当前时间,请你求出当日可以利用会议室宣讲的最大值思路分析:1.按照最早开始的会议排序,最早开始的优先。2.按照最短时间排序,时间最短的优

Linux系统编程——网络编程的学习

Linux系统编程学习相关博文Linux系统编程——文件编程的学习Linux系统编程——进程的学习Linux系统编程——进程间通信的学习Linux系统编程——线程的学习Linux系统编程——网络编程的学习一、概述1.TCP/UDP2.端口号3.字节序4.Sockt服务器和客户端的开发步骤1.服务器2.客户端二、网络编程

Python+Requests+Excel接口测试实战

1、EXCEL文件接口保存方式,如图。2、然后就是读取EXCEL文件中的数据方法,如下:1importxlrd234classreadExcel(object):5def__init__(self,path):6self.path=path78@property9defgetSheet(self):10#获取索引11x

SQL注入脚本编写

文章目录布尔盲注脚本延时注入脚本安装xampp,在conf目录下修改它的http配置文件,如下,找到配置文件:修改配置文件中的默认主页,让xampp能访问phpstudy的www目录,因为xampp的响应速度比phpstudy快得多,所以用它做SQL注入脚本的服务器:布尔盲注脚本以sqli-labs第8关为例,在第8关

Vue3通透教程【十七】Vite构建TS版本Vue项目

文章目录🌟写在前面🌟创建TS版本的Vue3项目🌟插件安装🌟写在最后🌟写在前面专栏介绍:凉哥作为Vue的忠实粉丝输出过大量的Vue文章,应粉丝要求开始更新Vue3的相关技术文章,Vue框架目前的地位大家应该都晓得,所谓三大框架使用人数最多,公司选型最多的框架,凉哥之前在文章中也提到过就是Vue框架之所以火起来的

高压配电安全监测系统:确保电力系统的稳定运行

随着现代社会对电力需求的不断增长,高压配电系统的重要性日益凸显。为了保证电力系统的稳定运行,提高供电质量,采用高压配电安全监测系统至关重要。力安科技高压配电安全监测系统通过在每面高压柜(进线柜、出线柜、联络柜)配置一只A62系列电力探测器,在断路器的进出铜排接线处各安装一只无线测温探测器,并加装开口电流互感器一套,经G

异步机制的简单实现

计算机有两种阻塞,一是cpu阻塞,二是io阻塞。cpu阻塞就是cpu密集计算,io阻塞比如等待网络响应,等待磁盘响应,纯粹是浪费时间。线程机制和异步机制都可避免io阻塞,但cpu阻塞的负面效果就只有线程可以避免了。在现代计算机语言里,大量线程切换虽然会有性能问题,但是线程用起来简单,而且线程能在固定时间切换,可保证实时

热文推荐