事务策略之事务模型.docx

上传人:b****0 文档编号:12761544 上传时间:2023-04-21 格式:DOCX 页数:15 大小:23.70KB
下载 相关 举报
事务策略之事务模型.docx_第1页
第1页 / 共15页
事务策略之事务模型.docx_第2页
第2页 / 共15页
事务策略之事务模型.docx_第3页
第3页 / 共15页
事务策略之事务模型.docx_第4页
第4页 / 共15页
事务策略之事务模型.docx_第5页
第5页 / 共15页
点击查看更多>>
下载资源
资源描述

事务策略之事务模型.docx

《事务策略之事务模型.docx》由会员分享,可在线阅读,更多相关《事务策略之事务模型.docx(15页珍藏版)》请在冰豆网上搜索。

事务策略之事务模型.docx

事务策略之事务模型

开发人员、设计人员和架构师经常会混淆事务模型与事务策略。

我经常会让与客户接触的架构师和技术总监描述他们项目的事务策略。

我通常会获得三种回应。

有时,他们会说“我们实际上并未在应用程序中使用事务。

”另一些时候,我会听到迷惑的回答:

“我不明白你的意思。

”但是,我也会遇到非常自信的回答:

“我们使用声明式事务。

”在本文中,术语声明式事务描述的是一个事务模型,但它绝不是一种事务策略。

关于本系列

事务将改善您数据的质量、完整性以及一致性,并且让您的应用程序更加可靠。

在Java应用程序中实现成功的事务处理并不是一项轻松的任务,它是一项与编写代码相关的设计工作。

在这个新的系列中,MarkRichards将指导您设计一个有效的事务策略,它适用于各种用例,从简单的应用程序到高级性能事务处理。

Java平台支持的三种事务模型包括:

∙LocalTransaction模型

∙ProgrammaticTransaction模型

∙DeclarativeTransaction模型

这些模型描述事务在Java平台中的基本运行方式,以及它们是如何实现的。

但是,它们仅提供了事务处理的规则和语义。

如何应用事务模型则完全由您决定。

举例来说,应该如何在REQUIRED和MANDATORY事务属性之间做出选择?

您应该在何时何种情况下指定事务回滚指令?

您应该在何时考虑ProgrammaticTransaction模型与DeclarativeTransaction模型的优劣?

您应该如何优化高性能系统的事务?

事务模型本身无法回答这些问题。

您必须通过开发自己的事务策略或采用本文介绍的四种主要事务策略之一来解决它们。

如本系列的第一篇文章所述,许多常见的事务陷阱都会影响到事务行为,并且由此会降低数据的完整性和一致性。

同样,缺乏有效的(或任何)事务策略将对您数据的完整性和一致性造成负面影响。

本文所描述的事务模型是开发有效事务策略的基本元素。

理解这些模型之间的差异以及它们的运行方式对于理解使用它们的事务策略非常重要。

在介绍完三种事务模型之后,我将讨论适用于大多数业务应用程序(从简单的Web应用程序到大型的高速事务处理系统)的四种事务策略。

事务策略系列的后续文章将详细讨论这些策略。

LocalTransaction模型

在LocalTransaction模型中,事务由底层数据库资源管理程序(而非应用程序所在的容器或框架)管理,这便是它得名的原因。

在这个模型中,您将管理连接,而不是事务。

从“了解事务陷阱”一文可知,在使用对象关系映射框架,如Hibernate、TopLink或theJavaPersistenceAPI(JPA),执行数据库更新时,您不能使用LocalTransaction模型。

在使用数据访问对象(DAO)或基于JDBC的框架和数据库存储过程时,您可以使用它。

您可以采用以下两种方式来使用LocalTransaction模型:

让数据库来管理连接,或者以编程的方式管理连接。

要让数据库管理连接,您需要将JDBCConnection对象上的autoCommit属性设置为true(默认值),这将通知底层数据库管理系统(DBMS)在完成插入、更新或删除操作之后提交事务,或者在操作失败时返回任务。

