`
czjxdm
  • 浏览: 124701 次
社区版块
存档分类
最新评论

事务管理最佳实践全面解析(转)

阅读更多
事务管理最佳实践全面解析
前言
写作这篇文章的起因,是前一段时间,我使用Jbpm工作流引擎开发工作流管理系统的过程中,使用编程方式管理事务时遇到的问题。
由于之前很长一段时间,我一直都在使用Spring和EJB容器的声明式事务管理,因此,咋一遇到Jbpm这样的编程方式管理事务的情况,一下子搞不定了!经过几天的研究,我重新思考了怎样进行事务管理这个问题,并且发明了一种非常好的编程范式,或者说是事务管理的最佳实践。不敢独享,拿出来与诸君共赏。请大家批评指正。
前几个月,我对C++和Java编程方式进行了比较和研究。并且总结了一些C++编程中管理对象的最佳实践。
但由于那一段时间工作较忙,没有及时把文章写出来。后来想写文章时,却找不到当初为了说明写的Java和C++演示代码了。因此,该文一直未成,颇为遗憾!因此,这篇事务管理的文章,我不敢拖得太久。由于时间仓促,写得不好,请大家见谅!
事务管理
企业级应用,或者叫“信息管理系统”。这类软件通过数据库持久化保存、处理的信息。它们工作的核心,就是数据库。这类应用,是目前市场上最主流的商业应用。
事务管理,这个概念出自于数据库管理系统中。事务是一个单元的工作,要么全做,要么全不做。
事务管理对于维持数据库系统内部保存的数据逻辑上的一致性、完整性,起着至关重要的作用。如:一个银行应用软件中,转帐的操作中,需要先在A用户帐户中减去资金,然后再在B用户帐户中增加相应的资金。如果完成A帐户操作后,由于系统故障或者网络故障,没有能够完成接下来的操作,那么A帐户中的资金就白白流失了。显然,客户是无法接受这样的结果的!
如果我们把一个A和B帐户的操作放在一个事务单元中,那么如果遇到上述异常情况,A帐户减少资金的操作会回滚。A帐户的资金不会减少。
事务管理和数据库连接的关系
事务管理的工作,需要在数据库连接上进行。如果没有数据库连接,事务管理是无法实施的。
因此,一个事务单元,应该小于或者等于一个数据库连接的生命周期。
事务管理最佳模式
数据库连接管理最佳模式
数据库连接,是一种很宝贵也很昂贵的资源。一个数据库可以提供的数据库连接总数是有限的。而且,获取一次数据库连接也是非常昂贵的操作。需要建立网络连接。因此,我们应当尽可能的重用数据库连接,让数据库连接维持的时间尽可能的长。
但是,我们也不能把数据库连接维持的太久。因为,上文已经说过了,一个数据库可以提供的数据库连接总数是有限的。如果数据库连接的时间很长,那么其他需要数据库连接的工作就无法得到所需的数据库连接。
因此,最佳的数据库连接模式,是“每次请求,一次数据库连接”这样的使用模式。
因为,多次请求之间的时间间隔是无法预料的,可能长达几小时、甚至几天。数据库连接显然不能白白的等待在那里。而应该返回给数据库,或者数据库连接缓冲池,让其他程序和组件有机会使用数据库连接。
另外,如果一次数据库连接,小于一次用户请求,那么,数据库连接的得到和关闭次数又太频繁了。因为,得到一次数据库连接是非常消耗资源的。一次用户请求,是一个短时、瞬间的操作,完全没有必要使用多个数据库连接。
另外,上文中说过,事务是依托在数据库连接之上的。多个数据库连接之间,是无法使用同一个事务的。(实际上,JTA分布式事务是可以在一个事务中使用多个数据库连接的)
因此,我们更应该让数据库连接的生命周期尽可能的延长。
事务管理最佳模式
最佳的数据库连接模式,是“每次请求,一次数据库连接”这样的使用模式。事务,与之相仿。最佳的事务管理模式,也是“每次请求,一次数据库连接,一次事务”。
一次用户请求,是用户对软件系统功能的一次独立调用。用户当然不希望他的一次操作,系统只执行一部分这种情况的发生。因此,对一次用户请求的响应,使用一次事务,是非常和正确的。
对于一次单纯的查询操作,不更改持久化数据库中记录,那么我们不需要使用事务。在数据库操作发生错误时,抛出异常,让用户界面显示出问题即可。而对于更改数据库记录的操作,并且涉及到多次数据库操作的,则必须使用事务,以保证数据库中记录的完整性和真实性。
数据库连接和事务管理的反模式
数据库连接和事务管理,在应用中有一些反模式。我们应该避免这样做,否则会死得很惨!
一、数据库连接和事务管理跨越一个客户的多次请求
这样的数据库连接和事务,其持续时间是无法估量的。这样严重影响软件和数据库的性能。这是绝对不可取的。
二、每个数据库操作,一次数据库连接和事务
这是一种非常常见的反模式。在采用DAO设计模式进行O-R映射中,DAO接口的一个数据库访问方法,就执行一次数据库连接的获取和释放,并且执行一次或者多次事务。
如,下面的代码:
/*
4,删除单条消息
*/
publicvoid deleteMessage(String id){
Connection conn=DB.getConnection();
Statement stmt =null;
ResultSet rst=null;
try {
stmt = conn.createStatement();
//拼装SQL
String sql=”delete from message where id=''”+id+”''”;
stmt.executeUpdate(sql);
}
catch (SQLException ex) {
ex.printStackTrace();
thrownew DataAccessException();
}finally{
DB.freeDbResource(conn,stmt,rst);
}
}
这是典型的反模式。
数据库连接在Dao中得到和释放。如果一次用户请求需要用到多个Dao方法,那么就需要多次得到和释放数据库连接。造成了极大的浪费。而且,也无法对多个Dao方法实施事务管理。
另外,JDBC中,默认的事务管理方式是自动提交。上面的代码只有一个SQL执行语句。所有只有一次事务。如果Dao方法中有多个SQL语句,那么就会在一个Dao方法中使用多个事务,多次提交到数据库中,这也是极端错误的!
当然,上面这个简单的Dao方法,并不会造成任何实际的损害,这里仅仅说明这种使用方式是一种反模式。
事务管理的最佳设计模式
最佳的事务管理模式,是“每次请求,一次数据库连接,一次事务”。那么,根据这个原则,具体我们应该怎样编写程序呢?
一、事务管理的分层
企业级应用软件中的代码部分,可以分为以下几个层次:
(一)控制器Controller层
这是表现层的业务委派。它处理用户的请求,完成用户要求的功能。它接收用户传来的参数,然后调用业务层的服务方法,完成所需的功能。
根据“每次请求,一次数据库连接,一次事务”的原则。似乎,这里是最好的得到和关闭数据库连接,管理事务的地方。因为,Controller层中的每一个方法,对应着用户的一次请求。
但是,我认为,这里决不应该“得到和关闭数据库连接,管理事务”。因为,首先,控制器层,作为表现层技术的一部分,它的作用,仅仅是委派操作给业务层的服务方法,应该尽可能的小。不应该包括这些代码。
其次,管理数据库连接和事务,这是业务层的逻辑,应该在业务层处理,而不是在表现层处理。
更实际一点来说,Struts这种技术中,我们一般不使用Spring来管理事务。这样,如果“得到和关闭数据库连接,管理事务”放在Struts的控制器层—Action类中,那么Spring自动管理数据库连接和事务的声明式事务管理机制就无法使用了!
因此,我们应该坚决地拒绝在控制器层中处理数据库连接和事务的诱惑!
(二)业务服务Service层
业务服务层,是业务逻辑的实际存放地。它们提供的服务分为2种:
1,为控制器层提供服务,处理用户请求。
2,为其他类(不仅仅是控制器层,可能是其他Service方法等)提供服务。
传统上,大家都不区分这两类服务方法。统称为Service。
而在我的方法中,我把它们区分开来。
1,直接为控制器层提供服务,并且需要使用到数据库操作,从而需要处理数据库连接和事务的,我把它们成为Transaction方法。用*Transaction后缀标识。
这样的方法,我仍然把它们放在Service接口中。如果你需要实现这样的方法。看到后缀,你就知道,你需要在这里调用Dao方法,并且“得到和关闭数据库连接,管理事务”。
如果你不在这里进行“得到和关闭数据库连接,管理事务”的操作,那么系统一定会出现故障!
2,为其他类(可以是控制器层,也可能是其他Service方法等)提供服务,并且不需要访问数据库的方法。我称它们为Service方法。使用*Service后缀,或者不使用后缀来标识它们。
这样的方法,你可以无所顾忌的使用,既可以在控制器层中调用,也可以在任何代码中调用!
3,需要使用到数据库操作,并且不可以直接被控制器层调用的方法。我称它们为Dao方法。使用*Dao后缀来标识它们。
它们不是Dao接口中的方法,而是Service业务逻辑接口中的方法。我称它们为Dao方法,并不是说,它们是Dao接口的方法,而是表示它们是Service层中需要使用Dao接口操纵数据库的服务方法。并且,它们本身不含有“得到和关闭数据库连接,管理事务”的代码。因此,所有需要调用它们的方法,需要注意了,“得到和关闭数据库连接,管理事务”这些任务还没有做。如果直接在控制器层调用它们,那么一定会出现数据库和事务的错误!
(三)DAO数据访问层
DAO数据访问模式,是目前用的最到的模式。在DAO中,使用各类数据库访问技术(如,JDBC,iBatis,Hibernate等)操作数据库,实现O-R映射。
其中的方法,大都满足“需要使用到数据库操作,并且不可以直接被控制器层调用的方法”这样一种情况。我们可以使用*Dao后缀来标识这些方法,也可以不使用后缀。因为Dao接口的方法,大抵都是这类方法。
二、数据库连接和事务管理最佳模式
在我们的编程范式中,是这样工作的:
控制器层,接收用户请求参数,并委派给业务层的Service接口执行业务逻辑。它可以直接调用Service接口的*Transaction方法和*Service方法或者没有后缀的一般方法。
其中,*Transaction方法需要用到数据库。其中必然调用了业务层的Dao方法,或者DAO层的数据库访问方法。其实现方法中必然有处理“得到和关闭数据库连接,管理事务”的代码。
而*Service方法或者没有后缀的一般方法,则没有使用数据库。
在DAO数据访问层,执行数据库操作的DAO方法,并不需要创建和关闭数据库连接,也不需要处理事务。它们之需要得到数据库连接,然后使用这个连接即可。(数据库连接,可以通过参数从外部得到,也可以从本地线程变量中得到。后者是目前主流的技术)
这就是我提出的“事务管理最佳实践”的工作情况。
在Service业务层和DAO数据访问层中,我们都使用了“接口—实现类”相分离的设计模式。
一、编程方式的数据库连接和事务管理
假设,现在我们使用多种数据库访问技术,来进行O-R映射。看看我们这个架构的适应能力。
我们的系统,分别使用JDBC,iBatis,Hibernate这三种数据库访问技术,使用编程方式手工管理数据库连接和事务,不使用Spring这样的IOC容器进行管理。看看我们需要做什么:
(一)JDBC编程方式管理数据库连接和事务
首先,开发一个JDBCUtil类,得到数据库连接,并且把它们放在一个线程变量中,以便一个线程重用一个数据库连接。
然后,开发DAO接口的实现类。实现DAO方法。从本地线程变量中得到数据库连接,使用它。不需要关闭这个连接,也不需要管理事务。
接着,开发SeriVCe层的*Dao后缀命名的方法。它们只需要调用DAO接口的方法即可。不需要和数据库连接、事务打交道。
最后,开发Service层的*Transaction后缀命名的方法。它们调用JDBCUtil类的方法,创建一个数据库连接,并把它放在JDBCUtil类的本地线程变量中,设置conn.setAutoCommit(false);等待DAO接口的方法去取这个已经设为不自动提交的数据库连接。
然后,在Try块中,调用Dao方法(Service接口或者DAO接口的Dao方法)。调用结束之后,提交事务,并在异常处理模块中,设置回滚。最后,在finally块中关闭数据库连接,清除本地线程变量的值。
(二)iBatis编程方式管理数据库连接和事务
iBatis本身就是使用本地线程变量来管理数据库连接的。
1,DAO接口的实现方法中,调用iBatis代码,执行数据库操作。
2,Service层的Dao方法,不需要任何更改。
3,Service层的Transaction方法,需要使用iBatis的事务管理代码。
private SqlMapClient sqlMap = XMLSqlMaPBuilder.buildSqlMap(reader);
public updateItemDescriptionTransaction (String itemId, String newDescription) throws SQLException {
try {
sqlMap.startTransaction ();
dao方法调用;
sqlMap.commitTransaction ();
} finally {
sqlMap.endTransaction ();
}
}
iBatis处理事务的代码,也处理得数据库连接。并且,事务的回滚也被iBatis搞定了。
也就是说,换了一种数据库访问技术,只需要改变Service层中*Transaction方法的实现和DAO层的实现。
(三)Hibernate编程方式管理数据库连接和事务
Hibernate也是如此。
下面是Hibernate的助手类:
publicclass HibernateSessionFactoryFromJbpm {
privatestaticfinal ThreadLocal threadLocal = new ThreadLocal();
privatestatic org.hibernate.SessionFactory sessionFactory;
/**
*ReturnstheThreadLocalSessioninstance. Lazyinitialize
*the<code>SessionFactory</code>ifneeded.
*
* @returnSession
* @throwsHibernateException
*/
publicstatic Session getSession() throws HibernateException {
Session session = (Session) threadLocal.get();
if (session == null || !session.isOpen()) {
if (sessionFactory == null) {
rebuildSessionFactory();
}
session = (sessionFactory != null) ? sessionFactory.openSession()
: null;
threadLocal.set(session);
}
return session;
}
/**
* Rebuildhibernatesessionfactory
*
*/
publicstaticvoid rebuildSessionFactory() {
try {
// configuration.configure(configFile);
//sessionFactory = configuration.buildSessionFactory();
sessionFactory =HibernateHelper.createSessionFactory();
} catch (Exception e) {
System.err
.println(”%%%% Error Creating SessionFactory %%%%”);
e.printStackTrace();
}
}
/**
* Closethesinglehibernatesessioninstance.
*
* @throwsHibernateException
*/
publicstaticvoid closeSession() throws HibernateException {
Session session = (Session) threadLocal.get();
threadLocal.set(null);
if (session != null) {
session.close();
}
}
}
Hibernate的Session,是对JDBC Connection的封装。Hibernate不同于JDBC和iBatis。它默认就把自动提交设为false。也就是说,如果你不显式的使用Hiberante事务,那么根本不会操作数据库!这点需要注意。
(四)Jbpm对Hiberante的封装
另外,再说一下Jbpm对Hiberante所作的封装。Jbpm使用的是Hiberante3的数据库访问技术。但是,它对Hibernate进行了封装。
使用Jbpm,事务管理更加简单。
如:
public List getAllCanSeenTaskInstancesTransaction (PageModule view,String userId) throws Exception {
JbpmContext jbpmContext = JbpmConfiguration.getInstance().createJbpmContext();
try {
returnthis.getAllCanSeenTaskInstances(view, userId);
}finally{
jbpmContext.close();
}
}
jbpmContext.close();方法执行时,自动提交事务。如果发生异常,自动回滚。并且,最后会关闭Hiberante本地线程中的Session,并清空该线程变量。
二、声明方式的数据库连接和事务管理
Spring容器管理业务代码和DAO数据访问代码,是现在非常常用的一种方式。使用Spring时,我们一般使用Spring声明式事务来管理数据库连接和事务。
另外,还有EJB容器也有声明式事务管理的机制,两者的使用方法大体相同,我就不再论述,这里只说Spring。
Spring管理下的JDBC,iBatis,Hibernate数据库访问方法。我们在DAO接口的实现类中,可以使用Spring提供的助手类的便利方法,进行数据库操作。也可以使用Spring提供的助手类,得到Connection,Session等进行数据库操作。或者使用Spring助手类的execute()方法调用数据库操作代码。
如果你原先使用自己的助手类得到Connection,Session。那么你完全可以修改该助手类的实现方法,改为从Spring得到Connection,Session。这样就不需要修改DAO接口的实现类!
Service层中的Dao方法,仍然无需修改。
对于Service层中的Transaction方法。我们需要去除“得到和关闭数据库连接,管理事务”的代码。然后,在Spring的配置文件中,对其应用声明式事务管理。运行时,Spring会通过SpringAOP技术,自动得到数据库连接,管理事务。
可见,使用声明式事务管理,我们只需要修改得到数据库连接或者会话的Util助手类,以及Transaction方法即可。
综上所述,可以看到,我提出的这一套事务管理最佳实践是一套非常灵活、强大、简洁的管理事务的最佳实践。具有极其强大的适应能力。采用这套编程范式,你可以很容易地彻底摆脱事务管理带来的困扰!

 

分享到:
评论

相关推荐

    事务管理最佳实践全面解析

    事务管理的最佳实践之一是采用"每次请求,一次数据库连接,一次事务"的模式。这意味着每个用户请求都应该在一个单独的事务中完成,以确保该请求的完整性和一致性。对于只读查询,无需事务,但如果涉及数据库记录的...

    廖雪峰 Spring 2.0 核心技术与最佳实践 高清扫描版

    《Spring 2.0 核心技术与最佳实践》是由知名IT教育家廖雪峰编写的教程,旨在为从初学者到高级工程师提供全面而深入的Spring 2.0框架理解与应用指导。Spring框架是Java开发中的核心工具,尤其在企业级应用中广泛使用...

    秦金卫-转型求通-微服务架构最佳实践和发展趋势.pdf

    秦金卫在"转型求通-微服务架构最佳实践和发展趋势"中,深入探讨了微服务在金融行业的落地案例、最佳实践原则以及未来的发展趋势。 首先,金融行业的IT发展阶段经历了从电子化信息化、网络化移动化到数字化智能化的...

    MySQL面试全面解析手册

    MySQL面试全面解析手册是一本专为MySQL开发和面试准备而设计的综合性资源。该手册详细介绍了MySQL数据库的基本概念、SQL语法、常见查询优化技巧以及高级主题如索引优化、事务处理和复制等。通过系统化的知识点总结、...

    微服务架构深度解析与最佳实践.docx

    微服务架构是一种现代软件开发的方法论,它将大型复杂应用分解为一...然而,实施微服务需要全面考虑其带来的挑战,并采取相应的最佳实践策略,以确保系统的稳定性和可靠性。在实际应用中,不断学习和调整是成功的关键。

    wcf全面解析 下册

    同时,WCF下册对于WCF中经常遇到的问题和最佳实践也有深入的探讨,比如性能优化、服务版本控制、错误处理和日志记录等。 对于希望深入理解和运用WCF的IT专业人员来说,WCF全面解析下册无疑是一份宝贵的资源。它不仅...

    全面解析_SQLite

    ### 全面解析 SQLite #### 一、SQLite 快速...以上内容仅是《全面解析 SQLite.pdf》文件的部分概括,该文档提供了更详尽的技术细节和实践案例,非常适合想要深入了解 SQLite 数据库管理系统的开发者和技术人员参考。

    藏经阁-Apache Dubbo3 云原生升级与企业最佳实践-128.pdf

    - **精进云原生 - Dubbo & Kubernetes 最佳实践**:分享了如何结合Dubbo和Kubernetes进行微服务的部署和管理,以实现更高效、弹性的云原生架构。 - **政采云基于 Dubbo 的混合云数据跨网实践**:展示了在混合云环境...

    WCF全面解析

    在《WCF全面解析完(卷一)整版.pdf》中,读者将全面了解WCF的各个层面,从基础概念到高级特性和最佳实践,为成为WCF开发专家奠定坚实基础。这本书不仅适合初学者入门,也对有经验的开发者提供了深入的技术指导。

    史上最强全面解析SQLite

    ### 史上最强全面解析SQLite #### 使用SQLite编程的快速介绍 SQLite是一种轻量级的数据库,被广泛用于各种应用程序中。它具有嵌入式的特性,这意味着它不需要一个单独的服务器进程;相反,它可以被直接链接到应用...

    04-Mycat最佳实践.pdf

    最佳实践指的是通过一系列合理操作来达到最优化、最有效果的使用方法。下面我们将从多个方面详细介绍Mycat的最佳实践,包括实施指南、后端存储选择、以及Mycat存在的限制。 ### Mycat实施指南 #### 了解Mycat能力 ...

    MySQL性能优化的21个最佳实践.zip

    本文件“MySQL性能优化的21个最佳实践.zip”提供了一套全面的指南,旨在帮助开发者和DBA提升MySQL的运行效率。以下是对这些最佳实践的详细解释: 1. **选择合适的数据类型**:每个字段的数据类型都应精确匹配其存储...

    MySQL面试题:从基础到进阶全面解析

    9. 经验分享:总结在使用MySQL过程中的心得,如最佳实践、陷阱和避免错误的方法。 10. 兴趣点:阐述对MySQL特定方面的兴趣,如复制、分区或新特性。 通过全面掌握这些知识点,求职者能够在面试中展示出对MySQL的...

    2013年系统集成项目管理工程师重点考点难点暨历年真题解析.pdf

    - **信息系统服务管理**:涉及IT服务管理的最佳实践。 - **信息系统集成专业技术知识**:包括软件开发、网络构建等方面的专业知识。 - **云计算、物联网、4G、虚拟化等新技术**:掌握这些新兴技术的基本原理和应用...

    spring技术内幕第2版深入解析spring架构与设计原理

    尽管给出的文件信息没有提供实际的技术细节,但可以明确的是,《Spring技术内幕第2版》这本书必定会围绕Spring框架的设计哲学、组件结构、架构模式以及最佳实践等方面展开深入讨论,旨在帮助开发者不仅了解如何使用...

    Spring技术内幕:深入解析Spring架构与设计原理

    本书可能会深入讲解Spring事务管理的工作原理和最佳实践。 除了上述核心概念之外,Spring还涉及了众多模块如Spring MVC用于构建Web应用程序,Spring Data简化数据访问层开发,Spring Boot简化Spring应用的配置和...

    spring 源代码解析.zip

    《Spring源代码解析》 Spring框架作为Java领域最流行的开源框架之一,它的设计思想和实现方式一直是...通过对Spring源代码的学习,我们可以深入了解其设计模式和最佳实践,这对于成为一名优秀的Java开发者至关重要。

    深度解析Java游戏服务器开发源代码

    《深度解析Java游戏服务器开发源代码》是一本专注于Java技术在游戏服务器开发领域的专著,旨在帮助读者深入了解和...通过这本书,读者不仅可以提升自己的编程技能,还能掌握构建高性能游戏服务器的关键技术和最佳实践。

    【面试资料】-(机构内训资料)MySQL性能优化的21个最佳实践.zip

    这份【面试资料】-(机构内训资料)MySQL性能优化的21个最佳实践,旨在提供一套全面的指南,帮助数据库管理员和开发人员提升MySQL的运行效率,确保系统在高并发和大数据量场景下的稳定性和速度。 1. **选择合适的...

    学生籍贯管理系统,基于Java SSM框架开发(spring,spring mvc,mybatis)

    总的来说,这个学生籍贯管理系统通过Java SSM框架实现了高效、稳定和可扩展的后端功能,充分体现了现代Web应用开发的最佳实践。对于学习和理解SSM框架的集成运用,以及如何构建一个完整的管理信息系统,这个项目提供...

Global site tag (gtag.js) - Google Analytics