系统设计时需要考虑的五大因素:
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,不需要显式声明或者处理的异常)来报告并发失败要好得多。
- 大小: 16.1 KB
分享到:
相关推荐
在使用Feign客户端发送GET请求时,如果需要传递复杂的查询条件或分页参数,开发人员往往会倾向于使用Java POJO(Plain Old Java Object)对象作为方法参数。例如: ```java @FeignClient("microservice-provider-...
在Java开发中,数据访问对象(DAO)模式是一种常见的设计模式,用于封装对数据库的操作,使得业务逻辑与数据访问逻辑分离。JDBC(Java Database Connectivity)是Java平台中用于访问数据库的标准API,但它直接使用...
Spring框架的依赖注入设计模式让开发者能够轻松地管理对象之间的依赖关系,而EJB 3.0的Java注解则简化了EJB的声明式编程模型,使得容器可以自动处理事务、安全性和持久化等复杂任务。 总之,“POJO Application ...
- **POJO类中的布尔型变量**:避免在POJO类中布尔类型的变量名前加上`is`,以防止序列化问题。例如,使用`boolean deleted`而非`Boolean isDeleted`。 - **包名与类名规范**:包名一律使用小写,并且每个包名段仅...
Java 快速开发平台 J-Hi 是一款 JAVA WEB 应用软件快速开发开源平台,主要服务于软件企业和传统行业企事业单位信息中心的开发人员,为他们提供一套完整的一站式的 JAVA WEB 应用软件快速开发解决方案。该平台包括...
- **强制规定**:在POJO类中,布尔类型的变量不应以`is`开头,以避免某些框架解析时出现序列化问题。 - **反例**:定义为基本数据类型Boolean isDelete; **2. 常量定义** - **强制规定**:对于常量的定义,应确保...
在具体命名风格上,类名采用UpperCamelCase(大驼峰式命名),而方法名、参数名等使用lowerCamelCase(小驼峰式命名),有助于提高代码的阅读性和易懂性。常量则全部使用大写字母,单词之间用下划线分隔。 在OOP...
- **响应式设计**: 自动适应不同设备和屏幕尺寸,确保优秀的用户体验。 **应用场景** Vaadin适用于需要开发高质量、高度定制化的Web应用程序的企业。 **挑战** - **学习成本**: 对于初学者来说,Vaadin的学习成本...
- 对于高级的J2EE开发者来说,如果他们倾向于使用EJB会话Bean、POJO类、O/R映射等技术,则可能会发现本书提供的开发方式不是最理想的。对于这类开发者,Oracle提供了另一种基于ADF的开发指南,该指南更加侧重于如何...
综上所述,EJB3.0在很大程度上解决了EJB2.1中存在的问题,通过引入POJO模型、IOC模式、O/R Mapping技术、元数据批注等先进技术,极大提升了开发效率和系统的可维护性。这些改变使得EJB3.0成为了Java企业级开发的重要...
**强制规定**:类名采用`UpperCamelCase`风格,即驼峰式大写,但某些特殊情况下除外,例如领域模型中的DO(Data Object)、BO(Business Object)、DTO(Data Transfer Object)、VO(View Object)等。 **正例**:...
相对Hibernate和Apache OJB 等“一站式”ORM解决方案而言,ibatis 是一种“半 自动化”的ORM实现。 所谓“半自动”,可能理解上有点生涩。纵观目前主流的ORM,无论Hibernate 还是 Apache OJB,都对数据库...
- **POJO命名**:通常由Hibernate自动生成,符合实体对象命名规则。 - **DAO类命名**:同样由Hibernate自动生成,但需确保遵循数据库操作接口的命名习惯。 ### 注释规范 良好的注释可以极大提升代码的可读性。主要...
9. **前端框架**:如Bootstrap、Angular、Vue.js等,可以快速构建响应式、移动优先的Web界面,提高开发效率。 10. **安全机制**:包括身份验证(Authentication)和授权(Authorization),防止未授权访问。Spring ...
JSP是Java平台上的一个重要技术,用于创建动态网页,它结合了Java编程语言的强大力量和HTML的易用性,使得开发人员可以方便地构建交互式、数据驱动的Web应用。 **JSP核心概念** 1. **JSP页面结构**:JSP页面由静态...
Spring的核心特性包括了依赖注入(DI)、面向切面编程(AOP)以及基于POJO的轻量级和最小侵入式编程模型。 2. **企业级应用**:通常指的是需要处理大量数据和业务逻辑,同时要求高可用性、安全性和可伸缩性的应用...
- 支持**POJO**(Plain Old Java Object,普通的Java对象)风格的开发,使得代码更加简洁明了。 - 规范化的API设计确保了不同JPA实现之间的高度兼容性。 #### 2. MyEclipse支持下的JPA开发 - **MyEclipse**是一...
此外,类名使用UpperCamelCase(驼峰式命名),而方法名、参数名、成员变量和局部变量则使用lowerCamelCase(小驼峰命名)。常量命名需要全部大写,并用下划线隔开。 在类命名的细节上,手册提出了类名命名需避免...
在MVC模式下,模型通常由JavaBeans(也称为POJO,Plain Old Java Object)实现,它们封装了业务逻辑和数据。在Eclipse中,可以创建Java类来实现这些功能,并通过JSP页面上的EL(Expression Language)或JSTL(Java...