清单1展示了这种技巧,它在TRADE表中插入了一条股票交易命令:

清单1.包括一个更新的本地事务

publicclassTradingServiceImpl{

publicvoidprocessTrade(TradeDatatrade)throwsException{

ConnectiondbConnection=null;

try{

DataSourceds=(DataSource)

(newInitialContext()).lookup("jdbc/MasterDS");

dbConnection=ds.getConnection();

dbConnection.setAutoCommit(true);

Statementsql=dbConnection.createStatement();

Stringstmt="insertintoTRADE...";

sql.executeUpdate(stmt1);

}finally{

if(dbConnection!

=null)

dbConnection.close();

}

}

}

注意,在清单1中,autoCommit值设置为true,这将指示DBMS应该在各数据库语句之后提交本地事务。

如果在逻辑工作单元(LUW)中维护一个数据库活动,那么这一技巧将非常有用。

但是,假设清单1中的processTrade()方法还将更新ACCT表中的余额以反映交易订单的值。

在本例中,两个数据库操作是相互独立的,针对TRADE表的插入操作将在更新ACCT表之后提交给数据库。

若ACCT表更新失败,没有任何机制可以回滚到对TRADE表的更新操作,从而造成数据库中的数据不一致。

此场景又引出了第二个技巧:

以编程的方式管理连接。

在此技巧中,您将Connection对象上的autoCommit属性设置为false,并手动提交或回滚连接。

清单2演示了此技巧:

清单2.包括多个更新的本地事务

publicclassTradingServiceImpl{

publicvoidprocessTrade(TradeDatatrade)throwsException{

ConnectiondbConnection=null;

try{

DataSourceds=(DataSource)

(newInitialContext()).lookup("jdbc/MasterDS");

dbConnection=ds.getConnection();

dbConnection.setAutoCommit(false);

Statementsql=dbConnection.createStatement();

Stringstmt1="insertintoTRADE...";

sql.executeUpdate(stmt1);

Stringstmt2="updateACCTsetbalance...";

sql.executeUpdate(stmt2);

dbCmit();

}catch(Exceptionup){

dbConnection.rollback();

throwup;

}finally{

if(dbConnection!

=null)

dbConnection.close();

}

}

}

注意,在清单2中,autoCommit属性设置为false,这将通知底层DBMS在代码而非数据库中管理连接。

在本例中,如果一切正常,您必须调用Connection对象上的commit()方法;否则,如果出现异常,则调用rollback()方法。

通过这种方式,您可以在相同的工作单元中协调两个数据库活动。

虽然LocalTransaction模型现在看来有些过时,但它是本文结束部分介绍的一个主要事务策略的重要元素。

ProgrammaticTransaction模型

ProgrammaticTransaction模型的名称来自这样一个事实:

开发人负责管理事务。

在ProgrammaticTransaction模型中,与LocalTransaction模型不同,您将管理事务,并且将与底层数据库连接相分离。

与清单2中的示例相似,借助此模型,开发人员将负责从事务管理程序获取一个事务,启动该事务,提交事务,以及(如果出现异常)回滚到事务。

您可能已经猜到,这会产生大量易于出错的代码,从而对应用程序中的业务逻辑造成影响。

但是,一些事务策略需要使用ProgrammaticTransaction模型。

虽然概念是相同的是,但ProgrammaticTransaction模型的实现在SpringFramework和EJB3.0规范中是不同的。

我将先使用EJB3.0来演示这个模型的实现,然后使用SpringFramework来展示相同的数据库更新。

使用EJB3.0实现编程事务

在EJB3.0中,您将从事务管理程序(换句话说,容器)获取一个事务,方法是对javax.transaction.UserTransaction执行JavaNamingandDirectoryInterface(JNDI)查找。

获取UserTransaction之后,您可以调用begin()方法来启动事务,调用commit()方法来提交事务,并且调用rollback()方法以便在出现错误时回滚事务。

在此模型中,容器不会自动提交或回滚事务;开发人员需要自己在执行数据库更新的方法编写此行为。

