Contents

Spring JDBC模板

1.概述

在本教程中,我们将介绍 Spring JDBC 模块的实际用例。

Spring JDBC 中的所有类都分为四个独立的包:

  • core — JDBC 的核心功能。该包下的一些重要类包括JdbcTemplateSimpleJdbcInsertSimpleJdbcCallNamedParameterJdbcTemplate
  • datasource — 用于访问数据源的实用程序类。它还具有用于在 Jakarta EE 容器之外测试 JDBC 代码的各种数据源实现。
  • object — 以面向对象的方式访问数据库。它允许运行查询并将结果作为业务对象返回。它还映射业务对象的列和属性之间的查询结果。
  • support — 支持coreobject包下的类,例如,提供SQLException转换功能

2.配置

让我们从数据源的一些简单配置开始。 我们将使用 MySQL 数据库:

@Configuration
@ComponentScan("com.blogdemo.jdbc")
public class SpringJdbcConfig {
    @Bean
    public DataSource mysqlDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/springjdbc");
        dataSource.setUsername("guest_user");
        dataSource.setPassword("guest_password");
        return dataSource;
    }
}

或者,我们也可以充分利用嵌入式数据库进行开发或测试。

这是一个创建 H2 嵌入式数据库实例并使用简单 SQL 脚本预填充它的快速配置:

@Bean
public DataSource dataSource() {
    return new EmbeddedDatabaseBuilder()
      .setType(EmbeddedDatabaseType.H2)
      .addScript("classpath:jdbc/schema.sql")
      .addScript("classpath:jdbc/test-data.sql").build();
}

最后,同样可以使用 XML 配置数据源来完成:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" 
  destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/springjdbc"/>
    <property name="username" value="guest_user"/>
    <property name="password" value="guest_password"/>
</bean>

3. JdbcTemplate和运行查询

3.1.基本查询

JDBC 模板是主要的 API,我们将通过它访问我们感兴趣的大部分功能:

  • 创建和关闭连接
  • 运行语句和存储过程调用
  • 遍历ResultSet并返回结果

首先,我们从一个简单的例子开始,看看JdbcTemplate能做什么:

int result = jdbcTemplate.queryForObject(
    "SELECT COUNT(*) FROM EMPLOYEE", Integer.class);

这是一个简单的插入:

public int addEmplyee(int id) {
    return jdbcTemplate.update(
      "INSERT INTO EMPLOYEE VALUES (?, ?, ?, ?)", id, "Bill", "Gates", "USA");
}

注意使用?提供参数的标准语法。

接下来,让我们看一下这种语法的替代方法。

3.2. 带有命名参数的查询

为了获得对命名参数的支持,我们将使用框架提供的另一个 JDBC 模板 — NamedParameterJdbcTemplate。 此外,它包装了JbdcTemplate并使用 ? 提供了传统语法的替代方案。指定参数。

在幕后,它将命名参数替换为 JDBC ? 占位符并委托给包装的JDCTemplate以运行查询:

SqlParameterSource namedParameters = new MapSqlParameterSource().addValue("id", 1);
return namedParameterJdbcTemplate.queryForObject(
  "SELECT FIRST_NAME FROM EMPLOYEE WHERE ID = :id", namedParameters, String.class);

请注意我们如何使用MapSqlParameterSource为命名参数提供值。 让我们看看使用 bean 的属性来确定命名参数:

Employee employee = new Employee();
employee.setFirstName("James");
String SELECT_BY_ID = "SELECT COUNT(*) FROM EMPLOYEE WHERE FIRST_NAME = :firstName";
SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(employee);
return namedParameterJdbcTemplate.queryForObject(
  SELECT_BY_ID, namedParameters, Integer.class);

请注意我们现在如何使用BeanPropertySqlParameterSource实现,而不是像以前那样手动指定命名参数。

3.3. 将查询结果映射到 Java 对象

另一个非常有用的特性是能够通过实现RowMapper接口将查询结果映射到 Java 对象。

例如,对于查询返回的每一行,Spring 使用行映射器来填充 java bean:

public class EmployeeRowMapper implements RowMapper<Employee> {
    @Override
    public Employee mapRow(ResultSet rs, int rowNum) throws SQLException {
        Employee employee = new Employee();
        employee.setId(rs.getInt("ID"));
        employee.setFirstName(rs.getString("FIRST_NAME"));
        employee.setLastName(rs.getString("LAST_NAME"));
        employee.setAddress(rs.getString("ADDRESS"));
        return employee;
    }
}

随后,我们现在可以将行映射器传递给查询 API 并获得完全填充的 Java 对象:

String query = "SELECT * FROM EMPLOYEE WHERE ID = ?";
Employee employee = jdbcTemplate.queryForObject(
  query, new Object[] { id }, new EmployeeRowMapper());

4.异常翻译

Spring 自带开箱即用的数据异常层次结构——以DataAccessException作为根异常——并将所有底层原始异常转换为它。

因此,我们通过不处理低级持久性异常来保持理智。我们还受益于 Spring 将低级异常包装在DataAccessException或其子类之一中。

这也使异常处理机制独立于我们正在使用的底层数据库。

除了默认的SQLErrorCodeSQLExceptionTranslator之外,我们还可以提供我们自己的SQLExceptionTranslator实现。

下面是一个自定义实现的快速示例——在存在重复键违规时自定义错误消息,这会在使用 H2 时导致错误代码 23505

