原文: http://zetcode.com/springboot/datajpanamedquery/

Spring Boot Data JPA @NamedQuery教程展示了如何使用 JPA @NamedQuery创建自定义查询。

Spring 是用于创建企业应用的流行 Java 应用框架。 Spring Boot 是 Spring 框架的演进,可帮助您轻松创建独立的,生产级的基于 Spring 的应用。

Spring Data JPA

Spring Data JPA 有助于实现基于 JPA 的存储库。 它增强了对基于 JPA 的数据访问层的支持。 它使构建使用数据访问技术的 Spring 支持的应用变得更加容易。 Spring Data JPA 是较大的 Spring Data 系列的一部分。

JPA @NamedQuery

@NamedQuery注解是我们创建的预定义查询,并与容器管理的实体相关联。 @Query注解是类似的注解,它直接在存储库方法上声明查找程序查询。 在域类上使用@NamedQuery时,在Repository接口上使用 Spring Data JPA @Query注解。 这样可以将域类从持久性特定的信息中解放出来,这是一件好事。

Spring Boot Data JPA @NamedQuery示例

以下应用是一个简单的 Spring Boot Web 应用,它使用 JPA @NamedQuery创建一个自定义查询。 该示例的数据存储在基于内存的 H2 数据库中。 数据库在应用启动时初始化。

  1. pom.xml
  2. src
  3. ├───main
  4. ├───java
  5. └───com
  6. └───zetcode
  7. Application.java
  8. ├───controller
  9. MyController.java
  10. ├───model
  11. City.java
  12. ├───repository
  13. CityRepository.java
  14. └───service
  15. CityService.java
  16. ICityService.java
  17. └───resources
  18. application.properties
  19. data-h2.sql
  20. schema-h2.sql
  21. ├───static
  22. index.html
  23. └───templates
  24. showCities.ftl
  25. └───test
  26. └───java

这是项目结构。

pom.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
  5. http://maven.apache.org/xsd/maven-4.0.0.xsd">
  6. <modelVersion>4.0.0</modelVersion>
  7. <groupId>com.zetcode</groupId>
  8. <artifactId>springbootdatajpanamedquery</artifactId>
  9. <version>1.0-SNAPSHOT</version>
  10. <packaging>jar</packaging>
  11. <properties>
  12. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  13. <maven.compiler.source>11</maven.compiler.source>
  14. <maven.compiler.target>11</maven.compiler.target>
  15. </properties>
  16. <parent>
  17. <groupId>org.springframework.boot</groupId>
  18. <artifactId>spring-boot-starter-parent</artifactId>
  19. <version>2.1.1.RELEASE</version>
  20. </parent>
  21. <dependencies>
  22. <dependency>
  23. <groupId>com.h2database</groupId>
  24. <artifactId>h2</artifactId>
  25. <scope>runtime</scope>
  26. </dependency>
  27. <dependency>
  28. <groupId>org.springframework.boot</groupId>
  29. <artifactId>spring-boot-starter-freemarker</artifactId>
  30. </dependency>
  31. <dependency>
  32. <groupId>org.springframework.boot</groupId>
  33. <artifactId>spring-boot-starter-web</artifactId>
  34. </dependency>
  35. <dependency>
  36. <groupId>org.springframework.boot</groupId>
  37. <artifactId>spring-boot-starter-data-jpa</artifactId>
  38. </dependency>
  39. </dependencies>
  40. <build>
  41. <plugins>
  42. <plugin>
  43. <groupId>org.springframework.boot</groupId>
  44. <artifactId>spring-boot-maven-plugin</artifactId>
  45. </plugin>
  46. </plugins>
  47. </build>
  48. </project>

Maven POM 文件包含 H2 数据库,Freemarker 和 Spring Boot Data JPA 的依赖项。

resources/application.properties

  1. spring.main.banner-mode=off
  2. spring.datasource.platform=h2
  3. logging.level.org.hibernate.SQL=DEBUG
  4. spring.jpa.show-sql=true
  5. spring.jpa.hibernate.ddl-auto=none

application.properties文件中,我们有各种配置设置。 使用spring.main.banner-mode属性,我们可以关闭 Spring 横幅。