清单3通过JPA展示了使用EJB3.0的ProgrammaticTransaction模型:

清单3.使用EJB3.0实现的编程事务

@Stateless

@TransactionManagement(TransactionManagementType.BEAN)

publicclassTradingServiceImplimplementsTradingService{

@PersistenceContext(unitName="trading")EntityManagerem;

publicvoidprocessTrade(TradeDatatrade)throwsException{

InitialContextctx=newInitialContext();

UserTransactiontxn=(UserTransaction)ctx.lookup("UserTransaction");

try{

txn.begin();

em.persist(trade);

AcctDataacct=em.find(AcctData.class,trade.getAcctId());

doubletradeValue=trade.getPrice()*trade.getShares();

doublecurrentBalance=acct.getBalance();

if(trade.getAction().equals("BUY")){

acct.setBalance(currentBalance-tradeValue);

}else{

acct.setBalance(currentBalance+tradeValue);

}

mit();

}catch(Exceptionup){

txn.rollback();

throwup;

}

}

}

在带无状态会话bean的JavaEE容器环境中使用ProgrammaticTransaction模型时,您必须通知容器您正在使用编程事务。

为此,您需要使用@TransactionManagement注释并将事务类型设置为BEAN。

如果您未使用此注释,则容器将假定您使用声明式事务管理(CONTAINER),它是EJB3.0的默认事务类型。

在无状态会话bean上下文外部的客户机层中使用编程事务时,您不需要设置事务类型。

使用Spring实现编程事务

SpringFramework提供了两种实现ProgrammaticTransaction模型的方法。

一种是通过SpringTransactionTemplate来实现,另一种是直接使用Spring平台事务管理程序。

由于我并不热衷于编写匿名内部类和难以理解的代码,因此我将使用第二种技巧来演示Spring中的ProgrammaticTransaction模型。

Spring至少有九种平台事务管理程序。

最常用的包括DataSourceTransactionManager、HibernateTransactionManager、JpaTransactionManager和JtaTransactionManager。

我的代码示例使用的是JPA,因此我将展示JpaTransactionManager的配置。

要在Spring中配置JpaTransactionManager,只需要使用org.springframework.orm.jpa.JpaTransactionManager类在应用程序上下文XML文件中定义bean,并添加一个到JPAEntityManagerFactorybean的引用。

然后,假定包含应用程序逻辑的类是由Spring管理的,将事务管理程序注入到bean中,如清单4所示:

清单4.定义SpringJPA事务管理程序

class="org.springframework.orm.jpa.JpaTransactionManager">

如果Spring未管理应用程序类,那么您可以在您的方法中引用事务管理程序,方法是在Spring上下文中使用getBean()方法。

在源代码中,您现在可以使用平台管理程序获取一个事务。

执行了所有更新之后,您可以调用commit()方法来提交事务,或者调用rollback()方法来回滚事务。

清单5展示了此技巧:

清单5.使用SpringJPA事务管理程序

publicclassTradingServiceImpl{

@PersistenceContext(unitName="trading")EntityManagerem;

JpaTransactionManagertxnManager=null;

publicvoidsetTxnManager(JpaTransactionManagermgr){

txnManager=mgr;

}

publicvoidprocessTrade(TradeDatatrade)throwsException{

TransactionStatusstatus=

txnManager.getTransaction(newDefaultTransactionDefinition());

try{

em.persist(trade);

AcctDataacct=em.find(AcctData.class,trade.getAcctId());

doubletradeValue=trade.getPrice()*trade.getShares();

doublecurrentBalance=acct.getBalance();

if(trade.getAction().equals("BUY")){

acct.setBalance(currentBalance-tradeValue);

}else{

acct.setBalance(currentBalance+tradeValue);

}

txnMmit(status);

}catch(Exceptionup){

txnManager.rollback(status);

throwup;

}

}

}

注意清单5中SpringFramework与EJB3.0之间的差异。

在Spring中,获取事务(随后启动它)的方法是在平台事务管理程序上调用getTransaction()。

