事务模型
3种事务模型
本地事务模型
本地事务模型的名称来⾃于它实际上不是管理事务的框架,⽽是本地资源管理器。资源管理器是与之通信的数据源的实际提供者。例如,对于数据库,资源管理器是通过数据库驱动程序和DBMS实现的。对于JMS,资源管理器是通过特定的JMS提供程序实现的队列(或主题)连接⼯⼚。使⽤本地事务模型,开发⼈员管理连接,⽽不是事务。实际管理本地事务的是DBMS或JMS提供程序。关于本地事务,事实是事务管理由底层数据库(DBMS)处理,如果是jms,则由底层消息传递提供程序处理。从开发⼈员的⾓度来看,我们不管理本地事务模型中的事务,⽽是管理连接。下⾯的代码⽰例演⽰了使⽤直接JDBC代码的本地事务模型的使⽤:
public void updateTradeOrder(TradeOrderData order)
throws Exception {
DataSource ds = (DataSource) (new InitialContext()).lookup(
"jdbc/MasterDS");
Connection conn = ds.getConnection();
conn.setAutoCommit(false);
Statement stmt = ateStatement();
String sql = "update trade_order ... ";
try {
connmit();
} catch (Exception e) {
throw e;
} finally {
stmt.close();
conn.close();
}
}
注意,在上⾯的⽰例中,使⽤connection.setautoCOMMIT(False)和connection.COMMIT()和connection.ROLLBACK()⽅法。setautoCOMMIT()⽅法是整个基于开发⼈员的连接管理的⼀个⾮常重要的部分。auto commit flag
告诉底层DBMS是否应该在执⾏每个SQL语句后⽴即提交连接。⼀个true值告诉DBMS在每个SQL语句执⾏后⽴即提交或回滚连接,⽽⼀个false值将保持连接活动,并且在执⾏显式commit()之前不提交更改。默认情况下,此标志通常设置为true。因此,默认情况下,如果我们有多个SQL更新,它们中的每⼀个都将被独⽴地执⾏和提交,并且connection.COMM T()和connection.Rollback()语句将被忽略。
开放源代码意味着什么对于Spring框架中的低级jdbc编码,您只需使⽤org.springframework.jdbc.datasource.datasourceutils,如下⾯的编码⽰例所⽰:
Connection conn = Connection(dataSource);
conn.setAutoCommit(false);
Statement stmt = ateStatement();
String sql = "update trade_order ... ";
try {
connmit();
} catch (Exception e) {
throw e;
} finally {
stmt.close();
conn.close();
}
}
在Spring中,数据源和相应的业务对象将在Spring配置⽂件中定义如下:
<bean id="datasource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jdbc/MasterDS"/>
</bean>
<bean id="TradingService" class="ading.server.TradingService">
<property name="dataSource">
<ref local="datasource"/>
</property>
</bean>
本地事务局限性
对于简单的更新和⼩的应⽤程序,本地事务模型⼯作得很好。然⽽,⼀旦我们给我们的应⽤程序增加了⼀些复杂性,这个模型就会崩溃。这个模型有⼏个限制,可能会对您的应⽤程序体系结构造成严重限制。
本地事务模型的第⼀个问题是,在编码连接逻辑时,开发⼈员有⾜够的空间出错。开发⼈员必须⾮常密切地注意⾃动提交标志设置,特别是在同⼀⽅法中进⾏多次更新时。此外,开发⼈员必须始终意识到正在调⽤的⽅法,以及这些⽅法是否管理连接。除⾮您有⼀个简单的应⽤程序,⼤多是单表更新,否则没有有效的⽅法。
本地事务模型的另⼀个问题是,当使⽤xa全局事务协调多个资源时,本地事务不能同时存在(我们将在第5章中更详细地看到这⼀点)。当协调多个资源时,如数据库和jms⽬的地(即。队列或主题)您不能使⽤本地事务模型,并且仍然保持ACID属性。考虑到这些约束和限制,本地事务模型应该只⽤于简单的基于Web的java应⽤程序,这些应⽤程序具有简单的表更新。
考虑到这些约束和限制,本地事务模型应该只⽤于简单的基于Web的java应⽤程序,这些应⽤程序具有简单的表更新。
编程式事务模型
借助javaee定义的统⼀的api,即JTA来管理事务。避免了由于本地事务模型的多样性(多实现的复杂性)带来的限制,如本地事务中的DB driver的不同实现,DBMS的不同,JMS的多样性实现等等。编程式事务模型和本地事务模型最⼤的不同就是,编程式事务模型管理的是事务,⽽不是连接。也即是,编程式封装了连接的逻辑
UserTransaction txn = UserTransaction();
txn.begin();
try {
TradeOrderDAO dao = new TradeOrderDAO();
dao.updateTradeOrder(order);
txnmit();
} catch (Exception e) {
log.fatal(e);
throw e;
}
}
借助于编程式事务,开发者管理的是事务,⽽不是连接。开发者使⽤ansaction.UserTransaction接⼝的begin()⽅法来创建关联当前线程的事务,使⽤commit()和rollback()来提交和回滚
在使⽤编程事务时,开发⼈员必须密切注意事务管理,因为它涉及异常处理。开发⼈员必须确保事务始终在启动事务的⽅法中终⽌。这往往⽐听起来的困难多⼀倍,特别是对于具有复杂异常处理的⼤型、复杂的应⽤程序。
只有当您有很好的理由使⽤编程事务模型时,才应该使⽤该事务模型。有三个很好的原因是客户端发起的事务、本地化的JTA事务和长期运⾏的事务。除了这些场景之外,您应该使⽤声明性事务模型。
声明式事务模型
我们在编程事务模型中看到,开发⼈员必须使⽤BEGIN()、COMMIT()和ROLLBACK()⽅法显式启动事务并提交或回滚事务。
使⽤声明性事务模型,容器管理事务,这意味着开发⼈员不必编写java代码来启动或提交事务。但是,开发⼈员必须告诉容器如何管理事务。这是通过l部署描述符中的XML配置设置和特定于每个应⽤服务器(EJB)的扩展部署描述符,或者在
声明性事务模型,也称为EJB世界中的容器管理事务(或CMT)。使⽤声明式模型,框架或容器管理事务的开始和停⽌(即提交或回滚)。开发⼈员只需要告诉框架什么时候在应⽤程序异常上回滚事务,并通过EJB中的XML部署描述符(例如l)或spring的bean定义⽂件(例如l)中的配置参数配置事务。开发者负责事务的开始和结束,在EJB中,这是通过UserTransactionManager接⼝完成的。使⽤start()⽅法启动事务,使⽤commit()或rollback()⽅法终⽌事务。在spring中,这是通过使⽤TransactionTemplate 或通过位于ansaction包中的平台事务管理器来完成的。
此图是根据springframework api整理
不管您使⽤的是什么框架,⼤多数企业级java应⽤程序都会利⽤java事务api(jta)进⾏事务管理。JTA是开发⼈员⽤来管理事务的接⼝。另⼀⽅⾯,java事务服务(J TS)是实现JTA的底层事务服务,被⼤多数商业和开放源应⽤服务器使⽤(请注意,除了可以使⽤的JTS之外,市场上还有其他事务服务)。认为JTA和JTS之间的关系类似于JDBC与相应的底层数据库驱动程序之间的关系;JTA对JDBC就像JTS 对数据库驱动程序⼀样。该JTA可以通过商业应⽤服务器或开源事务管理器实现。
Java事务服务(Jts)是CORBA ots1.1规范(对象事务服务)的Java语⾔映射。作为⼀名开发⼈员,这并不是⾮常重要的,除⾮你是在玩⼀些古怪版本的琐碎追求或在⼀个真正艰难的⼯作⾯试。虽然J2EE没有强制
使⽤jts,但对于异构实现之间的分布式事务的互操作性来说,jts是强制性的。由于JTA必须同时⽀持jts和⾮jts实现,所以仅仅通过查看JTA接⼝就很难知道实现所⽀持的确切功能。例如,尽管jts规范对嵌套事务具有可选的⽀持,但J2EE不⽀持此特性。
幸运的是,开发⼈员在处理事务时需要关注的接⼝不多。例如,在使⽤编程事务时,我们需要使⽤的惟⼀接⼝是
对于javax.Transaction包提供的接⼝,我们可以做更多的⼯作,但是开发⼈员不会经常使⽤这些接⼝。例如,我们将在本书中进⼀步看到,有时可能需要直接访问事务管理器。当我们使⽤声明性事务时需要⼿动挂起或恢复事务时,就会出现这些情况。我们还可以使⽤TransactionManager接⼝启动、提交和回滚事务。
⽤户事务接⼝仅在编程事务模型中使⽤,主要在EJB中。这个接⼝中开发⼈员需要关注的惟⼀⽅法是:
·BEGIN()
·COMMIT()
·ROLLBACK()
·getStatus()
切,transactionManager接⼝也能做。但是,对于⼤多数⽅法,最好使⽤usertransaction接⼝,除⾮需要暂停或恢复事务,否则不要使⽤transactionManager接⼝。
在声明性事务模型或编程事务模型中,使⽤悬空()⽅法来挂起与当前线程关联的事务。此⽅法返回对当前事务的引⽤,如果没有与当前线程关联的事务,则返回NULL。如果需要挂起当前事务以执⾏与xa不兼容的代码或存储过程,则此⽅法⾮常有⽤。我们将在书的第五章中看到使⽤这种⽅法的⼀个例⼦。
在声明性或编程性事务模型中使⽤resume()⽅法来恢复先前挂起的事务。它以对先前挂起的事务的引⽤作为参数,将该事务与当前线程关联,然后继续该事务。
在声明性事务模型中使⽤setrollbackonly()⽅法通知容器,当前事务的唯⼀可能结果是回滚。这个⽅法的有趣之处在于,它实际上不会在调⽤时回滚事务;它只会标记事务回滚,此结果是在调⽤getStatus()时会返回⼀个STATUS_MARKED_ROLLBACK状态。
同样的结果也可以通过TransactionManager.setRollbackOnly()⽅法来实现,但是由于我们已经有了SessionContext或MessageDriveContext,所以使⽤这个⽅法是有意义的。
Status Interface
正如我们在上⼀节中所看到的,我们可以通过ansaction.Status接⼝获得事务的状态,这是从status()⽅法获得的值的结果。我专门⽤⼀节来讨论事务状态,因为⾸先,它有点酷,其次,它为我们提供了许多关于当前事务状态的有⽤信息。以下值包含在状态接⼝中:
· STATUS_ACTIVE
· STATUS_COMMITTED
· STATUS_COMMITTING
· STATUS_MARKED_ROLLBACK
· STATUS_NO_TRANSACTION
· STATUS_PREPARED
· STATUS_PREPARING
· STATUS_ROLLEDBACK
· STATUS_ROLLING_BACK
· STATUS_UNKNOWN
在上⾯列出的所有状态值中,在⼤多数主流java业务应⽤程序中,唯⼀对开发⼈员真正有⽤的是status_active、status_mark_rollback和status_no_transaction。
STATUS_ACTIVE
there may be times when it is important to see if a current transaction is associated with the thread.
有时,查看当前事务是否与线程关联很重要。例如,出于调试和调优的⽬的,我们可能需要添加⼀个切⾯或来检查在查询操作期间是否存在⼀个事务。
使⽤这个切⾯和状态值,我们可以检测到对事务设计策略的可能优化。其他情况下,如果需要挂起当前事务或执⾏可能导致应⽤程序失败的代码(例如,执⾏xa下包含DDL代码的存储过程),则可能需要使⽤此状态。下⾯的代码⽚段显⽰了Status_Active Status值的⽤法:
...
if (Status() ==
Status.STATUS_ACTIVE
)
logger.info("Transaction active in query operation");
...
在此⽰例中,如果在查询⽅法中有事务,我们将在⽇志⽂件中创建条⽬。这可能表⽰我们在不需要交易时进⾏交易,从⽽确定可能的优化机会或整个交易设计策略的问题。
STATUS_MARKED_ROLLBACK
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论