spring.datasource.platform设置数据库的供应商名称。 在初始化脚本中使用它。 spring.jpa.show-sql允许记录 SQL 语句。 最后,spring.jpa.hibernate.ddl-auto禁止从实体自动创建模式。

com/zetcode/model/City.java

  1. package com.zetcode.model;
  2. import java.util.Objects;
  3. import javax.persistence.Entity;
  4. import javax.persistence.GeneratedValue;
  5. import javax.persistence.GenerationType;
  6. import javax.persistence.Id;
  7. import javax.persistence.NamedQuery;
  8. import javax.persistence.Table;
  9. @Entity
  10. @Table(name = "cities")
  11. @NamedQuery(name = "City.findAllOrderedByNameDescending",
  12. query = "SELECT c FROM City c ORDER BY c.name DESC")
  13. public class City {
  14. @Id
  15. @GeneratedValue(strategy = GenerationType.AUTO)
  16. private Long id;
  17. private String name;
  18. private int population;
  19. public City() {
  20. }
  21. public City(String name, int population) {
  22. this.name = name;
  23. this.population = population;
  24. }
  25. public Long getId() {
  26. return id;
  27. }
  28. public void setId(Long id) {
  29. this.id = id;
  30. }
  31. public String getName() {
  32. return name;
  33. }
  34. public void setName(String name) {
  35. this.name = name;
  36. }
  37. public int getPopulation() {
  38. return population;
  39. }
  40. public void setPopulation(int population) {
  41. this.population = population;
  42. }
  43. @Override
  44. public int hashCode() {
  45. int hash = 7;
  46. hash = 79 * hash + Objects.hashCode(this.id);
  47. hash = 79 * hash + Objects.hashCode(this.name);
  48. hash = 79 * hash + this.population;
  49. return hash;
  50. }
  51. @Override
  52. public boolean equals(Object obj) {
  53. if (this == obj) {
  54. return true;
  55. }
  56. if (obj == null) {
  57. return false;
  58. }
  59. if (getClass() != obj.getClass()) {
  60. return false;
  61. }
  62. final City other = (City) obj;
  63. if (this.population != other.population) {
  64. return false;
  65. }
  66. if (!Objects.equals(this.name, other.name)) {
  67. return false;
  68. }
  69. return Objects.equals(this.id, other.id);
  70. }
  71. @Override
  72. public String toString() {
  73. var builder = new StringBuilder();
  74. builder.append("City{id=").append(id).append(", name=")
  75. .append(name).append(", population=")
  76. .append(population).append("}");
  77. return builder.toString();
  78. }
  79. }

