Sung in Blog

           一些技术文章 & 一些生活杂碎
JDBC 抽象和数据存储异常层次

数据访问是Spring 的另一个闪光点。

JDBC 提供了还算不错的数据库抽象,但是需要用痛苦的API。这些问题包括:

    需要冗长的错误处理代码来确保ResultSets,Statements以及(最重要的)Connections在使用后关闭。这意味着对JDBC的正确使用可以快速地导致大量的代码量。它还是一个常见的错误来源。Connection leak可以在有负载的情况下快速宕掉应用程序。

    SQLException相对来说不能说明任何问题。JDBC不提供异常的层次,而是用抛出SQLException来响应所有的错误。找出到底哪里出错了——例如,问题是死锁还是无效的SQL?——要去检查SQLState或错误代码。这意味着这些值在数据库之间是变化的。

Spring用两种方法解决这些问题:

    提供API,把冗长乏味和容易出错的异常处理从程序代码移到框架之中。框架处理所有的异常处理;程序代码能够集中精力于编写恰当的SQL和提取结果上。

    为你本要处理SQLException程序代码提供有意义的异常层次。当Spring第一次从数据源取得一个连接时,它检查元数据以确定数据库。它使用这些信息把SQLException映射为自己从org.springframework.dao.DataAccessException派生下来的类层次中正确的异常。因而你的代码可以与有意义的异常打交道,并且不需要为私有的SQLState或者错误码担心。Spring的数据访问异常不是JDBC特有的,因而你的DAO并不一定会因为它们可能抛出的异常而绑死在JDBC上。

Spring提供两层JDBC API。第一个时,在org.springframework.jdbc.core包中,使用回调机制移动控制权——并且因而把错误处理和连接获取和释放——从程序的代码移到了框架之中。这是一种不同的Inversion of Control,但是和用于配置管理的几乎有同等重要的意义。

Spring使用类似的回调机制关注其他包含特殊获取和清理资源步骤的API,例如JDO(获取和释放是由PersistenceManager完成的),事务管理(使用JTA)和JNDI。Spring中完成这些回调的类被称作template。

例如,Spring的JdbcTemplate对象能够用于执行SQL查询并且在如下的列表中保存结果:

代码:
JdbcTemplate template = new JdbcTemplate(dataSource);
final List names = new LinkedList();
template.query("SELECT USER.NAME FROM USER",
   new RowCallbackHandler() {
      public void processRow(ResultSet rs) throws SQLException {
         names.add(rs.getString(1));
      }
   });


注意回调中的程序代码是能够自由抛出SQLException的:Spring将会捕捉到这些异常并且用自己的类层次重新抛出。程序的开发者可以选择哪个异常,如果有的话,被捕捉然后处理。

JdbcTemplate提供许多支持不同情景包括prepared statements和批量更新的方法。Spring的JDBC抽象有比起标准JDBC来说性能损失非常小,甚至在当应用中需要的结果集数量很大的时候。

在org.springframework.jdbc.object包中是对JDBC的更高层次的抽象。这是建立在核心的JDBC回调功能基础纸上的,但是提供了一个能够对RDBMS操作——无论是查询,更新或者是存储过程——使用Java对象来建模的API。这个API部分是受到JDO查询API的影响,我发现它直观而且非常有用。

一个用于返回User对象的查询对象可能是这样的:

代码:

class UserQuery extends MappingSqlQuery {

   public UserQuery(DataSource datasource) {
      super(datasource, "SELECT * FROM PUB_USER_ADDRESS WHERE USER_ID = ?");
      declareParameter(new SqlParameter(Types.NUMERIC));
      compile();
   }

   // Map a result set row to a Java object
   protected Object mapRow(ResultSet rs, int rownum) throws SQLException {
      User user = new User();
      user.setId(rs.getLong("USER_ID"));
      user.setForename(rs.getString("FORENAME"));
      return user;
   }

   public User findUser(long id) {
      // Use superclass convenience method to provide strong typing
      return (User) findObject(id);
   }
}

这个类可以在下面用上:
代码:

User user = userQuery.findUser(25);

这样的对象经常可以用作DAO的inner class。它们是线程安全的,除非子类作了一些超出常规的事情。

在org.springframework.jdbc.object包中另一个重要的类是StoredProcedure类。Spring让存储过程通过带有一个业务方法的Java类进行代理。如果你喜欢的话,你可以定义一个存储过程实现的接口,意味着你能够把你的程序代码从对存储过程的依赖中完全解脱出来。

Spring数据访问异常层次是基于unchecked(运行时)exception的。在几个工程中使用了Spring之后,我越来越确信这个决定是正确的。

数据访问异常一般是不可恢复的。例如,如果我们不能链接到数据库,某个业务对象很有可能就不能完成要解决的问题了。一个可能的异常是optimistic locking violation,但是不是所有的程序使用optimistic locking。强制编写捕捉其无法有效处理的致命的异常通常是不好的。让它们传播到上层的handler,比如servlet或者EJB 容器通常更加合适。所有的Spring对象访问异常都是DataAccessException的子类,因而如果我们确实选择了捕捉所有的Spring数据访问异常,我们可以很容易做到这点。

注意如果我们确实需要从unchecked数据访问异常中恢复,我们仍然可以这么做。我们可以编写代码仅仅处理可恢复的情况。例如,如果我们认为只有optimistic locking violation是可恢复的,我们可以在Spring的DAO中如下这么写:

代码:

try {
   // do work
}
catch (OptimisticLockingFailureException ex) {
   // I'm interested in this
}

如果Spring的数据访问异常是checked的,我们需要编写如下的代码。注意我们还是可以选择这么写:
代码:

try {
   // do work
}
catch (OptimisticLockingFailureException ex) {
   // I'm interested in this
}
catch (DataAccessException ex) {
   // Fatal; just rethrow it
}

第一个例子的潜在缺陷是——编译器不能强制处理可能的可恢复的异常——这对于第二个也是如此。因为我们被强制捕捉base exception(DataAccessException),编译器不会强制对子类(OptimisticLockingFailureException)的检查。因而编译器可能强制我们编写处理不可恢复问题的代码,但是对于强制我们处理可恢复的问题并未有任何帮助。

Spring对于数据访问异常的unchecked使用和许多——可能是大多数——成功的持久化框架是一致的。(确实,它部分是受到JDO的影响。)JDBC是少数几个使用checked exception的数据访问API之一。例如TopLink和JDO大量使用unchecked exception。Gavin King现在相信Hibernate也应该选择使用unchecked exception。

Spring的JDBC能够用以下办法帮助你:


    你决不需要在使用JDBC时再编写finally block。

    总的来说你需要编写的代码更少了

    你再也不需要挖掘你的RDBMS的文档以找出它为错误的列名称返回的某个罕见的错误代码。你的程序不再依赖于RDBMS特有的错误处理代码。

    无论使用的是什么持久化技术,你都会发现容易实现DAO模式,让业务代码无需依赖于任何特定的数据访问API。

在实践中,我们发现所有这些都确实有助于生产力的提高和更少的bug。我过去常常厌恶编写JDBC代码;现在我发现我能够集中精力于我要执行的SQL,而不是烦杂的JDBC资源管理。

如果需要的话Spring的JDBC抽象可以独立使用——不强迫你把它们用作Spring的一部分。
posted on 2005-10-26 15:53 Sung 阅读(323) 评论(0)  编辑  收藏 所属分类: Java

只有注册用户登录后才能发表评论。


网站导航: