3.使用 JDBC 进行数据访问

下表中概述的操作序列可能最好地显示了 Spring Framework JDBC 抽象提供的值。该表显示了 Spring 负责哪些操作,哪些操作是您的责任。

表 4. Spring JDBC-谁做什么?

Action Spring You
定义连接参数。 X
打开连接。 X
指定 SQL 语句。 X
声明参数并提供参数值 X
准备并执行该语句。 X
设置循环以遍历结果(如果有)。 X
进行每次迭代的工作。 X
处理任何异常。 X
Handle transactions. X
关闭连接,语句和结果集。 X

 

3.1. 选择一种用于 JDBC 数据库访问的方法

您可以选择几种方法来构成 JDBC 数据库访问的基础。除了JdbcTemplate的三种风格之外,新的SimpleJdbcInsertSimpleJdbcCall方法还优化了数据库元数据,并且 RDBMS Object 样式采用了一种更加面向对象的方法,类似于 JDO Query 设计。一旦开始使用这些方法之一,您仍然可以混合搭配以包含来自其他方法的功能。所有方法都需要兼容 JDBC 2.0 的驱动程序,某些高级功能需要 JDBC 3.0 驱动程序。

  • JdbcTemplate是经典且最受欢迎的 Spring JDBC 方法。这种“最低级别”的方法以及所有其他方法都在幕后使用了 JdbcTemplate。

  • NamedParameterJdbcTemplate包装JdbcTemplate以提供命名参数,而不是传统的 JDBC ?占位符。当您有多个 SQL 语句参数时,此方法可提供更好的文档编制和易用性。

  • SimpleJdbcInsertSimpleJdbcCall优化数据库元数据以限制必要的配置量。这种方法简化了编码,因此您只需要提供表或过程的名称,并提供与列名称匹配的参数映射即可。仅当数据库提供足够的元数据时,此方法才有效。如果数据库不提供此元数据,则必须提供参数的显式配置。

  • RDBMS 对象(包括MappingSqlQuerySqlUpdateStoredProcedure)要求您在初始化数据访问层期间创建可重用且线程安全的对象。此方法以 JDO Query 为模型,其中您定义查询字符串,声明参数并编译查询。完成后,可以使用各种参数值多次调用 execute 方法。

3.2. 包层次结构

Spring 框架的 JDBC 抽象框架由四个不同的软件包组成:

coreorg.springframework.jdbc.core软件包包含JdbcTemplate类及其各种回调接口,以及各种相关类。名为org.springframework.jdbc.core.simple的子包包含SimpleJdbcInsertSimpleJdbcCall类。另一个名为org.springframework.jdbc.core.namedparam的子程序包包含NamedParameterJdbcTemplate类和相关的支持类。

datasourceorg.springframework.jdbc.datasource软件包包含一个 Util 类,用于方便的DataSource访问和各种简单的DataSource实现,可用于在 Java EE 容器之外测试和运行未修改的 JDBC 代码。名为org.springfamework.jdbc.datasource.embedded的子程序包支持使用 Java 数据库引擎(例如 HSQL,H2 和 Derby)创建嵌入式数据库。

objectorg.springframework.jdbc.object软件包包含一些类,这些类将 RDBMS 查询,更新和存储过程表示为线程安全的可重用对象。参见将 JDBC 操作建模为 Java 对象。尽管查询返回的对象自然会与数据库断开连接,但此方法由 JDO 建模。较高级别的 JDBC 抽象取决于org.springframework.jdbc.core包中的较低级别的抽象。

supportorg.springframework.jdbc.support包提供SQLException转换功能和一些 Util 类。 JDBC 处理期间引发的异常将转换为org.springframework.dao包中定义的异常。这意味着使用 Spring JDBC 抽象层的代码不需要实现 JDBC 或 RDBMS 特定的错误处理。所有翻译的异常均未选中,这使您可以选择捕获可从中恢复的异常,同时将其他异常传播到调用方。

3.3. 使用 JDBC 核心类控制基本 JDBC 处理和错误处理

3.3.1. 使用 JdbcTemplate

  • 运行 SQL 查询

  • 更新语句和存储过程调用

  • ResultSet个实例执行迭代并提取返回的参数值。

  • 捕获 JDBC 异常,并将其转换为org.springframework.dao包中定义的通用,信息量更大的异常层次结构。

在代码中使用JdbcTemplate时,只需实现回调接口,即可为它们明确定义 Contract。给定JdbcTemplate类提供的ConnectionPreparedStatementCreator回调接口将创建一条准备好的语句,提供 SQL 和任何必要的参数。 CallableStatementCreator接口(创建可调用语句)也是如此。 RowCallbackHandler接口从ResultSet的每一行提取值。

您可以通过直接实例化DataSource引用在 DAO 实现中使用JdbcTemplate,也可以在 Spring IoC 容器中对其进行配置,并将其作为 Bean 引用提供给 DAO。

Note

DataSource应该始终配置为 Spring IoC 容器中的 bean。在第一种情况下,将 Bean 直接提供给服务。在第二种情况下,将其提供给准备好的模板。

此类发出的所有 SQL 都以DEBUG级别记录在与模板实例的标准类名相对应的类别下(通常为JdbcTemplate,但是如果使用JdbcTemplate类的自定义子类,则可能有所不同)。

 
Querying (SELECT)

以下查询获取关系中的行数:

int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class);

以下查询使用绑定变量:  