这是City实体。 它包含一个命名查询。

  1. @Entity
  2. @Table(name = "cities")
  3. @NamedQuery(name = "City.findAllOrderedDescending",
  4. query = "SELECT c FROM City c ORDER BY c.name DESC")
  5. public class City {

@Entity注解指定该类是一个实体,并映射到数据库表。 @Table注解指定要用于映射的数据库表的名称。 @NamedQuery定义了一个命名查询,该查询返回按名称降序排列的所有城市。

resources/schema-h2.sql

  1. CREATE TABLE cities(id INT PRIMARY KEY AUTO_INCREMENT,
  2. name VARCHAR(255), population INT);

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

resources/data-h2.sql

  1. INSERT INTO cities(name, population) VALUES('Bratislava', 432000);
  2. INSERT INTO cities(name, population) VALUES('Budapest', 1759000);
  3. INSERT INTO cities(name, population) VALUES('Prague', 1280000);
  4. INSERT INTO cities(name, population) VALUES('Warsaw', 1748000);
  5. INSERT INTO cities(name, population) VALUES('Los Angeles', 3971000);
  6. INSERT INTO cities(name, population) VALUES('New York', 8550000);
  7. INSERT INTO cities(name, population) VALUES('Edinburgh', 464000);
  8. INSERT INTO cities(name, population) VALUES('Suzhou', 4327066);
  9. INSERT INTO cities(name, population) VALUES('Zhengzhou', 4122087);
  10. INSERT INTO cities(name, population) VALUES('Berlin', 3671000);

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

com/zetcode/CityRepository.java

  1. package com.zetcode.repository;
  2. import com.zetcode.model.City;
  3. import java.util.List;
  4. import org.springframework.data.repository.CrudRepository;
  5. import org.springframework.stereotype.Repository;
  6. @Repository
  7. public interface CityRepository extends CrudRepository<City, Long> {
  8. List<City> findAllOrderedByNameDescending();
  9. }

我们将findAllOrderedByNameDescending()的声明添加到存储库接口中。

com/zetcode/service/ICityService.java

  1. package com.zetcode.service;
  2. import com.zetcode.model.City;
  3. import java.util.List;
  4. public interface ICityService {
  5. List<City> findAllOrderedByNameDescending();
  6. }

ICityService包含一种契约方法,用于按名称降序排列所有城市。

com/zetcode/service/CityService.java

  1. package com.zetcode.service;
  2. import com.zetcode.model.City;
  3. import com.zetcode.repository.CityRepository;
  4. import java.util.List;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.stereotype.Service;
  7. @Service
  8. public class CityService implements ICityService {
  9. @Autowired
  10. private CityRepository repository;
  11. @Override
  12. public List<City> findAllOrderedByNameDescending() {
  13. var cities = (List<City>) repository.findAllOrderedByNameDescending();
  14. return cities;
  15. }
  16. }

CityService包含findAllOrderedByNameDescending()方法的实现。 我们使用存储库从数据库检索数据。

com/zetcode/controller/MyController.java

  1. package com.zetcode.controller;
  2. import com.zetcode.model.City;
  3. import com.zetcode.service.ICityService;
  4. import java.util.List;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.stereotype.Controller;
  7. import org.springframework.ui.Model;
  8. import org.springframework.web.bind.annotation.GetMapping;
  9. @Controller
  10. public class MyController {
  11. @Autowired
  12. private ICityService cityService;
  13. @GetMapping("/showCities")
  14. public String findAllOrderedByNameDescending(Model model) {
  15. var cities = (List<City>) cityService.findAllOrderedByNameDescending();
  16. model.addAttribute("cities", cities);
  17. return "showCities";
  18. }
  19. }

MyController类用@Controller注解。

  1. @Autowired
  2. private ICityService cityService;

我们在countryService字段中插入ICityService

  1. @GetMapping("/showCities")
  2. public String findAllOrderedByNameDescending(Model model) {
  3. var cities = (List<City>) cityService.findAllOrderedByNameDescending();
  4. System.out.println(cities);
  5. model.addAttribute("cities", cities);
  6. return "showCities";
  7. }

我们将带有showCities路径的请求映射到控制器的findAllOrderedByNameDescending()方法。 该模型将获得按名称降序排列的城市列表,并将处理发送到showCities.ftl Freemarker 模板文件。

resources/templates/showCities.ftl

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>Cities</title>
  5. <meta charset="UTF-8">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. </head>
  8. <body>
  9. <h2>List of cities ordered by name in descending order</h2>
  10. <table>
  11. <tr>
  12. <th>Id</th>
  13. <th>Name</th>
  14. <th>Population</th>
  15. </tr>
  16. <#list cities as city>
  17. <tr>
  18. <td>${city.id}</td>
  19. <td>${city.name}</td>
  20. <td>${city.population}</td>
  21. </tr>
  22. </#list>
  23. </table>
  24. </body>
  25. </html>

showCities.ftl模板文件中,我们在 HTML 表中显示数据。

resources/static/index.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>Home page</title>
  5. <meta charset="UTF-8"/>
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  7. </head>
  8. <body>
  9. <a href="showCities">Show ordered cities by name in descending order</a>
  10. </body>
  11. </html>

index.html中有一个链接,显示规定的城市。

com/zetcode/Application.java

  1. package com.zetcode;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. @SpringBootApplication
  5. public class Application {
  6. public static void main(String[] args) {
  7. SpringApplication.run(Application.class, args);
  8. }
  9. }

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

  1. $ mvn spring-boot:run

应用运行后,我们可以导航到localhost:8080

在本教程中,我们展示了如何在 Spring Boot 应用中使用 JPA @NamedQuery注解创建自定义 JPQL 查询。 您可能也对相关教程感兴趣: Spring Boot Data JPA @Query教程Spring Boot CrudRepository教程Spring Boot REST Data JPA 教程或列出所有 Spring Boot 教程