21.2.4 持久化数据

在 Spring 应用中,有多种使用数据库的方式。我们可以使用 JPA 或 Hibernate 将对象映射为关系型数据库中的表和列。或者,我们干脆放弃关系型数据库,使用其他类型的数据库,如 Mongo 或 Neo4j。

对于 Contacts 应用来说,关系型数据库是不错的选择。我们将会使用 H2 数据库和 JDBC(使用 Spring 的 JdbcTemplate),让这个过程尽可能地简单。

选择这种方案就需要在构建中添加一些依赖。JDBC Starter 依赖会将 Spring JdbcTemplate 需要的所有内容都引入进来。不过,要结合使用 H2 数据库的话,我们还需要添加 H2 依赖。如果使用 Gradle 的话,在 dependencies 代码块添加如下两行代码就能完成这项任务:

compile("org.springframework.boot:spring-boot-starter-jdbc")
compile("com.h2database:h2")

如果使用 Maven 构建的话,我们需要如下的两个代码块:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
  <groupId>com.h2database</groupId>
  <artifactId>h2</artifactId>
</dependency>

将这两项依赖添加到构建之中后,我们就可以编写 Repository 类了。如下程序清单中的 ContactRepository 将会使用注入的 JdbcTemplate 实现在数据库中读取和写入 Contact 对象。

程序清单 21.6 ContactRepository 能够从数据库中存取 Contact
package contacts;

import java.util.List;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

@Repository
public class ContactRepository {

  private JdbcTemplate jdbc;
  
  @Autowired
  public ContactRepository(JdbcTemplate jdbc) {
    this.jdbc = jdbc;
  }

  public List<Contact> findAll() {
    return jdbc.query(
        "select id, firstName, lastName, phoneNumber, emailAddress " +
        "from contacts order by lastName",
        new RowMapper<Contact>() {
          public Contact mapRow(ResultSet rs, int rowNum) throws SQLException {
            Contact contact = new Contact();
            contact.setId(rs.getLong(1));
            contact.setFirstName(rs.getString(2));
            contact.setLastName(rs.getString(3));
            contact.setPhoneNumber(rs.getString(4));
            contact.setEmailAddress(rs.getString(5));
            return contact;
          }
        }
      );
  }

  public void save(Contact contact) {
    jdbc.update(
        "insert into contacts " +
        "(firstName, lastName, phoneNumber, emailAddress) " +
        "values (?, ?, ?, ?)",
        contact.getFirstName(), contact.getLastName(),
        contact.getPhoneNumber(), contact.getEmailAddress());
  }
  
}

与 ContactController 类似,这个 Repository 类非常简单。它与传统 Spring 应用中的 Repository 类并没有什么差别。从实现中,根本无法看出它要用于 Spring Boot 的应用程序中。findAll() 方法使用注入的 JdbcTemplate 从数据库中获取 Contact 对象,save() 方法使用注入的 JdbcTemplate 保存新的 Contact 对象。因为 ContactRepository 使用了 @Repository 注解,因此在组件扫描的时候,它会被发现并创建为 Spring 应用上下文中的 bean。

但是,JdbcTemplate 呢?我们难道不需要在 Spring 应用上下文中声明 JdbcTemplate bean 吗?为了声明它,我们是不是还要声明一个 H2 DataSource?

对这两个问题的简短问答就是 “不需要”。当 Spring Boot 探测到 Spring 的 JDBC 模块和 H2 在类路径下的时候,自动配置就会发挥作用,将会自动配置 JdbcTemplate bean 和 H2DataSource bean。Spring Boot 再一次为我们处理了所有的 Spring 配置。

那数据库模式该怎么处理呢?我们必须要自己来定义创建 contacts 表的模式,对不对?

这绝对是正确的!Spring Boot 没有办法猜测 contacts 表会是什么样 子。所以,我们需要定义模式,如下所示:

create table contacts (
  id identity,
  firstName varchar(30) not null,
  lastName varchar(50) not null,
  phoneNumber varchar(13),
  emailAddress varchar(30)
);

现在,我们只需要有一种方式加载这个 “create table” 的 SQL 并将其在 H2 数据库中执行就可以了。幸好,Spring Boot 也涵盖了这项功能。如果我们将这个文件命名为 schema.sql 并将其放在类路径根下 (也就是 Maven 或 Gradle 项目的 “src/main/resources” 目录下),当应用启动的时候,就会找到这个文件并进项数据加载。

Last updated