POJO式开发x

发布时间:2022-06-21 11:45:09   来源:党团工作    点击:   
字号:

 POJO 式开发 POJO

  POJO 就是简单 java 对象,不实现任何特殊接口。

 POJO 这一名字由 Fower 、 Rebbecca 、 Parsos 、 Josh MacKenzie(Foeler POJO) 发明,目的地是为了给普通 Java 对象取个令人兴奋的、过目不忘的名字。

  早期 EJB 及其存在的问题 及其存在的问题

  EJB1.0 版本发布于 1998 年,它提供了两种企业 bean :会话 bean 和实体 bean 。会话 bean 便是无状态服务或与客户端之间的有状态会话。实体 bean 表示数据库里的数据,最初意在实现业务对象。

 EHB2 提炼了 EJB 编程模型。不仅增加了支持由容器管理的关系增强型实体 bean ,还新增了消息驱动 bean( 负责处理 Java Message Service 或 JMS ,消息 ) 。

  EJB 存在的问题 存在的问题

 尽管有很多书帮助开发人员对付 EJB ,并学会如何有效的使用 EJB ,但是 EJB 的;两个主要问题并没有直接解决。

  第一,

 EJB 鼓励开发人员编写过程式 过程式应用程序 应用程序 第二,

 使用 EJB 开发 相当麻烦

  过程式设计的缺点:

 过程式设计的缺点:

  对业务逻辑的组织方式主要有两种:过程式或面向对象。过程式方式以函数为单元组织代码,这些函数操作单独的简单数据对象。在过程式架构中,数据结构遍布各处,并作为参数传入函数,或返回给调用函数。

 数据与操作之间的关系非常松散 ,并且完全由开发人员自己维护。在面向对象语言出现之前,这种编程方式主导了软件开发。

 与之相比,面向对象方法则以对象为单元组织代码,这些对象 具有状态和行为 ,并与其他对象协作。

 数据结构和操作定义在一个语言构造单元内,数据和对数据操作并存于其中。数据和操作之间的关系 ( 和状态 ) 由语言本省维护 。与过程式设计相比,面向对象设计更易理解、维护、扩展和测试。

  如果业务逻辑够简单,过程式设计方法倒也不成问题,但是业务逻辑总有变得愈加复杂的趋势。一旦需求改变,业务逻辑就必须实现新的特性, EJB 的代码量会不断增加。

  EJB2 在一定程度上就是鼓励人们编写过程式代码, 实现新行为时,不必像设计真正的对象模型那样费心地识别类并赋予其职责。相反,你可以编写一个新的会话 bean 方法或在现有方法里添加代码 。

  这钟过程式的设计方法,有些开发人员仍把持久对象简单的视为一种向数据库存取数据和编写过程式业务逻辑方法,这就是所谓的贫血模型

  EJB 开发的麻烦:

 开发的麻烦:

  n

 你必须面对恼人而长的编辑 - 编译 - 调试周期 n

 你得面对关注点缺少分离的显示 n

 你必须编写大量的代码才能实现一个 EJB n

 你必须编写数据传输对象 (DTO)

  用 POJO 开发 开发

 用 POJO 进行开发,仅有 POJO 本身还是不够的。在企业应用程序里,你还需要诸如事务管理、安全和持久化等服务,此前这些服务由 EJB 容器提供。现在的解决方案是使用所谓“轻量级”框架来代替 J2EE STACK 里的一些“重量级”部分。主要是 4 种轻量级框架:

 Hibernate 、 JDO 、 Ibatis 和 Spring 。

  这些技术的主要特征在于他们都是非侵入式的。它们提供事务和持久化时并不要求应用程序类实现任何特殊接口。甚至当应用程序的类需要运行在事务里或者持久化的时候,它们仍是 POJO 。

  典型的 典型的 EJB 和 POJO 方法比较 方法比较

  典型的 EJB 方法 POJO 方法 组织 过程式业务逻辑 面向对象设计 面向对象设计 实现 基于 EJB POJO 数据库访问 JDBC/SQL 或实体 Bean 持久层框架 持久层框架 返回给表示层的数据 DTO 业务对象 业务对象 事务管理 EJB 容器管理的事务 Spring 框架 框架 应用程序组装 显示的 JNDI 查询 依赖注入 依赖注入

  l

 面向对象设计

  整个设计更容易理解和维护 更易于测试 更易扩展

  l

 使用 POJO

  开发更加容易 更加快捷 可移植性增强

 l

 持久化 POJO

  使用 JDO 和 Hibernate 提供透明持久化,这意味着类不会意识到它们是持久的。应用程序只需要调用持久层框架 API 保存、查询和删除持久对象、而且对测试也很方便。

  l

 消除 DTO

  DTO 又称为值对象( value object )。

 DTO 只是一个由成员变量组成的简单行为对象,用于从业务层向表示层返回数据。这是由于表示层无法高效地访问 EJB2 实体 bean ,因此 EJB 程序需要 DTO 。

  向表示层返回 Hibernate 、 JDO 、 EJB3 对象有两种方式。一种选择是表示层返回仍持久地的对象。另一种做法是让业务层返回脱管对象。

 l

  是 POJO 具有事务性

  用 spring 管理事务。对测试也很方便。

  系统设计时需要考虑的五大因素:

 系统设计时需要考虑的五大因素:

 1 、如何组织业务逻辑 2 、如何封装业务逻辑,以及暴露给表示层及其他客户程序调用的接口

 3 、如何访问数据库 4 、如何处理短事务中的并发 5 、如何处理长期运行事务中的并发 决策 选项 业务逻辑封装 EJB Session Façade 模式 POJO Façade 模式 Exposed Domain Model 模式 数据库访问 直接使用 JDBC iBATIS Hibernate JDO 数据库事务中的并发 不理睬该问题 悲观锁 乐观锁 可串行化隔离级别 长期运行事务中的并发 不理睬该问题 Pessimistic Offline Lock 模式 Optimistic Offline Lock 模式

  以上 5 个决策每个都有多种选项。基于 EJB 的设计,它由会话 bean 实现的过程式代码组成,并使用 JDBC 访问数据库。相比之下,基于 POJO 的设计由对象模型组成,通过 JDO 、Hibernate 等 O/R 框架映射到数据库,并用使用 Spring 进行事务管理的 POJO façade 进行封装。每种选项都有优缺点,这也决定了它只能适用于某种具体情况。每种选项都会在一个或多个方面做出一定的妥协,包括功能性、开发难易度、可维护性和可用性等,需要自己的应用程序作出最佳选择。

  封装业务逻辑 封装业务逻辑

 业务逻辑的接口由那些可被表示层调用的类型(type )和方法(method )

 组成。接口设计的要点是应当封装多少业务逻辑的实现,并对表示层不可见。封装隐藏了业务逻辑的实现细节,可以防止表示层受业务逻辑变化的影响,从而提升可 维护性。同时还需考虑怎样处理事务、安全性和远程调用等问题,因为通常这些都是业务逻辑接口代码的职责。一般来说,业务层接口应保证对业务层的每个调用都 在事务中执行,以便保证数据库的数据一致性。同样的,业务层接口还要验证调用者是否有足够的权限来调用某种业务方法。此外,它还要负责处理某些远程客户 端。

 若存在表示层远程访问业务层 API 的情况,尽量将业务层的 API 设计成粗粒度的,这样对业务层的调用越少,数据库事务数量就越少,内存缓存对象的机会就越多。还能减少网络来回传输次数。

  单个数据库 事务中的并发 事务中的并发 完全事务脚本、乐观(optimistic )锁、悲观(pessimistic )锁 )锁

  完全事务脚本 是一种解决方案是使用完全和其他事务隔离的事务,用数据库的话来说,就是隔离级别为 serializable (串行化)的事务(悲观锁的另一种方案)。数据库保证:执行多个 serializable 事务的结果和一个个串行执行它们的结果一样。serializable 事务避免了更新丢失、读取不一致等问题。一些数据库还提供了repeatable read (能够保持一致的重复读取)和 Read committed 的 隔离级别,完全隔离事务有两个主要优点:一是使用简单;二是避免了很多并发问题,包括修改丢失和读取不一致的问题。完全隔离事务的主要缺点是开销太大,降 低了性能和规模扩展性,不管有没有并发更新,都需要额外开销。而且,由于死锁和其他并发相关问题,完全隔离事务比低隔离级别的事务的失败频率更高。用 serializable 或者 repeatable read 隔离级别的事务,这种方案不需要数据库模式变化。因为数据库的 serializable 事务机制能够处理并发更新,所以不需要用语句来锁住记录,也不需要维护版本号。在 spring 中可以通过配置数据源的属性“defaultTransactionIsolation ”为“SERIALIZABLE ”来实现。

  乐观锁的工作原理 乐观锁的工作原理 是让应用程序检查它即将更新的数据是否已被另一个事务修改(自该数据上次读取以来)。实现乐观锁的一种常见做法是在每个表里添加一个版本字段,每次应用程序更新数据表记录时就增加这个版本字段。每个 UPDATE 语句中的 WHERE 子句会根据上次读取的值来判断这个版本号是否改变。使用诸如JDO 和 Hibernate 的持久层构架时,实现乐观锁更为容易,因为它们已将乐观锁作为配置选项提供。

 应用程序或持久层框架有三种方法可以判断一条记录自从上次读取出来后是否被修改过。

 第一种方法是 用一个 version (版本)字段来跟踪记录修改状况 ,每次修改,version 都会递增。事务只需要把原来读出的 version 和当前 version 进行比较,就可以判断一条记录是否被修改过。应用程序检查和修改 version 字段是比较简单的做法,通常也是最好的做法。

 第二种方法是 用时间戳字段,每次应用程序修改数据,时间戳也会更新 。

 事务只需要把原来读出的时间戳和当前时间戳进行比较,就可以判断一条记录是否被修改过。这个表结构也很容易实现,尤其是这种情况下,数据表经常已经有一个 时间戳字段来记录用户修改记录的时间。然而,时间戳的问题是,如果两个修改操作之间的时间差小于时钟最小单位,那么一个事务可能覆盖另一个事务的修改。所 以,只有在无法增加 version 字段的遗留系统中,才应该使用时间戳,否则,尽量使用version (版本)。

 第三种方法是 把上次读出的字段值和现有字段值进行比较 。这种方法最大的好处是,不需要引入 version 或者时间戳字段,所以可以用在遗留系统中。这个方法的一个缺点是使得 SQL UPDATE 更加复杂,因为 WHERE 子句里面包含所有的字段的条件(具体原因我们后面会详述)。还必须处理 null 字段,可能比较复杂。比如,有一次我发现,一个持久层框架不能正确比较空字符串,因为 Oracle 把空字符串认为是 null ,这和 Java 不一样。我们在数据表里面增加了一个版本字段,解决了这个问题。

 第三种方法的另一个缺点是,浮点数字段不能精确比较,浮点数字段的修改可能发现不了。由于这些问题,应用程序只有在别无选择,无法应用版本和时间戳的情况下,才应该使用这种方法。

  悲观锁的工作原理 悲观锁的工作原理 是 当读取某些记录时,事务先锁住这些记录,这样可以防止其他事务访问这些数据记录。具体细节要视数据库而定,不过糟糕的是,并非所有数据库都支持悲观锁。如果数据库支持悲观锁,在直接执行 SQL 语句的应用程序中,实现悲观锁非常容易。在 JDO 或 Hibernate 应用程序中使用悲观锁更为容易。JDO 以配置选项的方式提供悲观锁,而 Hibernate 则提供一个简单实用的 API ,来锁定对象。

 获取锁的机制是数据库相关的,并非所有数据库都支持。在 Oracle 数据库中,应用程序通过 SELECT FOR UPDATE 语句锁住选出的记录,从而实现悲观锁。如果已经有事务锁住记录,执行 SELECT FOR UPDATE 语句的事务就会被。如果其他事务更新、删除或者试图用 SELECT FOR UPDATE 选取这些记录,阻塞情况就会发生。事务一直阻塞,直到事务提交或者回滚。如果事务不想等待,可以采用SELECT FOR UPDATE NO WAIT ,如果不能立即锁住记录,就返回 ORA-00054 错误。你还可以用 SELECT FOR UPDATE WAIT 来指定等待时间。要注意的是:一些

 数据库对 SELECT FOR UPDATE 的用法有限制。例如,Oracle 里面,SELECT FOR UPDATE 只能用在顶层 SQL ,而不能嵌在子查询里面。还有一些 SQL 特性不能和SELECT FOR UPDATE 一起使用。这些特性包括 DISTINCT ,集合统计函数(max 、min 、sum 、count ),GROUP BY 。SELECT FOR UPDATE 也不能用在某些类型的view 和嵌套的 SELECT 里面。

 处理长事务中的并发 处理长事务中的并发 乐观离线锁(Optimistic Offline Lock) 模式、悲观离线锁(Pessimistic Offline Lock ) 模式 模式

  乐观离线锁模式 是 扩展此前描述的乐观锁机制,在编辑过程的最后一个数据库事务里,检查数据自最初读取后并未改变。例如,你可以使用共享数据表里的版本号字段实现这一机制。

 在编辑过程开始时,应用程序先将版本号存储在会话状态里。然后,当用户保存其更改时,应用程序进行检查,保证会话状态里保存的版本号和数据库中的版本号一 致. 由于乐观离线锁模式只在用户要保存修改后的数据时才进行检测,在实现诸如“修改订单”用例时,要用户放弃好几分钟才完成的操作,用户肯定会非常恼火,此时更好的选择是使用悲观离线锁 . 悲观离线锁模式 是在编辑过程开始之初,就锁定共享数据,以防止其他用户编辑该共享数据。这种方式与此前描述的悲观锁机制类似,只不过这里锁由应用程序而不是数据库实现。由于每次只有一个用户能编辑共享数据,因此可保证用户能保存自己的修改。

 通知并发更新失败 通知并发更新失败 当 DAO 或者数据库认为当前的两个事务不能并发运行时,就会发生并发失败。DAO 可以允许 JDBC SQLException 传播到 DAO 的调用者。不过,有两个原因说明这种做法不是一个好主意。抛出 SQLException 的第一个问题是 JDBC 相关。应用程序也可以使用 JDO 这样的持久层,但是抛出其他的非 JDBC 异常。理想情况下,更高层的应用程序组件不应该知道底层访问了数据库。

 SQLException 的另一个问题是,SQLException 是一个 checked exception (非 Runtime Exception ,需要显式声明或者捕获的异常),需要 DAO 调用者或者捕捉 SQLException ,或者在方法签名上声明继续抛出 SQLException ,这种情况下,调用者代码会变得杂乱。使用unchecked exceptions (Runtime Exception ,不需要显式声明或者处理的异常)来报告并发失败要好得多。