Spring自带了一组数据访问框架,集成了多种数据访问技术。不管你是直接通过JDBC还是用Hibernate这样的对象关系映射(object-relational mappingORM)框架框架实现数据持久化,Spring都能够消除持久化代码中那些单调枯燥的数据访问逻辑。我们可以依赖Spring来处理底层的数据访问,这样,就可以专注于应用程序中数据的管理了。
Spring的数据访问哲学
Spring的目标之一是允许我们在开发应用的时候,能够遵循面向对象(OO)原则中的”针对接口编程”。Spring对数据访问的支持也不例外。
为了避免持久化的逻辑分散到应用的各个组件中,最好将数据访问的功能放到一个或多个专注于此项任务的组件中,这样的组件通常称为数据访问对象或Repository. 为了避免应用与特定的数据访问策略耦合在一起,编写良好的Repstitory应该以接口的方式暴露功能,如下图展现可设计数据访问层的合理方式。
服务对象本身并不会处理数据访问,而是将数据访问委托给Repository。Repository接口确保其与服务对象的松耦合。
服务对象通过接口来访问Repository的好处:
- 使得服务对象易于测试,因为他们不再与特定的数据访问是实现绑定在一起。实际上,你可以为这些数据访问接口创建mock实现,这样无需连接数据库就能测试服务对象,而且会显著提升单元测试的效率并排除因数据不一致所造成的测试失败。
- 数据访问层是以持久化技术无关的方式来进行访问,持久化方式的选择独立于Repository,同时只有数据访问相关的方法才通过接口进行暴露。这可以实现灵活的设计,并且切换持久化框架对应用程序其他部分所带来的影响是最小的。如果将数据访问层的实现细节渗透到应用程序的其他部分中,那么整个应用程序将于数据访问侧耦合在一起,从而导致僵化的设计。
接口是实现松耦合代码的关键,并且应将其用于应用程序的各个层,而不仅仅是持久化层。还要说明一点,尽管Spring鼓励使用接口,但这并不是强制的———你可以使用Spring将bean(DAO或其他类型)直接转配到另一个bean的某个属性中,而不是一定要通过接口注入。
为了将数据访问层与应用程序的其他部分隔离开来,Spring采用的方式之一就是提供统一的异常体系,这个异常体系用在了它支持的所有持久化方案中。
Spring的数据访问异常体系
如果你曾经编写过JDBC代码(不使用Spring),你肯定会意识到如果不强制捕获SQLExecption的话,几乎无法使用JDBC做任何事情,SQLExecption
表示在尝试访问数据库的时候出现问题,但是这个异常却没有告诉你在哪里出错了。
可能导致抛出Execption的常见问题包括:
- 应用程序无法连接数据库;
- 要执行的查询存在语法错误;
- 查询中所使用的表或列不存在;
- 视图插入或更行的数据违反了数据库约束。
SQLExecption的问题在于捕获到它的时候该改如何处理,事实上能够触发SQLExecption的问题通常是不能再catch代码块中解决的,大多数抛出SQLExecption的情况表示发生了致命性的错误,如果应用程序不能够连接到数据库,这个通常意味着应用不能继续使用了,类似的,如果查询时出现了异常,那么运行时程序基本上也是无能为力的了。
如果无法从SQLExecption中恢复,那么为什么我们还有强制捕获它呢?
即使对某些SQLExecption有处理方案,我们还是要捕获它,并查看其属性才能够获知问题的根源的更多信息,这个是因为SQLExecption被视为处理数据访问所有问题的通用异常,对于所有的数据访问问题,都会抛出SQLExecption,而不是对每种可能的问题都会有不同的异常类型。
一些持久化框架提供了相对丰富的异常体系,例如,Hibernate 提供了二十左右的异常,分别对应于特定的数据库访问问题,这样就可以针对想处理的异常编写catch代码块。即便如此,Hibernate的异常是其本身特有的,如果抛出了Hibernate所特有的异常,那我们对Hibernate的使用将会渗透到应用的其他部分,如果不这样做的话,我们就得捕获持久化平台的异常,然后将其作为平台无关的异常再次抛出。
一方面,JDBC的异常体系过于简单了———实际上,它算不上一个体系,另一方面,Hibernate的异常体系是其本身所独有的,但是,我们需要的数据访问异常要具有描述性而且又与特定的持久化框架无关。Spring 的JDBC异常就完成了这两个目标。
Spring所提供的平台无关的持久化异常
Spring JDBC提供的数据访问异常体系解决了以上的两个问题。不同于JDBC,Spring提供了多个数据访问异常,分别描述了他们抛出时所对应的问题。
JDBC 异常 | Spring 的数据访问异常 |
---|---|
BatchUpdateException | BadSqlGrammarExecption |
DataTruncation | CannotAcquireLockExecption |
SQLExecption | DataAccessExecption |
SQLWarning | QueryTimeoutExecption |
—– | ——- |
上表中只是列出了Spring中的部分异常,实际上Spring为读取和写入数据库的几乎所有错误都提供了异常。尽管Spring的异常体系比JDBC简单的SQLException丰富得多,但是它并没有与特定的持久化方式相关联。这意味着我们可以使用Spring抛出一致的异常,而不用关心所选择的持久化方案,这有助于我们将所选择持久化机制与数据访问层隔离开来。
上面表中要注意的一点是:这些异常都继承自DataAccessExeecption,DataAcccessException的特殊之处在于它是一个非检查型异常,换句话说,没有必要捕获Spring所抛出的数据库访问异常。DataAccesssExecption 只是Spring处理检查型异常和非检查型异常哲学的一个范例,Spring认为触发异常的很多问题是不能在catch代码k中修复的,Spring使用了非检查型异常,而不是强制开发人员编写catch代码块,这把是否要捕获异常的权利留给了开发人员。