Skip to content

Latest commit

 

History

History
225 lines (166 loc) · 9.82 KB

1521-jdbctemplate.md

File metadata and controls

225 lines (166 loc) · 9.82 KB

15.2.1JdbcTemplate

JdbcTemplate是JDBC core包里面的核心类。它封装了对资源的创建和释放,可以帮你避免忘记关闭连接等常见错误。它也包含了核心JDBC工作流的一些基础工作、例如执行和声明语句,而把SQL语句的生成以及查询结果的提取工作留给应用代码。JdbcTemplate执行查询、更新SQL语句和调用存储过程,运行ResultSet迭代和抽取返回参数值。它也可以捕获JDBC异常并把它们转换成更加通用、解释性更强的异常层次结构、这些异常都定义在org.springframework.dao包里面。

当你在代码中使用了JdbcTemplate类,你只需要实现回调接口。PreparedStatementCreator回调接口通过传入的Connection类(该类包含SQL和任何必要的参数)创建已声明的语句。CallableStatementCreator也提供类似的方式、该接口用于创建回调语句。RowCallbackHandler用于获取结果集每一行的值。

可以在DAO实现类中通过传入DataSource引用来完成JdbcTemplate的初始化;也可以在Spring IOC容器里面配置、作为DAO bean的依赖Bean配置。

备注:DataSource最好在Spring IOC容器里作为Bean配置起来。在上面第一种情况下,DataSourcebean直接传给相关的服务;第二种情况下DataSource bean传递给JdbcTemplate bean。

JdbcTemplate中使用的所有SQL以DEBUG级别记入日志(一般情况下日志的归类是JdbcTemplate对应的全限定类名,不过如果需要对JdbcTemplate进行定制的话,可能是它的子类名)

Examples of JdbcTemplate class usage

这一节提供了JdbcTemplate类的一些使用例子。这些例子没有囊括JdbcTemplate可提供的所有功能;全部功能和用法请详见相关的javadocs.

查询(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匿名类中重复的代码抽取到单独的类中(通常是一个静态类),方便被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实现增删改

你可以使用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))");

下面的例子调用一段简单的存储过程。更复杂的存储过程支持文档covered later

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

JdbcTemplate 最佳实践

JdbcTemplate实例一旦配置之后是线程安全的。这点很重要因为这样你就能够配置JdbcTemplate的单例,然后安全的将其注入到多个DAO中(或者repositories)。JdbcTemplate是有状态的,内部存在对DataSource的引用,但是这种状态不是会话状态。

使用JdbcTemplate类的常用做法是在你的Spring配置文件里配置好一个DataSource,然后将其依赖注入进你的DAO类中(NamedParameterJdbcTemplate也是如此)。JdbcTemplateDataSource的Setter方法中被创建。就像如下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 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>

另一种替代显式配置的方式是使用component-scanning和注解注入。在这个场景下需要添加@Repository注解(添加这个注解可以被component-scanning扫描到),同时在DataSource的Setter方法上添加@Autowired注解:

@Repository
public class JdbcCorporateEventDao implements CorporateEventDao {

    private JdbcTemplate jdbcTemplate;

    @Autowired
    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">

    <!-- 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,同时对应多个JdbcTemplates配置。