ZetCode

Spring Boot Data JPA 派生查询

最后修改于 2023 年 7 月 25 日

在本文中,我们将展示如何从方法名创建查询。

Spring 是一个流行的 Java 应用程序框架,用于创建企业应用程序。Spring Boot 是 Spring 框架的演进,它有助于以最小的 effort 创建基于 Spring 的独立、生产级应用程序。

Spring Data JPA

Spring Data JPA 方便了基于 JPA 的存储库的实现。它增强了对基于 JPA 的数据访问层的支持。它使得构建使用数据访问技术的 Spring 驱动的应用程序更容易。Spring Data JPA 是更大的 Spring Data 系列的一部分。

Spring Data JPA 派生查询

Spring Data JPA 可以从方法名创建查询。这是一种特定的 *约定优于配置* 形式。Spring Data JPA 从特定关键字结合属性创建查询;例如:findByAgeLessThan, findByFirstnameEndingWithfindByFirstnameEquals。Spring Data JPA 文档中提供了关键字列表。

Spring Boot Data JPA 派生查询示例

以下应用程序使用了两个派生查询。

build.gradle
...
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           │   Application.java
│   │           │   MyRunner.java
│   │           ├───model
│   │           │       City.java
│   │           ├───repository
│   │           │       CityRepository.java
│   │           └───service
│   │                   CityService.java
│   │                   ICityService.java
│   └───resources
│           application.properties
│           data-h2.sql
│           schema-h2.sql
└───test
    └───java

这是项目结构。

build.gradle
plugins {
    id 'org.springframework.boot' version '3.1.1'
    id 'io.spring.dependency-management' version '1.1.0'
    id 'java'
}

group = 'com.zetcode'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    runtimeOnly 'com.h2database:h2'
}

Gradle 构建文件包含 H2 数据库和 Spring Boot Data JPA 的依赖项。

resources/application.properties
spring.main.banner-mode=off
spring.sql.init.platform=h2
spring.jpa.hibernate.ddl-auto=none

application.properties 文件中,我们编写了 Spring Boot 应用程序的各种配置设置。使用 spring.main.banner-mode 属性,我们关闭了 Spring banner。

spring.sql.init.platform 设置数据库的供应商名称。它用于初始化脚本中。spring.jpa.hibernate.ddl-auto 禁用了从实体自动创建模式的功能。

com/zetcode/model/City.java
package com.zetcode.model;

import java.util.Objects;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

@Entity
@Table(name = "cities")
public class City {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String name;
    private int population;

    public City() {
    }

    public City(String name, int population) {
        this.name = name;
        this.population = population;
    }

    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 int getPopulation() {
        return population;
    }

    public void setPopulation(int population) {
        this.population = population;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 79 * hash + Objects.hashCode(this.id);
        hash = 79 * hash + Objects.hashCode(this.name);
        hash = 79 * hash + this.population;
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final City other = (City) obj;
        if (this.population != other.population) {
            return false;
        }
        if (!Objects.equals(this.name, other.name)) {
            return false;
        }
        return Objects.equals(this.id, other.id);
    }

    @Override
    public String toString() {

        var builder = new StringBuilder();
        builder.append("City{id=").append(id).append(", name=")
                .append(name).append(", population=")
                .append(population).append("}");

        return builder.toString();
    }
}

这是 City 实体。每个实体都必须至少定义两个注解:@Entity@Id

@Entity
@Table(name = "cities")
public class City {

@Entity 注解指定该类是一个实体,并映射到一个数据库表。@Table 注解指定用于映射的数据库表的名称。

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@Id 注解指定了实体的标识符,@GeneratedValue 提供了为主键值生成策略的规范。

resources/schema-h2.sql
CREATE TABLE cities(id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255), population INT);

当应用程序启动时,将执行 schema-h2.sql 脚本。它创建一个新的数据库表。

