一. 引言
Spring是一个轻量级的应用程序框架。在许多情况中,Spring都能够良好地代换传统的由Java EE应用程序服务器所提供的服务。Spring既是综合性的也是模块化的。基于其分层架构,它能够使开发者灵活地单独使用其任何一部分。Spring由许多模块组成,例如IoC容器,AOP,MVC,持久性,DAO和remoting。这些模块都是相当松耦合的:其中,一些模块的使用根本不需要另一些模块。以前,简直还没有象Spring应用程序这样的:你可以选择使用一些,大多数,或所有的Spring框架支持的组件来构建你的应用程序。
Spring框架所提供的JDBC支持与其它Spring部分并非是紧耦合的,这极有利于代码的可维护性。本文将向你展示任何直接使用JDBC(也即是,不通过一些O/R映射框架本身使用JDBC)的应用程序是如何从Spring中受益的。
二. 传统型JDBC
传统型JDBC有许多积极的方面使之在许多J2SE和J2EE应用程序开发中占有重要地位。然而,也有一些特征使其难于使用:
· 开发者需要处理大量复杂的任务和基础结构,例如大量的try-catch-finally-try-catch块。
· 应用程序需要复杂的错误处理以确定连接在使用后被正确关闭,这样以来使得代码变得冗长,膨胀,并且重复。
· JDBC中使用了极不明确性的SQLException异常。
· JDBC没有引入具体的异常子类层次机制。
相应于任何一种错误,都只是抛出SQLException异常-无论它来源于JDBC驱动程序还是来源于数据库,这使得程序员很难理解到底是哪里实际出现了错误。例如,假如SQL对象是无效的或已经被锁定,那么将抛出一个SQLException异常。调试这样的异常需要一定的时间来检查SQL状态值和错误代码。更有甚者,SQL状态值和错误代码的含义在各种数据库之间都有些差别。
事实证实,编写JDBC代码并不是一项轻易的工作-存在大量的重复性的工作。为了说明问题,下面是一个例子-使用传统型JDBC来从数据库中得到一个可用任务的列表。
package com.spring.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Vector;
public class TraditionalJDBC {
public Vector getTasksNames() {
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
Vector task = new Vector();
try {
con = getConnection();
pstmt = con.prepareStatement( "select TASKNAME from tasks");
rs = pstmt.executeQuery();
while (rs.next()) {
task.add(rs.getString(1));
}
} catch (SQLException e) {
System.out.println(e);
} finally {
try {
rs.close();
pstmt.close();
con.close();
} catch (SQLException e1) {
System.out.println(e1);
}
}
return task;
}
private Connection getConnection()throws SQLException {
try {
DriverManager.registerDriver(new Oracle.jdbc.driver.OracleDriver());
return DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl",
"scott","tiger");
} catch (SQLException sqle) {
System.out.println(sqle);
return null;
}
}
public static void main(String[] args) {
TraditionalJDBC obj = new TraditionalJDBC();
Vector task = obj.getTasksNames();
for (int i = 0; i < task.size(); i++) {
System.out.println(task.elementAt(i));
}
}
}
除了实际查询数据库的SQL代码外,上面的示例中需要巨大数量的例程代码。getConnection()方法与我们的任务无关,而即使是getTasksNames()方法也仅包含特定于当前任务的两行代码。剩下的都是一些普通的复杂的任务代码。
JDBC的许多积极方面使得它在许多J2SE和J2EE应用程序中仍然占有重要地位。然而,正如你所见,有一些特征使其比我们可能想像的要更难于使用。JDBC这些乏味并且有时挫败人性的特征已经导致出现了许多公共的可以利用的JDBC抽象框架(例如SQLExecutor和Apache Jakarta Commons DBUtils),还有数不清的自家生产性JDBC应用程序框架。一种公共的可以利用的JDBC抽象框架正是Spring框架的JDBC抽象。
三. Spring JDBC简介
Spring所提供的JDBC抽象框架由四个不同的包组成:
· 核心包包含JdbcTemplate。这个类是一个基础类之一-由Spring框架的JDBC支持提供并使用。
· 数据源包是实现单元测试数据库存取代码的重要的一部分。它的DriverManagerDataSource能够以一种类似于你已经习惯于JDBC中的用法:只要创建一个新的DriverManagerDataSource并且调用setter方法来设置DriverClassName,Url,Username和PassWord。
· 对象包中包含类,用于描述RDBMS查询、更改和存储过程为线程安全的、可重用的对象。
· 支持包-你可以从这里找到SQLException翻译功能和一些工具类。
1) 模板设计模式
Spring JDBC实现模板设计模式,这意味着,代码中的重复的复杂的任务部分是在模板类中实现的。这种方式简化了JDBC的使用,因为由它来处理资源的创建和释放。这有助于避免普通错误,例如忘记关闭连接等。它执行核心JDBC工作流任务,如语句创建和执行,而让应用程序代码来提供SQL并且提取结果。
2) Spring JDBC异常处理
Spring框架非凡强调在传统型JDBC编程中所面临的与下列方案有关的问题:
· Spring提供一个抽象异常层,把冗长并且易出错误的异常处理从应用程序代码移到由框架来实现。框架负责所有的异常处理;应用程序代码则能够专注于使用适当的SQL提取结果。
· Spring提供了一个重要的异常类层次,以便于你的应用程序代码中可以使用恰当的SQLException子类。
借助于一个抽象异常层,我们成功地实现了数据库独立性而不必改变异常处理。例如,假如你把你的数据库从PostgreSQL改变为Oracle,那么你不必把异常处理从OracleDataException改变到PostgresDataException。Spring能够捕捉应用程序服务器特定的异常并抛出一个Spring数据异常。
当处理异常时,Spring检查来自一个数据库连接的元数据可用性以决定数据库产品。它使用这种知识来把SQLException映射到其自己异常层次中的具体的异常上。因此,我们不需要担心专门性的SQL状态或错误代码问题;Spring的数据存取异常不是JDBC特定的,因此你的DAO不必绑定到JDBC(由于其可能抛出的异常)。
四. Spring JDBC示例
在下面两个列表中,我们将使用前面用传统型JDBC实现的业务逻辑为例并且展示使用Spring JDBC版本是多么轻易。首先,我们从一个简单的接口开始。
package com.spring.jdbc;
import java.util.List;
public interface TasksDAO {
public List getTasksNames();
}
接下来,我们提供针对于TasksDAO接口的一种实现。
package com.spring.jdbc;
import java.util.Iterator;
import java.util.List;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXMLApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.RowMapperResultReader;
import org.springframework.jdbc.core.support.JdbcDaoSupport;