使用 JPA 访问数据

本指南将引导您完成构建应用程序的过程,该应用程序使用 Spring Data JPA 将数据存储到关系数据库并从中检索数据。

您将构建的内容

您将构建一个应用程序,该应用程序将 `Customer` POJO(普通旧 Java 对象)存储在基于内存的数据库中。

您需要什么

如何完成本指南

与大多数 Spring 入门指南 一样,您可以从头开始并完成每个步骤,也可以绕过您已经熟悉的基本的设置步骤。无论哪种方式,您最终都会得到可运行的代码。

从头开始,请继续从 Spring Initializr 开始

跳过基础步骤,请执行以下操作

完成之后,您可以将您的结果与 `gs-accessing-data-jpa/complete` 中的代码进行比较。

从 Spring Initializr 开始

您可以使用此预初始化项目 并单击“生成”以下载 ZIP 文件。此项目已配置为适合本教程中的示例。

要手动初始化项目

  1. 导航到https://start.spring.io。此服务将引入应用程序所需的所有依赖项,并为您完成大部分设置工作。

  2. 选择 Gradle 或 Maven 以及您要使用的语言。本指南假设您选择了 Java。

  3. 单击依赖项并选择Spring Data JPA,然后选择H2 数据库

  4. 单击生成

  5. 下载生成的 ZIP 文件,这是一个使用您的选择配置的 Web 应用程序的存档。

如果您的 IDE 集成了 Spring Initializr,则可以从您的 IDE 中完成此过程。
您也可以从 Github 分叉项目,并在您的 IDE 或其他编辑器中打开它。

定义简单的实体

在此示例中,您存储 `Customer` 对象,每个对象都用作 JPA 实体进行注释。以下清单显示了 Customer 类(位于 `src/main/java/com/example/accessingdatajpa/Customer.java` 中)

package com.example.accessingdatajpa;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class Customer {

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

  protected Customer() {}

  public Customer(String firstName, String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  @Override
  public String toString() {
    return String.format(
        "Customer[id=%d, firstName='%s', lastName='%s']",
        id, firstName, lastName);
  }

  public Long getId() {
    return id;
  }

  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }
}

这里有一个 `Customer` 类,它具有三个属性:`id`、`firstName` 和 `lastName`。您还有两个构造函数。默认构造函数仅为了 JPA 的目的而存在。您不会直接使用它,因此它被指定为 `protected`。另一个构造函数是您用来创建要保存到数据库的 `Customer` 实例的构造函数。

`Customer` 类用 `@Entity` 进行了注释,表明它是一个 JPA 实体。(因为不存在 `@Table` 注释,所以假设此实体映射到名为 `Customer` 的表。)

`Customer` 对象的 `id` 属性用 `@Id` 进行了注释,以便 JPA 将其识别为对象的 ID。`id` 属性也用 `@GeneratedValue` 进行了注释,以指示应自动生成 ID。

其他两个属性 `firstName` 和 `lastName` 没有注释。假设它们映射到与属性名称相同的列。

方便的 `toString()` 方法打印出客户的属性。

创建简单的查询

Spring Data JPA 专注于使用 JPA 将数据存储到关系数据库中。其最引人注目的功能是能够在运行时从存储库接口自动创建存储库实现。

要了解其工作原理,请创建一个与 `Customer` 实体一起使用的存储库接口,如下面的清单(位于 `src/main/java/com/example/accessingdatajpa/CustomerRepository.java` 中)所示

package com.example.accessingdatajpa;

import java.util.List;

import org.springframework.data.repository.CrudRepository;

public interface CustomerRepository extends CrudRepository<Customer, Long> {

  List<Customer> findByLastName(String lastName);

  Customer findById(long id);
}

`CustomerRepository` 扩展了 `CrudRepository` 接口。它使用的实体和 ID 的类型(`Customer` 和 `Long`)在 `CrudRepository` 的泛型参数中指定。通过扩展 `CrudRepository`,`CustomerRepository` 继承了几个用于处理 `Customer` 持久化的方法,包括用于保存、删除和查找 `Customer` 实体的方法。

Spring Data JPA 还允许您通过声明其方法签名来定义其他查询方法。例如,`CustomerRepository` 包含 `findByLastName()` 方法。

在典型的 Java 应用程序中,您可能希望编写一个实现 `CustomerRepository` 的类。但是,这就是 Spring Data JPA 如此强大的原因:您无需编写存储库接口的实现。Spring Data JPA 在您运行应用程序时创建实现。

现在您可以连接此示例并查看其外观!

创建应用程序类

Spring Initializr 为应用程序创建了一个简单的类。以下清单显示了 Initializr 为此示例创建的类(位于 `src/main/java/com/example/accessingdatajpa/AccessingDataJpaApplication.java` 中)

package com.example.accessingdatajpa;

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

@SpringBootApplication
public class AccessingDataJpaApplication {

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

}

`@SpringBootApplication` 是一个方便的注释,它添加了以下所有内容:

  • `@Configuration`:将类标记为应用程序上下文的 bean 定义的来源。

  • `@EnableAutoConfiguration`:告诉 Spring Boot 开始根据类路径设置、其他 bean 和各种属性设置添加 bean。例如,如果 `spring-webmvc` 位于类路径中,则此注释将应用程序标记为 Web 应用程序并激活关键行为,例如设置 `DispatcherServlet`。

  • `@ComponentScan`:告诉 Spring 在 `com/example` 包中查找其他组件、配置和服务,使其能够找到控制器。

