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
, findByFirstnameEndingWith
或 findByFirstnameEquals
。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
这是项目结构。
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 的依赖项。
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
禁用了从实体自动创建模式的功能。
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
提供了为主键值生成策略的规范。
CREATE TABLE cities(id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255), population INT);
当应用程序启动时,将执行 schema-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
文件。它用数据填充表。
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);
在那里,查询将查找人口少于指定数量的城市。
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
提供了两个契约方法。
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
上调用。
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' 结尾的城市。
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 派生查询来获取数据。