int countOfActorsNamedJoe = this.jdbcTemplate.queryForObject(
        "select count(*) from t_actor where first_name = ?", Integer.class, "Joe");

以下查询查找String:  

String lastName = this.jdbcTemplate.queryForObject(
        "select last_name from t_actor where id = ?",
        new Object[]{1212L}, String.class);

以下查询查找并填充单个域对象:  

Actor actor = this.jdbcTemplate.queryForObject(
        "select first_name, last_name from t_actor where id = ?",
        new Object[]{1212L},
        new RowMapper<Actor>() {
            public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
                Actor actor = new Actor();
                actor.setFirstName(rs.getString("first_name"));
                actor.setLastName(rs.getString("last_name"));
                return actor;
            }
        });

以下查询查找并填充许多域对象:  

List<Actor> actors = this.jdbcTemplate.query(
        "select first_name, last_name from t_actor",
        new RowMapper<Actor>() {
            public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
                Actor actor = new Actor();
                actor.setFirstName(rs.getString("first_name"));
                actor.setLastName(rs.getString("last_name"));
                return actor;
            }
        });

如果最后两个代码段确实存在于同一应用程序中,则删除两个RowMapper匿名内部类中存在的重复并将它们提取到单个类(通常是static嵌套类)中,然后可以引用该重复是有意义的。根据需要使用 DAO 方法。例如,最好编写以下代码片段,如下所示:  

public List<Actor> findAllActors() {
    return this.jdbcTemplate.query( "select first_name, last_name from t_actor", new ActorMapper());
}

private static final class ActorMapper implements RowMapper<Actor> {

    public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
        Actor actor = new Actor();
        actor.setFirstName(rs.getString("first_name"));
        actor.setLastName(rs.getString("last_name"));
        return actor;
    }
}

  

使用 JdbcTemplate 更新(INSERT,UPDATE 和 DELETE)

您可以使用update(..)方法执行插入,更新和删除操作。参数值通常作为变量参数提供,或者作为对象数组提供。

下面的示例插入一个新条目:

this.jdbcTemplate.update(
        "insert into t_actor (first_name, last_name) values (?, ?)",
        "Leonor", "Watling");

以下示例更新现有条目:

this.jdbcTemplate.update(
        "update t_actor set last_name = ? where id = ?",
        "Banjo", 5276L);

下面的示例删除一个条目:  

this.jdbcTemplate.update(
        "delete from actor where id = ?",
        Long.valueOf(actorId));
其他 JdbcTemplate 操作

您可以使用execute(..)方法来运行任意 SQL。因此,该方法通常用于 DDL 语句。带有回调接口,绑定变量数组等的变体极大地超载了它。以下示例创建一个表:  

this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");

下面的示例调用一个存储过程:  

this.jdbcTemplate.update(
        "call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
        Long.valueOf(unionId));

 

JdbcTemplate类的实例一旦配置便是线程安全的。这很重要,因为这意味着您可以配置JdbcTemplate的单个实例,然后将该共享引用安全地注入到多个 DAO(或存储库)中。 JdbcTemplate是有状态的,因为它维护对DataSource的引用,但是此状态不是会话状态。

使用JdbcTemplate类(和关联的NamedParameterJdbcTemplate类)的常见做法是在 Spring 配置文件中配置DataSource,然后将共享的DataSource bean 依赖注入到 DAO 类中。在DataSource的设置器中创建JdbcTemplate

这将导致类似于以下内容的 DAO:

public class JdbcCorporateEventDao implements CorporateEventDao {

    private JdbcTemplate jdbcTemplate;

    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    // JDBC-backed implementations of the methods on the CorporateEventDao follow...
}

以下示例显示了相应的 XML 配置:  

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="corporateEventDao" class="com.example.JdbcCorporateEventDao">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <context:property-placeholder location="jdbc.properties"/>

</beans>

 

显式配置的替代方法是使用组件扫描和注解支持进行依赖项注入。在这种情况下,可以用@Repository注解该类(这使其成为组件扫描的候选对象),并用@Autowired注解DataSource setter 方法。以下示例显示了如何执行此操作:  

@Repository (1)
public class JdbcCorporateEventDao implements CorporateEventDao {

    private JdbcTemplate jdbcTemplate;

    @Autowired (2)
    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource); (3)
    }

    // JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
  • (1) 用@Repository注解类。
  • (2) 用@Autowired注解DataSource setter 方法。
  • (3) 用DataSource创建一个新的JdbcTemplate

以下示例显示了相应的 XML 配置: 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- Scans within the base package of the application for @Component classes to configure as beans -->
    <context:component-scan base-package="org.springframework.docs.test" />

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <context:property-placeholder location="jdbc.properties"/>

</beans>

如果使用 Spring 的JdbcDaoSupport类,并且各种 JDBC 支持的 DAO 类都从该类扩展,则您的子类将从JdbcDaoSupport类继承setDataSource(..)方法。您可以选择是否从此类继承。提供JdbcDaoSupport类只是为了方便。

无论您选择使用(或不使用)以上哪种模板初始化样式,都无需在每次运行 SQL 时都创建一个新的JdbcTemplate类实例。配置完成后,JdbcTemplate实例是线程安全的。如果您的应用程序访问多个数据库,则可能需要多个JdbcTemplate实例,这需要多个DataSources实例,然后需要多个不同配置的JdbcTemplate实例。

  

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

 

 

 

3.使用 JDBC 进行数据访问

上一篇:mysql - select + 函数操作


下一篇:JDBC连接MySQL报错Unknown system variable 'query_cache_size'