public class CustomSQLErrorCodeTranslator extends SQLErrorCodeSQLExceptionTranslator {
    @Override
    protected DataAccessException
      customTranslate(String task, String sql, SQLException sqlException) {
        if (sqlException.getErrorCode() == 23505) {
          return new DuplicateKeyException(
            "Custom Exception translator - Integrity constraint violation.", sqlException);
        }
        return null;
    }
}

要使用这个自定义异常转换器,我们需要通过调用setExceptionTranslator()方法将其传递给JdbcTemplate

CustomSQLErrorCodeTranslator customSQLErrorCodeTranslator = 
  new CustomSQLErrorCodeTranslator();
jdbcTemplate.setExceptionTranslator(customSQLErrorCodeTranslator);

5. 使用 SimpleJdbc 类的 JDBC 操作

SimpleJdbc类提供了一种简单的方法来配置和运行 SQL 语句。这些类使用数据库元数据来构建基本查询。因此,SimpleJdbcInsertSimpleJdbcCall类提供了一种更简单的方法来运行插入和存储过程调用。

5.1. SimpleJdbcInsert

让我们看一下以最少的配置运行简单的插入语句。 **INSERT 语句是根据SimpleJdbcInsert的配置生成的。**我们只需要提供表名、列名和值。 首先,让我们创建一个 SimpleJdbcInsert

SimpleJdbcInsert simpleJdbcInsert = 
  new SimpleJdbcInsert(dataSource).withTableName("EMPLOYEE");

接下来,让我们提供列名称和值,然后运行操作:

public int addEmplyee(Employee emp) {
    Map<String, Object> parameters = new HashMap<String, Object>();
    parameters.put("ID", emp.getId());
    parameters.put("FIRST_NAME", emp.getFirstName());
    parameters.put("LAST_NAME", emp.getLastName());
    parameters.put("ADDRESS", emp.getAddress());
    return simpleJdbcInsert.execute(parameters);
}

此外,我们可以使用executeAndReturnKey() API 来允许数据库生成主键。我们还需要配置实际的自动生成列:

SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(dataSource)
                                        .withTableName("EMPLOYEE")
                                        .usingGeneratedKeyColumns("ID");
Number id = simpleJdbcInsert.executeAndReturnKey(parameters);
System.out.println("Generated id - " + id.longValue());

最后,我们还可以使用BeanPropertySqlParameterSourceMapSqlParameterSource传入这些数据。

5.2. 使用SimpleJdbcCall 的存储过程

让我们也看看正在运行的存储过程。

我们将使用SimpleJdbcCall抽象:

SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(dataSource)
                     .withProcedureName("READ_EMPLOYEE");

public Employee getEmployeeUsingSimpleJdbcCall(int id) {
    SqlParameterSource in = new MapSqlParameterSource().addValue("in_id", id);
    Map<String, Object> out = simpleJdbcCall.execute(in);
    Employee emp = new Employee();
    emp.setFirstName((String) out.get("FIRST_NAME"));
    emp.setLastName((String) out.get("LAST_NAME"));
    return emp;
}

6. 批量操作

另一个简单的用例是将多个操作批处理在一起。

6.1. 使用JdbcTemplate 的基本批处理操作

使用JdbcTemplate,可以通过batchUpdate() API运行批处理操作。

这里有趣的部分是简洁但非常有用的BatchPreparedStatementSetter实现:

public int[] batchUpdateUsingJdbcTemplate(List<Employee> employees) {
    return jdbcTemplate.batchUpdate("INSERT INTO EMPLOYEE VALUES (?, ?, ?, ?)",
        new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                ps.setInt(1, employees.get(i).getId());
                ps.setString(2, employees.get(i).getFirstName());
                ps.setString(3, employees.get(i).getLastName());
                ps.setString(4, employees.get(i).getAddress();
            }
            @Override
            public int getBatchSize() {
                return 50;
            }
        });
}

6.2. 使用NamedParameterJdbcTemplate 进行批量操作

我们还可以选择使用NamedParameterJdbcTemplatebatchUpdate() API 进行批处理操作。 此 API 比上一个 API 更简单。因此,不需要实现任何额外的接口来设置参数,因为它有一个内部准备好的语句设置器来设置参数值。

相反,参数值可以作为SqlParameterSource数组传递给*batchUpdate()*方法。

SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(employees.toArray());
int[] updateCounts = namedParameterJdbcTemplate.batchUpdate(
    "INSERT INTO EMPLOYEE VALUES (:id, :firstName, :lastName, :address)", batch);
return updateCounts;

7. Spring JDBC 与 Spring Boot

Spring Boot 提供了一个启动器spring-boot-starter-jdbc用于将 JDBC 与关系数据库一起使用。 与每个 Spring Boot 启动器一样,这个启动器可以帮助我们快速启动和运行我们的应用程序。

7.1. Maven 依赖

我们需要 spring-boot-starter-jdbc 依赖项作为主要依赖项。我们还需要我们将使用的数据库的依赖项。在我们的例子中,这是MySQL

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

7.2. 配置

Spring Boot 会自动为我们配置数据源。我们只需要在*properties *文件中提供属性:

spring.datasource.url=jdbc:mysql://localhost:3306/springjdbc
spring.datasource.username=guest_user
spring.datasource.password=guest_password

就是这样。只需执行这些配置,我们的应用程序就可以启动并运行。我们现在可以将它用于其他数据库操作。

我们在上一节中看到的标准 Spring 应用程序的显式配置现在包含在 Spring Boot 自动配置中。