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