resources/data-h2.sql
INSERT INTO cities(name, population) VALUES('Bratislava', 432000);
INSERT INTO cities(name, population) VALUES('Budapest', 1759000);
INSERT INTO cities(name, population) VALUES('Prague', 1280000);
INSERT INTO cities(name, population) VALUES('Warsaw', 1748000);
INSERT INTO cities(name, population) VALUES('Los Angeles', 3971000);
INSERT INTO cities(name, population) VALUES('New York', 8550000);
INSERT INTO cities(name, population) VALUES('Brest', 139163);
INSERT INTO cities(name, population) VALUES('Edinburgh', 464000);
INSERT INTO cities(name, population) VALUES('Suzhou', 4327066);
INSERT INTO cities(name, population) VALUES('Zhengzhou', 4122087);
INSERT INTO cities(name, population) VALUES('Berlin', 3671000);
INSERT INTO cities(name, population) VALUES('Bucharest', 1836000);

稍后,将执行 data-h2.sql 文件。它用数据填充表。

com/zetcode/repository/CityRepository.java
package com.zetcode.repository;

import com.zetcode.model.City;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface CityRepository extends CrudRepository<City, Long> {

    List<City> findByNameEndingWith(String ending);
    List<City> findByPopulationLessThan(int population);
}

我们有两个方法,从中生成派生查询。

List<City> findByNameEndingWith(String ending);

在这里,查询将查找以指定字符串结尾的城市名称。

List<City> findByPopulationLessThan(int population);

在那里,查询将查找人口少于指定数量的城市。

com/zetcode/service/ICityService.java
package com.zetcode.service;

import com.zetcode.model.City;

import java.util.List;

public interface ICityService {

    List<City> findByNameEndingWith(String ending);
    List<City> findByPopulationLessThan(int population);
}

ICityService 提供了两个契约方法。

com/zetcode/service/CityService.java
package com.zetcode.service;

import com.zetcode.model.City;
import com.zetcode.repository.CityRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class CityService implements ICityService {

    @Autowired
    private CityRepository cityRepository;

    @Override
    public List<City> findByNameEndingWith(String ending) {
        return cityRepository.findByNameEndingWith(ending);
    }

    @Override
    public List<City> findByPopulationLessThan(int population) {
        return cityRepository.findByPopulationLessThan(population);
    }
}

CityService 包含服务方法的实现。派生查询在 cityRepository 上调用。

com/zetcode/MyRunner.java
package com.zetcode;

import com.zetcode.service.ICityService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class MyRunner implements CommandLineRunner {

    private static final Logger logger = LoggerFactory.getLogger(MyRunner.class);

    private final ICityService cityService;

    @Autowired
    public MyRunner(ICityService cityService) {
        this.cityService = cityService;
    }

    @Override
    public void run(String... args) throws Exception {

        logger.info("Finding cities having population less than one million");
        var res1 = cityService.findByPopulationLessThan(1000000);
        logger.info("{}", res1);

        logger.info("Finding cities by name ending with 'est'");
        var res2 = cityService.findByNameEndingWith("est");
        logger.info("{}", res2);
    }
}

MyRunner 获取所有人口少于一百万的城市,以及所有名称以 'est' 结尾的城市。

com/zetcode/Application.java
package com.zetcode;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Application 设置了 Spring Boot 应用程序。@SpringBootApplication 启用了自动配置和组件扫描。

$ ./gradlew bootRun -q
...
... Finding cities having population less than one million
... [City{id=1, name=Bratislava, population=432000}, City{id=7, name=Brest, population=139163}, 
     City{id=8, name=Edinburgh, population=464000}]
... Finding cities by name ending with 'est'
... [City{id=2, name=Budapest, population=1759000}, City{id=7, name=Brest, population=139163}, 
     City{id=12, name=Bucharest, population=1836000}]
...

我们运行应用程序。

在本文中,我们使用 Spring Data JPA 派生查询来获取数据。

作者

我叫 Jan Bodnar,是一位充满激情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。迄今为止,我撰写了超过 1,400 篇文章和 8 本电子书。我拥有超过十年的编程教学经验。

列出 所有 Spring Boot 教程