`main()` 方法使用 Spring Boot 的 `SpringApplication.run()` 方法启动应用程序。您是否注意到没有一行 XML?也没有 `web.xml` 文件。此 Web 应用程序是 100% 纯 Java,您无需处理任何管道或基础设施的配置。

现在您需要修改 Initializr 为您创建的简单类。要获得输出(在此示例中为控制台输出),您需要设置一个日志记录器。然后,您需要设置一些数据并使用它来生成输出。以下清单显示了已完成的 `AccessingDataJpaApplication` 类(位于 `src/main/java/com/example/accessingdatajpa/AccessingDataJpaApplication.java` 中)

package com.example.accessingdatajpa;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class AccessingDataJpaApplication {

  private static final Logger log = LoggerFactory.getLogger(AccessingDataJpaApplication.class);

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

  @Bean
  public CommandLineRunner demo(CustomerRepository repository) {
    return (args) -> {
      // save a few customers
      repository.save(new Customer("Jack", "Bauer"));
      repository.save(new Customer("Chloe", "O'Brian"));
      repository.save(new Customer("Kim", "Bauer"));
      repository.save(new Customer("David", "Palmer"));
      repository.save(new Customer("Michelle", "Dessler"));

      // fetch all customers
      log.info("Customers found with findAll():");
      log.info("-------------------------------");
      repository.findAll().forEach(customer -> {
        log.info(customer.toString());
      });
      log.info("");

      // fetch an individual customer by ID
      Customer customer = repository.findById(1L);
      log.info("Customer found with findById(1L):");
      log.info("--------------------------------");
      log.info(customer.toString());
      log.info("");

      // fetch customers by last name
      log.info("Customer found with findByLastName('Bauer'):");
      log.info("--------------------------------------------");
      repository.findByLastName("Bauer").forEach(bauer -> {
        log.info(bauer.toString());
      });
      log.info("");
    };
  }

}

`AccessingDataJpaApplication` 类包含一个 `demo()` 方法,该方法对 `CustomerRepository` 进行了一些测试。首先,它从 Spring 应用程序上下文获取 `CustomerRepository`。然后,它保存一些 `Customer` 对象,演示 `save()` 方法并设置一些要使用的数据。接下来,它调用 `findAll()` 从数据库中获取所有 `Customer` 对象。然后,它调用 `findById()` 通过其 ID 获取单个 `Customer`。最后,它调用 `findByLastName()` 查找所有姓氏为“Bauer”的客户。`demo()` 方法返回一个 `CommandLineRunner` bean,该 bean 在应用程序启动时自动运行代码。

默认情况下,Spring Boot 启用 JPA 存储库支持,并在 `@SpringBootApplication` 所在的包(及其子包)中查找。如果您的配置在不可见的包中具有 JPA 存储库接口定义,则可以使用 `@EnableJpaRepositories` 及其类型安全的 `basePackageClasses=MyRepository.class` 参数指出备用包。

构建可执行 JAR

您可以使用 Gradle 或 Maven 从命令行运行应用程序。您还可以构建一个包含所有必要依赖项、类和资源的单个可执行 JAR 文件,然后运行该文件。构建可执行 jar 使得在整个开发生命周期中、跨不同环境等轻松地交付、版本化和部署服务作为应用程序。

如果您使用 Gradle,则可以使用 `./gradlew bootRun` 运行应用程序。或者,您可以使用 `./gradlew build` 构建 JAR 文件,然后运行 JAR 文件,如下所示:

java -jar build/libs/gs-accessing-data-jpa-0.1.0.jar

如果您使用 Maven,则可以使用 `./mvnw spring-boot:run` 运行应用程序。或者,您可以使用 `./mvnw clean package` 构建 JAR 文件,然后运行 JAR 文件,如下所示:

java -jar target/gs-accessing-data-jpa-0.1.0.jar
此处描述的步骤将创建一个可运行的 JAR。您还可以构建经典的 WAR 文件

运行应用程序时,您应该看到类似于以下内容的输出:

== Customers found with findAll():
Customer[id=1, firstName='Jack', lastName='Bauer']
Customer[id=2, firstName='Chloe', lastName='O'Brian']
Customer[id=3, firstName='Kim', lastName='Bauer']
Customer[id=4, firstName='David', lastName='Palmer']
Customer[id=5, firstName='Michelle', lastName='Dessler']

== Customer found with findById(1L):
Customer[id=1, firstName='Jack', lastName='Bauer']

== Customer found with findByLastName('Bauer'):
Customer[id=1, firstName='Jack', lastName='Bauer']
Customer[id=3, firstName='Kim', lastName='Bauer']

总结

恭喜!您已经编写了一个简单的应用程序,该应用程序使用 Spring Data JPA 将对象保存到数据库并从数据库中获取对象,而无需编写具体的存储库实现。

如果您想轻松地使用基于超媒体的 RESTful 前端公开 JPA 存储库,您可能想阅读使用 REST 访问 JPA 数据

另请参阅

以下指南也可能对您有所帮助:

想要编写新的指南或为现有指南做出贡献?请查看我们的贡献指南

所有指南均使用代码的 ASLv2 许可证和署名-非衍生作品创作共用许可证发布。

获取代码

免费

在云中工作

在 Spring Academy 的云端完成本指南。

转至 Spring Academy