19.1 Introduction to Spring Framework JDBC
Spring Framework JDBC抽象提供的增值功能可能最好由下表中列出的一系列操作来显示。该表显示了Spring将处理哪些操作,以及哪些操作是应用程序开发人员的职责。
Table 19.1. Spring JDBC - who does what?
Action | Spring | You |
---|---|---|
Define connection parameters.(定义连接参数。) |
X |
|
Open the connection.(打开连接) |
X |
|
Specify the SQL statement.(指定SQL语句。) |
X |
|
Declare parameters and provide parameter values(声明参数并提供参数值) |
X |
|
Prepare and execute the statement.(准备并执行语句。) |
X |
|
Set up the loop to iterate through the results (if any).(设置循环来迭代结果(如果有的话)。) |
X |
|
Do the work for each iteration.(为每个迭代执行工作。) |
X |
|
Process any exception.(处理任何异常。) |
X |
|
Handle transactions.(处理事物) |
X |
|
Close the connection, statement and resultset.(关闭连接、语句和resultset。) |
X |
Spring框架处理了所有底层细节,这些细节使JDBC成为一个乏味的API。
19.1.1 Choosing an approach for JDBC database access 选择JDBC数据库访问方法
您可以选择几种方法来形成JDBC数据库访问的基础。除了JdbcTemplate的三种风格之外,一种新的SimpleJdbcInsert和SimplejdbcCall方法优化了数据库元数据,RDBMS对象样式采用了一种更面向对象的方法,类似于JDO查询设计。一旦您开始使用这些方法中的一种,您仍然可以混合和匹配以包含来自不同方法的特性。所有方法都需要兼容JDBC 2.0的驱动程序,一些高级特性需要JDBC 3.0驱动程序。
- JdbcTemplate是经典的Spring JDBC方法,也是最流行的。这种“最低级别”的方法和其他所有方法都在幕后使用JdbcTemplate。
- NamedParameterJdbcTemplate包装了一个JdbcTemplate来提供命名参数,而不是传统的JDBC“?”占位符。当SQL语句有多个参数时,这种方法提供了更好的文档和易用性。
- SimpleJdbcInsert和SimpleJdbcCall优化数据库元数据,以限制必要的配置数量。这种方法简化了编码,因此您只需要提供表或过程的名称,并提供与列名匹配的参数映射。这只有在数据库提供足够的元数据时才有效。如果数据库不提供此元数据,则必须提供参数的显式配置。
- RDBMS对象包括MappingSqlQuery、SqlUpdate和StoredProcedure,它们要求您在初始化数据访问层时创建可重用和线程安全的对象。这种方法是在JDO查询之后建模的,在JDO查询中定义查询字符串、声明参数和编译查询。这样做之后,可以多次调用execute方法,并传入各种参数值。
19.1.2 Package hierarchy 包的层次结构
Spring框架的JDBC抽象框架由四个不同的包组成,即core, datasource, object, and support。
org.springframework.jdbc.core包含JdbcTemplate类及其各种回调接口,以及各种相关类。
子包org.springframework.jdbc.core.simple包含SimpleJdbcInsert和SimpleJdbcCall类。
另一个名为org.springframework.jdbc.core.namedparam的子包包含NamedParameterJdbcTemplate类和相关的支持类。
org.springframework.jdbc.datasource包包含一个实用程序类,用于方便地访问数据源,以及各种简单的数据源实现,这些实现可用于在Java EE容器之外测试和运行未经修改的JDBC代码。
名为org.springfamework.jdbc.datasource.embedded的子包提供嵌入式支持使用Java数据库引擎(如HSQL、H2和Derby)创建嵌入式数据库。
org.springframework.jdbc.object包包含一些类,它们将RDBMS查询、更新和存储过程表示为线程安全的、可重用的对象。这种方法由JDO建模,尽管查询返回的对象自然与数据库断开连接。这种更高层次的JDBC抽象依赖于org.springframework.jdbc.core包中的低层抽象。
org.springframework.jdbc.support包提供了SQLException翻译功能和一些实用程序类。JDBC处理过程中抛出的异常被转换为org.springframework.dao包中定义的异常。这意味着使用Spring JDBC抽象层的代码不需要实现JDBC或特定于rdbms的错误处理。所有已翻译的异常都是未检查的,这使您可以选择捕获可以从中恢复的异常,同时允许将其他异常传播到调用方。
19.2 Using the JDBC core classes to control basic JDBC processing and error handling 使用JDBC核心类来控制基本的JDBC处理和错误处理
19.2.1 JdbcTemplate
JdbcTemplate类是JDBC核心包中的中心类。它处理资源的创建和释放,帮助您避免常见错误,例如忘记关闭连接。它执行核心JDBC工作流的基本任务,如语句创建和执行,留下应用程序代码来提供SQL和提取结果。JdbcTemplate类执行SQL查询、更新语句和存储过程调用,执行结果集的迭代和返回参数值的提取。它还捕获JDBC异常并将其转换为org.springframework.dao包中定义的通用的、更有信息的异常层次结构。
当您在代码中使用JdbcTemplate时,您只需要实现回调接口,为它们提供一个明确定义的契约。PreparedStatementCreator回调接口为这个类提供的Connection
创建一个准备好的语句,提供SQL和任何必要的参数。对于创建可调用语句的CallableStatementCreator接口也是如此。RowCallbackHandler接口从结果集的每一行提取值。
JdbcTemplate可以在DAO实现中使用,可以通过直接实例化DataSource
引用,也可以在Spring IoC容器中配置,并作为bean引用提供给DAOs。
DataSource 应该始终配置为Spring IoC容器中的bean。在第一种情况下,bean是直接提供给服务的;在第二种情况下,它提供给准备好的模板。
这个类发出的所有SQL都在DEBUG级别的类别下进行日志记录,该类别对应于模板实例的完全限定类名(通常是JdbcTemplate,但如果使用JdbcTemplate类的自定义子类,则可能有所不同)。
Examples of JdbcTemplate class usage JdbcTemplate类使用示例
本节提供了一些使用JdbcTemplate类的示例。这些示例并不是JdbcTemplate公开的所有功能的详尽列表;有关这一点,请参见附带的javadoc。
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 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;
}
}
Updating (INSERT/UPDATE/DELETE) with JdbcTemplate
使用update(..)方法执行插入、更新和删除操作。参数值通常以var args或对象数组的形式提供。
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));
Other JdbcTemplate operations
您可以使用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 best practices JdbcTemplate最佳实践
JdbcTemplate类的实例在配置之后是线程安全的。这很重要,因为这意味着您可以配置JdbcTemplate的一个实例,然后安全地将这个共享引用注入多个DAOs(或存储库)。JdbcTemplate是有状态的,因为它维护对数据源的引用,但是这种状态不是会话状态。
在使用JdbcTemplate类(以及相关的NamedParameterJdbcTemplate类)时,一个常见的做法是在Spring配置文件中配置数据源,然后将共享数据源bean注入到DAO类中;JdbcTemplate是在数据源的setter中创建的。这导致DAOs在某种程度上看起来像下面这样:
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