Spring JDBC 抽象了JDBC API,下表标注X的表示在操作序列中,Spring和开发人员需要关注的操作
操作 | Spring | 开发人员 |
---|---|---|
定义连接串 | X | |
Open连接 | X | |
指定SQL语句 | X | |
定义参数和参数值 | X | |
prepare和执行语句 | X | |
循环处理结果集 | X | |
每行结果记录怎么处理 | X | |
处理异常 | X | |
处理事务 | X | |
关闭连接,statement,结果集 | X |
不同的JDBC 访问方式
Spring JDBC 提供了几种方法,以运用不同类与数据库的接口。除了JdbcTemplate之外,新的SimpleJdbcinsert 和SimpleJdbcCall两个类通过利用JDBC驱动提供的数据库元数据来简化JDBC操作,而RDBMS Object 样式采用了更类似于JDO Query 设计的面向对象的方法。
- JdbcTemplate
JdbcTemplate是最经典的Spring JDBC 方法。这是一种底层的方法,其他方法内部都借助于JdbcTemplate 来完成。
- NamedParameterJdbcTemplate
NamedParameterJdbcTemplate 封装了JdbcTemplate以提供命名参数,而不是使用传统的JDBC "?"占位符。当一个SQL 语句有多个参数时,这种方法则呈现出了更好的可良性和易用性。
- SimpleJdbclnsert 和SimpleJdbcCall
SimpleJdbcinsert 和SimpleJdbcCa ll 优化数据库元数据,以限制必要配置的数量。这种方法简化了编码,只需要提供表或存储过程的名称及与列名匹配的参数映射。这仅在数据库提供足够的元数据时有效。如果数据库不提供此元数据, 则必须提供参数的显式配置。
- RDBMS Object
RDBMS Object 包括MappingSqlQuery 、SqlUpdate 和StoredProcedure ,需要在数据访问层初始化期间建立可重用的且是线程安全的对象。此方法在JDO Query之后建模,可以在其中定义查询字符串、声明参数并编译查询。一旦这样做,执行方法可以多次调用传人的各种参数值。
JDBC包层次结构
Spring JDBC 由4个不同的包构成,分别为core 、datasource、object和support 。
-
core
:
org.springframework.jdbc.core
包包含JdbcTemplate和大量
callback 接口以及相关类。org.springframework.jdbc.core.simple
包含 SimpleJdbcInsert
和
SimpleJdbcCall
classes。 org.springframework.jdbc.core.namedparam
包含NamedParameterJdbcTemplate
以及相关支持类。
-
datasource
:
org.springframework.jdbc.datasource
包数据访问的实用工具类和多种DataSource的实现类,可以在Java EE 容器外测试JDBC代码。 org.springfamework.jdbc.datasource.embedded
子包提供了使用Java 数据库引擎( 如HSQL 、H2 和Derby )创建嵌入式数据库的支持
-
object
:
org.springframework.jdbc.object
为对象包,以面向对象的方式访问数据库。它允许执行查询并返回结果作为业务对象,可以在数据表的列和业务对象的属性之间映射查询结果。.
-
support
:
org.springframework.jdbc.support
为支持包,提供了SQLException 转换功能和一些实用工具类,均是core包和object包的支持类。.
JDBC Core类
JdbcTemplate
JdbcTemplate类是线程安全的。
JdbcTemplate它用于处理资源的创建和释放,可以避免开发人员常见的JDBC使用错误,如忘记关闭连接。它执行核心JDBC工作流的基本任务,如语句创建和执行,使应用程序代码提供SQL并提取结果。JdbcTemplate可以
- 执行Sql查询
- update语句以及存储过程调用
- 迭代ResultSet返回结果。
- 捕获异常并转换为DAO异常。
使用JdbcTemplate 时,只需要实现回调接口即可。PreparedStatementCreator 回调接口根据该类提供的Connection创建一个准备好的语句,提供SQL和任何必需的参数。CallableStatementCreator接口也是如此,该接口创建可调用语句。RowCallbackHandler接口从ResultSet的每一行提取值。
JdbcTemplate可以通过直接实例化DataSource引用在DAO实现中使用,或者在Spring IoC容器中配置并作为bean引用提供给DAO 。
DataSource 应始终在Spring IoC 容器中配置为一个bean 。在上述的第一种情况下,bean 直接提供给服务;在第二种情况下,它被提供给准备好的模板。
查询(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);
//查询和填充单个域对象,实现RowMapper类
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;
}
});//查询和填充多个域对象实现RowMapper类
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;
}
});
Updating (INSERT
, UPDATE
, and DELETE
)
//insert
this.jdbcTemplate.update(
"insert into t_actor (first_name, last_name) values (?, ?)",
"Leonor", "Watling");//update
this.jdbcTemplate.update(
"update t_actor set last_name = ? where id = ?",
"Banjo", 5276L);
其他操作
可以使用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));
NamedParameterJdbcTemplate
在经典的JDBC用法中,SQL参数是用占位轮“?”来表示的,这会受到位置的限制。这种方式的问题在于,一旦参数的顺序发生变化,就必须改变参数绑定。在Spring JDBC框架中,绑定SQL 参数的另一种选择是使用命名参数( named parameter ) 。
public int countOfActorsByFirstName(String firstName) {
String sql = "select count(*) from T_ACTOR where first_name = :first_name";
SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}// 基于Map的方式
public int countOfActorsByFirstName(String firstName) {
String sql = "select count(*) from T_ACTOR where first_name = :first_name";
Map<String, String> namedParameters = Collections.singletonMap("first_name", firstName);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}
与NamedParameterJdbcTemplate相关的一个很好的功能是SqlParameterSource接口,SqlParameterSource是NamedParameterJdbcTemplate 的命名参数值的来源。
MapSqlParameterSource类是一个非常简单的实现,它只是一个围绕java.util.Map的适配器,其中键是参数名称,值是参数值。
另一个SqlParameterSource 实现是BeanPropertySqlParameterSource类。这个类包装了一个任意的JavaBean ,并使用包装的JavaBean的属性作为命名参数值的来源。
public class Actor {
private Long id;
private String firstName;
private String lastName;
public String getFirstName() {
return this.firstName;
}
public String getLastName() {
return this.lastName;
}
public Long getId() {
return this.id;
}
}//属性作为参数源
public int countOfActors(Actor exampleActor) {
String sql = "select count(*) from T_ACTOR where first_name = :firstName and last_name = :lastName";
SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}
SQLExceptionTranslator
SQLExceptionTranslator是一个接口,其实现类可以用于SQLExceptions和Spring 自己的org.springframework.dao.DataAccessException 之间进行转换。
SQLErrorCodeSQLExceptionTranslator是SQLExceptionTranslator的默认实现。该实现使用特定的供应商代码,它比SQLState实现更精确。错误代码转换基于JavaBean 类型类SQLErrorCodes中保存的代码。该类由SQLErrorCodesFactory 创建和填充,顾名思义,它是一个根据名为sql-error-codes.xml的配置文件内容创建SQLErrorCodes 的工厂。该文件使用供应商代码填充,供应商代码从DatabaseMetaData获取DatabaseProductName。
public class CustomSQLErrorCodesTranslator extends SQLErrorCodeSQLExceptionTranslator {
protected DataAccessException customTranslate(String task, String sql, SQLException sqlex) {
// 对错误码:-12345特殊处理
if (sqlex.getErrorCode() == -12345) {
return new DeadlockLoserDataAccessException(task, sqlex);
}
return null;
}
}
检索自动生成的主键
update()方法支持检索由数据库生成的主键。这种支持是JDBC3.0标准的一部分。该方法将PreparedStatementCreator 作为其第一个参数,需要指定插入语句。另一个参数是KeyHolder,它包含从更新成功返回时生成的主键。
final String INSERT_SQL = "insert into my_test (name) values(?)";
final String name = "Rob";KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(
new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
PreparedStatement ps = connection.prepareStatement(INSERT_SQL, new String[] {"id"});
ps.setString(1, name);
return ps;
}
},
keyHolder);// keyHolder.getKey() now contains the generated key
数据库连接
DataSource
Spring通过一个DataSource(数据源)获得与数据库的连接。在使用Spring的JDBC时,可以从JNDI获取数据源,或者使用第三方提供的连接池实现来配置自己的数据源。流行的实现是Apache Jakarta Commons DBCP 和C3P0 。Spring 发行版本中的实现仅用于测试,并未提供连接池方案。所以在使用Spring的DriverManagerDataSource类时,仅用于测试目的,因为它不提供连接池的方案,在执行多个连接请求时性能较差。
- 代码方式:
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
dataSource.setUsername("sa");
dataSource.setPassword("");
- Spring方式:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<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"/>
- DBCP
<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"/>
- C3P0
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean><context:property-placeholder location="jdbc.properties"/>