匿名DefaultTransactionDefinition类包含关于事务及其行为的详细信息,包括事务名称、隔离级别、传播模式(事务属性)和事务超时(如果存在)。

在本例中,我可以仅使用默认值,其名称是空字符串,底层DBMS的默认的隔离级别通常为READ_COMMITTED,事务属性是PROPAGATION_REQUIRED,以及DBMS的默认超时。

还需注意,commit()和rollback()方法是使用平台事务管理程序调用的,而不是事务(与DJB的情况相同)。

DeclarativeTransaction模型

DeclarativeTransaction模型,又称作ContainerManagedTransactions(CMT),是Java平台中最常用的事务模型。

在该模型中,容器环境负责启动、提交和回滚事务。

开发人员仅负责指定事务的行为。

本系列的第一篇文章所讨论的大多数事务陷阱都与DeclarativeTransaction模型相关。

SpringFramework和EJB3.0都利用注释来指定事务行为。

Spring使用@Transactional注释,而EJB3.0使用@TransactionAttribute注释。

在使用DeclarativeTransaction模型时,容器将不会针对检测到的异常自动回滚事务。

开发人员必须指定出现异常时在何处以及何时回滚事务。

在SpringFramework中,您通过使用@Transactional注释上的rollbackFor属性来指定它。

在EJB中,您通过调用SessionContext上的setRollbackOnly()方法来指定它。

清单6展示了DeclarativeTransaction模型的EJB应用:

清单6.使用EJB3.0的声明式事务

@Stateless

publicclassTradingServiceImplimplementsTradingService{

@PersistenceContext(unitName="trading")EntityManagerem;

@ResourceSessionContextctx;

@TransactionAttribute(TransactionAttributeType.REQUIRED)

publicvoidprocessTrade(TradeDatatrade)throwsException{

try{

em.persist(trade);

AcctDataacct=em.find(AcctData.class,trade.getAcctId());

doubletradeValue=trade.getPrice()*trade.getShares();

doublecurrentBalance=acct.getBalance();

if(trade.getAction().equals("BUY")){

acct.setBalance(currentBalance-tradeValue);

}else{

acct.setBalance(currentBalance+tradeValue);

}

}catch(Exceptionup){

ctx.setRollbackOnly();

throwup;

}

}

}

清单7演示了DeclarativeTransaction模型的SpringFramework应用:

清单7.使用Spring的声明式事务

publicclassTradingServiceImpl{

@PersistenceContext(unitName="trading")EntityManagerem;

@Transactional(propagation=Propagation.REQUIRED,

rollbackFor=Exception.class)

publicvoidprocessTrade(TradeDatatrade)throwsException{

em.persist(trade);

AcctDataacct=em.find(AcctData.class,trade.getAcctId());

doubletradeValue=trade.getPrice()*trade.getShares();

doublecurrentBalance=acct.getBalance();

if(trade.getAction().equals("BUY")){

acct.setBalance(currentBalance-tradeValue);

}else{

acct.setBalance(currentBalance+tradeValue);

}

}

}

事务属性

除了回滚指令之外,您还必须指定事务属性,这将定义事务的行为。

Java平台支持六种事务属性,而与您使用的是EJB还是SpringFramework无关:

∙Required

∙Mandatory

∙RequiresNew

∙Supports

∙NotSupported

∙Never

在描述这些事务属性时,我将使用一个假想的methodA()方法,事务属性将应用于这个方法。

如果为methodA()指定了Required事务属性,并且在已有事务作用域内调用了methodA(),那么将使用已有的事务作用域。

否则,methodA()将启动一个新的事务。

如果事务是由methodA()启动的,则它也必须由methodA()来终止(提交或回滚)。

这是最常用的事务属性,并且是EJB3.0和Spring的默认属性。

遗憾的是,它的使用并不正确,从而造成了数据完整性和一致性问题。

对于本系列后续文章将要讨论的各事务策略,我将更加详细地阐述这个事务属性。

如果为methodA()指定了Mandatory事务属性,并且在已有事务作用域内调用了metho

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 小学教育 > 其它课程

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1