Spring Boot Data JPA Query By Example
最后修改于 2023 年 7 月 25 日
在本文中,我们将展示如何使用 Spring Data JPA Query By Example 技术创建查询。
Spring 是一个流行的 Java 应用程序框架,用于创建企业应用程序。Spring Boot 是 Spring 框架的演进,它有助于以最小的 effort 创建基于 Spring 的独立、生产级应用程序。
Spring Data JPA
Spring Data JPA 促进了基于 JPA 的存储库的实现。它增强了对基于 JPA 的数据访问层的支持。它使构建使用数据访问技术的 Spring 应用程序变得更容易。Spring Data JPA 是更大的 Spring Data 系列的一部分。
Spring Data JPA Query By Example
Query by Example (QBE) 是一种用户友好的查询技术,具有简单的界面。它允许动态创建查询。我们不需要用特定于存储的查询语言编写查询。
我们使用三个对象。probe
是一个实际的域对象的示例,其中包含已填充的字段。ExampleMatcher
包含有关如何匹配特定字段的详细信息。Example
由 probe 和 ExampleMatcher
组成。它用于创建查询。
QBE 有一些限制;它无法创建一些更高级的查询。
Spring Boot Data JPA QBE 示例
以下应用程序使用 QBE 生成查询。
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
。
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.data.repository.query.QueryByExampleExecutor; import org.springframework.stereotype.Repository; @Repository public interface CityRepository extends CrudRepository<City, Long>, QueryByExampleExecutor<City> { }
我们通过从 QueryByExampleExecutor
扩展我们的存储库来启用 QBE API。
package com.zetcode.service; import com.zetcode.model.City; import java.util.List; public interface ICityService { List<City> findByNameEnding(String ending); List<City> findByName(String name); }
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.data.domain.Example; import org.springframework.data.domain.ExampleMatcher; import org.springframework.stereotype.Service; import java.util.List; import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers.exact; @Service public class CityService implements ICityService { private final CityRepository cityRepository; @Autowired public CityService(CityRepository cityRepository) { this.cityRepository = cityRepository; } @Override public List<City> findByNameEnding(String ending) { var city = new City(); city.setName(ending); var matcher = ExampleMatcher.matching() .withMatcher("name", ExampleMatcher.GenericPropertyMatcher::endsWith) .withIgnorePaths("population"); var example = Example.of(city, matcher); return (List<City>) cityRepository.findAll(example); } @Override public List<City> findByName(String name) { var city = new City(); city.setName(name); var matcher = ExampleMatcher.matching() .withMatcher("name", exact()) .withIgnorePaths("population"); var example = Example.of(city, matcher); return (List<City>) cityRepository.findAll(example); } }
CityService
包含服务方法实现。
var city = new City(); city.setName(ending);
我们有一个 City
域对象。这被称为 probe。
var matcher = ExampleMatcher.matching() .withMatcher("name", match -> match.endsWith()) .withIgnorePaths("population");
匹配器将字符串结尾与城市名称匹配,并忽略人口。
var example = Example.of(city, matcher);
Example
由域对象和匹配器创建。
return (List<City>) cityRepository.findAll(example);
Example
被传递给 findAll
方法。
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 by name"); var res1 = cityService.findByName("Bratislava"); logger.info("{}", res1); var res2 = cityService.findByName("Berlin"); logger.info("{}", res2); logger.info("Finding cities by name ending with est"); var res3 = cityService.findByNameEnding("est"); logger.info("{}", res3); } }
MyRunner
查找 'Bratislava' 和 'Berlin' 城市,并找到所有名称以 '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 ... ... Finding cities by name ... [City{id=1, name=Bratislava, population=432000}] ... [City{id=11, name=Berlin, population=3671000}] ... 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 Query By Example 技术来生成查询。