11.2. 利用JDBC核心类控制JDBC的基本操作和错误处理
JdbcTemplate
是core包的核心类。它替我们完成了资源的创建以及释放工作,从而简化了我们对JDBC的使用。 它还可以帮助我们避免一些常见的错误,比如忘记关闭数据库连接。 JdbcTemplate将完成JDBC核心处理流程,比如SQL语句的创建、执行,而把SQL语句的生成以及查询结果的提取工作留给我们的应用代码。 它可以完成SQL查询、更新以及调用存储过程,可以对ResultSet
进行遍历并加以提取。 它还可以捕获JDBC异常并将其转换成org.springframework.dao
包中定义的,通用的,信息更丰富的异常。
使用JdbcTemplate进行编码只需要根据明确定义的一组契约来实现回调接口。 PreparedStatementCreator
回调接口通过给定的Connection
创建一个PreparedStatement,包含SQL和任何相关的参数。 CallableStatementCreateor
实现同样的处理,只不过它创建的是CallableStatement。 RowCallbackHandler
接口则从数据集的每一行中提取值。
我们可以在DAO实现类中通过传递一个DataSource
引用来完成JdbcTemplate的实例化,也可以在Spring的IoC容器中配置一个JdbcTemplate的bean并赋予DAO实现类作为一个实例。 需要注意的是DataSource
在Spring的IoC容器中总是配制成一个bean,第一种情况下,DataSource
bean将传递给service,第二种情况下DataSource
bean传递给JdbcTemplate bean。
最后,JdbcTemplate中使用的所有SQL将会以“DEBUG”级别记入日志(一般情况下日志的category是JdbcTemplate
相应的全限定类名,不过如果需要对JdbcTemplate
进行定制的话,可能是它的子类名)。
下面是一些使用JdbcTemplate
类的示例。(这些示例并不是完整展示所有的JdbcTemplate
所暴露出来的功能。请查看与之相关的Javadoc)。
一个简单的例子用于展示如何获取一个表中的所有行数。
int rowCount = this.jdbcTemplate.queryForInt("select count(0) from t_accrual");
一个简单的例子展示如何进行参数绑定。
int countOfActorsNamedJoe = this.jdbcTemplate.queryForInt(
"select count(0) from t_actors where first_name = ?", new Object[]{"Joe"});
查询一个String
。
String surname = (String) this.jdbcTemplate.queryForObject(
"select surname from t_actor where id = ?",
new Object[]{new Long(1212)}, String.class);
查询并将结果记录为一个简单的数据模型。
Actor actor = (Actor) this.jdbcTemplate.queryForObject(
"select first_name, surname from t_actor where id = ?",
new Object[]{new Long(1212)},
new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setFirstName(rs.getString("first_name"));
actor.setSurname(rs.getString("surname"));
return actor;
}
});
查询并组装多个数据模型。
Collection actors = this.jdbcTemplate.query(
"select first_name, surname from t_actor",
new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setFirstName(rs.getString("first_name"));
actor.setSurname(rs.getString("surname"));
return actor;
}
});
如果最后2个示例中的代码出现在同一段程序中,我们有必要去掉这些重复的RowMapper
匿名类代码,将这些代码抽取到一个单独的类中(通常是一个静态的内部类)。 这样,这个内部类就可以在DAO的方法中被共享。因而,最后2个示例写成如下的形式将更加好:
public Collection findAllActors() {
return this.jdbcTemplate.query( "select first_name, surname from t_actor", new ActorMapper());
}
private static final class ActorMapper implements RowMapper {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setFirstName(rs.getString("first_name"));
actor.setSurname(rs.getString("surname"));
return actor;
}
}
11.2.1.1.2. 更新(INSERT/UPDATE/DELETE)
this.jdbcTemplate.update(
"insert into t_actor (first_name, surname) values (?, ?)",
new Object[] {"Leonor", "Watling"});
this.jdbcTemplate.update(
"update t_actor set weapon = ? where id = ?",
new Object[] {"Banjo", new Long(5276)});
this.jdbcTemplate.update(
"delete from actor where id = ?",
new Object[] {new Long.valueOf(actorId)});
execute(..)
方法可以被用作执行任何类型的SQL,甚至是DDL语句。 这个方法的实现需要传入一个回调接口、需要绑定的参数数组等作为参数。
this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
调用一个简单的存储过程(更多复杂的存储过程支持请参见存储过程支持)。
this.jdbcTemplate.update(
"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
new Object[]{Long.valueOf(unionId)});
11.2.1.2. JdbcTemplate
的最佳实践
JdbcTemplate
类的实例是线程安全的实例。这一点非常重要,正因为如此,你可以配置一个简单的JdbcTemplate
实例,并将这个“共享的”、“安全的”实例注入到不同的DAO类中去。 另外, JdbcTemplate
是有状态的,因为他所维护的DataSource
实例是有状态的,但是这种状态是无法变化的。
使用JdbcTemplate
的一个常见的最佳实践(同时也是SimpleJdbcTemplate
和NamedParameterJdbcTemplate
类的最佳实践)就是在Spring配置文件中配置一个DataSource
实例,然后将这个共享的DataSource
实例助于到你的DAO中去。 而JdbcTemplate
的实例将在DataSource
的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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="corporateEventDao" class="com.example.JdbcCorporateEventDao">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- the DataSource
(parameterized for configuration via a PropertyPlaceHolderConfigurer
) -->
<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
<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>
</beans>
如果你使用Spring提供的JdbcDaoSupport
类,并且你的那些基于JDBC的DAO都继承自这个类,那么你会自动地从JdbcDaoSupport
类中继承了setDataSource(..)
方法。 是否将你的DAO类继承自这些类完全取决于你自己的决定,事实上这并不是必须的,如果你看一下JdbcDaoSupport
类你会发现,这里只是提供了一个简便的方式而已。
无论你是否使用上述这种初始化方式,都无需在执行某些SQL操作时多次创建一个JdbcTemplate
实例。记住,一旦JdbcTemplate
被创建,他是一个线程安全的对象。 一个你需要创建多次JdbcTemplate
实例的理由可能在于,你的应用需要访问多个不同的数据库,从而需要不同的DataSources
来创建不同的JdbcTemplates
实例。
11.2.2. NamedParameterJdbcTemplate
类
NamedParameterJdbcTemplate
类为JDBC操作增加了命名参数的特性支持,而不是传统的使用('?'
)作为参数的占位符。NamedParameterJdbcTemplate
类对JdbcTemplate
类进行了封装, 在底层,JdbcTemplate
完成了多数的工作。这一个章节将主要描述NamedParameterJdbcTemplate
类与JdbcTemplate
类的一些区别,也就是使用命名参数进行JDBC操作。
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public int countOfActorsByFirstName(String firstName) {
String sql = "select count(0) from T_ACTOR where first_name = :first_name";
SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName);
return namedParameterJdbcTemplate.queryForInt(sql, namedParameters);
}
注意这里在'sql'
中使用了命名参数作为变量,而这个名称所对应的值被定义在传入的'namedParameters'
中作为参数(也可以传入到MapSqlParameterSource
中作为参数)。
你也可以传入许多命名参数以及他们所对应的值,以Map
的方式,作为键值对传入到NamedParameterJdbcTemplate
中。 (其余的被NamedParameterJdbcOperations
所暴露的接口以及NamedParameterJdbcTemplate
实现类遵循了类似的方式,此处不包含相关内容)。
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public int countOfActorsByFirstName(String firstName) {
String sql = "select count(0) from T_ACTOR where first_name = :first_name";
Map namedParameters = Collections.singletonMap("first_name", firstName);
return this.namedParameterJdbcTemplate.queryForInt(sql, namedParameters);
}
NamedParameterJdbcTemplate
类所具备的另外一个比较好的特性就是可以接收SqlParameterSource
作为传入参数 (这个类位于相同的包定义中)。 你已经在先前的一个例子中看到了这个接口的一个具体实现类。( MapSqlParameterSource
类)。而SqlParameterSource
这个接口对于NamedParameterJdbcTemplate
类的操作而言是一个传入的参数。MapSqlParameterSource
只是一个非常简单的实现,使用了java.util.Map
作为转接器, 其中,Map中的Key表示参数名称,而Map中的Value表示参数值。
另外一个SqlParameterSource
的实现类是BeanPropertySqlParameterSource
。这个类对传统的Java进行了封装(也就是那些符合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;
}
// setters omitted...
}
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public int countOfActors(Actor exampleActor) {
// notice how the named parameters match the properties of the above 'Actor
' class
String sql = "select count(0) from T_ACTOR where first_name = :firstName and last_name = :lastName";
SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor);
return this.namedParameterJdbcTemplate.queryForInt(sql, namedParameters);
}
注意,NamedParameterJdbcTemplate
类只是封装了JdbcTemplate
模板; 因而如果你需要访问相应被封装的JdbcTemplate
类,并访问一些只有在JdbcTemplate
中拥有的功能,你需要使用getJdbcOperations()
方法来进行访问。
请参照第 11.2.1.2 节 “JdbcTemplate
的最佳实践”来获取一些使用NamedParameterJdbcTemplate
的最佳实践。
11.2.3. SimpleJdbcTemplate
类
注意
SimpleJdbcTemplate
所提供的一些特性必须工作在Java 5及以上版本。
SimpleJdbcTemplate
类是对JdbcTemplate
类进行的封装,从而可以充分利用Java 5所带来的varargs和autoboxing等特性。 SimpleJdbcTemplate
类完全利用了Java 5语法所带来的蜜糖效应。凡是使用过Java 5的程序员们如果要从Java 5迁移回之前的JDK版本,无疑会发现这些特性所带来的蜜糖效应。
“before and after”示例可以成为SimpleJdbcTemplate
类所带来的蜜糖效应的最佳诠释。 下面的代码示例首先展示了使用传统的JdbcTemplate
进行JDBC访问的代码,接着是使用SimpleJdbcTemplate
类做同样的事情。
// classic JdbcTemplate
-style...
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public Actor findActor(long id) {
String sql = "select id, first_name, last_name from T_ACTOR where id = ?";
RowMapper mapper = new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setId(rs.getLong("id"));
actor.setFirstName(rs.getString("first_name"));
actor.setLastName(rs.getString("last_name"));
return actor;
}
};
// notice the cast, the wrapping up of the 'id' argument
// in an array, and the boxing of the 'id' argument as a reference type
return (Actor) jdbcTemplate.queryForObject(sql, mapper, new Object[] {Long.valueOf(id)});
}
下面是同样的逻辑,使用了SimpleJdbcTemplate
;可以看到代码“干净”多了:
// SimpleJdbcTemplate
-style...
private SimpleJdbcTemplate simpleJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
public Actor findActor(long id) {
String sql = "select id, first_name, last_name from T_ACTOR where id = ?";
ParameterizedRowMapper<Actor> mapper = new ParameterizedRowMapper<Actor>() {
// notice the return type with respect to Java 5 covariant return types
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setId(rs.getLong("id"));
actor.setFirstName(rs.getString("first_name"));
actor.setLastName(rs.getString("last_name"));
return actor;
}
};
return this.simpleJdbcTemplate.queryForObject(sql, mapper, id);
}
请同样参照第 11.2.1.2 节 “JdbcTemplate
的最佳实践”来获取一些SimpleJdbcTemplate
的最佳实践
注意
SimpleJdbcTemplate
只是提供了JdbcTemplate
所提供的功能的子类。 如果你需要使用JdbcTemplate
的方法,而这些方法又没有在SimpleJdbcTemplate
中定义,你需要调用getJdbcOperations()
方法 获取相应的方法调用。JdbcOperations
接口中定义的方法需要在这边做强制转化才能使用。
为了从数据库中取得数据,我们首先需要获取一个数据库连接。Spring通过DataSource
对象来完成这个工作。 DataSource
是JDBC规范的一部分,它被视为一个通用的数据库连接工厂。通过使用DataSource, Container或Framework可以将连接池以及事务管理的细节从应用代码中分离出来。 作为一个开发人员,在开发和测试产品的过程中,你可能需要知道连接数据库的细节。但在产品实施时,你不需要知道这些细节。通常数据库管理员会帮你设置好数据源。
在使用Spring JDBC时,你既可以通过JNDI获得数据源,也可以自行配置数据源(使用Spring提供的DataSource实现类)。使用后者可以更方便的脱离Web容器来进行单元测试。 这里我们将使用DriverManagerDataSource
,不过DataSource有多种实现, 后面我们会讲到。使用DriverManagerDataSource
和你以前获取一个JDBC连接 的做法没什么两样。你首先必须指定JDBC驱动程序的全限定名,这样DriverManager
才能加载JDBC驱动类,接着你必须提供一个url(因JDBC驱动而异,为了保证设置正确请参考相关JDBC驱动的文档), 最后你必须提供一个用户连接数据库的用户名和密码。下面我们将通过一个例子来说明如何配置一个DriverManagerDataSource
:
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
dataSource.setUsername("sa");
dataSource.setPassword("");
相关推荐
### 错误处理和异常 JDBC操作中可能出现的异常主要有`SQLException`及其子类,如`SQLIntegrityConstraintViolationException`、`SQLTimeoutException`等。当出现异常时,需要捕获并处理这些异常,确保程序的健壮性...
在给定的文件中,我们有四个核心类:JdbcSession、JdbcSessionException、JdbcResourceManager和JdbcSessionFactory。这些类构成了一个完整的JDBC辅助库,提供了一套方便的接口来执行数据库操作。通过分析这些类的源...
综上所述,“异常处理和JDBC”涵盖了Java编程中与数据库交互的核心技术,包括如何优雅地处理可能出现的问题,以及如何利用JDBC提供的强大工具来高效、安全地操作数据库。学习并掌握这些知识点对于任何Java开发者来说...
首先,我们需要了解Struts1的核心组件: 1. **ActionServlet**:Struts1的入口点,负责处理所有的HTTP请求,并根据配置文件(struts-config.xml)将请求分发到相应的Action。 2. **Action**:业务逻辑处理类,实现...
- **JDBC 1.x**:最初版本,提供了基本的数据存储架构和核心接口,如`DriverManager`、`Connection`、`Statement`和`ResultSet`等。 - **JDBC 2.0**:引入了可滚动结果集、可更新结果集、批量更新等功能,增强了性能...
JDBC是Java语言中用于与各种数据库交互的一组接口和类,它提供了标准的方法来连接、查询、更新数据库。在本项目中,我们需要使用JDBC驱动来建立与SQL Server的连接,执行SQL语句,并处理查询结果。确保正确地配置...
它不仅提供了接口的定义,还涉及到数据类型映射、错误处理、连接管理、安全性、国际化和遗留问题的处理等。JDBC规范的API允许Java应用程序能够与各种关系数据库进行交互操作,其核心功能包括创建连接、发送SQL命令、...
DAO设计模式的核心思想是创建一个接口或抽象类,代表特定的数据对象,然后实现这个接口或抽象类来处理与数据库的交互。在Java中,通常会为每个数据库表创建一个DAO类,用于执行CRUD(Create、Read、Update、Delete)...
JDBC中的SQLException类用于处理在数据库操作过程中可能出现的错误。通过捕获并处理SQLException,可以在数据库操作失败时提供有用的错误信息,从而提高程序的健壮性和用户体验。 在JDBC中,可以将多个SQL语句组合...
此外,JDBC 6.0版本可能引入了一些新特性,例如增强的性能优化、更好的错误处理机制、支持更多的数据库特性(如XML类型处理、存储过程的调用等)以及对Java 8特性的兼容。开发者在实际应用中应参考官方文档,了解...
1. **DriverManager**: 它是JDBC的核心类之一,用于加载JDBC驱动并建立与数据库的连接。 2. **Connection**: 表示与数据库之间的连接,它是所有其他JDBC对象的工厂。 3. **Statement**: 用于向数据库发送静态SQL语句...
- JDBC 4.1引入了更好的错误处理,例如SQLException的getCause()方法。 - JDBC 4.2增加了对Java 8日期和时间类型的原生支持。 总之,JDBC是Java编程语言访问数据库的标准,理解并熟练掌握JDBC操作对于任何Java...
在开发过程中,注意管理和优化JDBC连接,避免资源泄漏,以及确保良好的错误处理机制,都是提高应用稳定性和性能的关键。同时,遵循最佳实践,如使用连接池管理数据库连接,可以有效地提升系统性能和可维护性。
在信息技术领域,数据库是存储和管理数据的核心组件。SQLServer作为微软公司推出的强大关系型数据库管理系统,广泛应用于企业级应用开发。对于Java开发者而言,与SQLServer进行交互时,就需要使用到数据库驱动。本文...
1. 主要的JDBC驱动类库(如mssql-jdbc.jar或sqljdbc4.jar),这是连接SQL Server数据库的核心组件。 2. 配置文件,如readme.txt或license.txt,包含了使用驱动的说明和许可信息。 3. 示例代码或文档,演示如何在Java...
- 错误处理:处理异常,如`SQLException`,确保程序的健壮性。 - 连接池:虽然这里提到的是单例,但实际生产环境中,通常使用连接池(如C3P0、HikariCP)来管理和复用数据库连接,以提高性能。 5. **数据库配置**...
这些类可能用于数据库连接池管理、性能优化、错误处理、日期时间处理等,帮助开发者更高效地进行数据库操作。 在使用这些JAR文件时,开发人员需要按照以下步骤进行操作: 1. **添加依赖**:将这三个JAR文件添加到...
JDBC架构主要包括四个主要组件:驱动管理器(Driver Manager)、数据库驱动(Database Driver)、JDBC API和数据库系统。驱动管理器负责加载和管理数据库驱动,JDBC API提供了一组接口和类供应用程序调用,而数据库...
3. **spring-core-3.2.3.RELEASE.jar**: Spring的核心模块,包含了Spring框架的基础组件,如IoC(Inversion of Control,控制反转)和DI(Dependency Injection,依赖注入)。这两个概念是Spring框架的核心,使你...