- 浏览: 222862 次
- 性别:
- 来自: 成都
文章分类
- 全部博客 (213)
- SQLServer (8)
- flex (8)
- 文章 (5)
- java (91)
- 数据结构 (0)
- 设计模式 (0)
- C# (2)
- Oracle (4)
- 技术 (4)
- 云计算 (0)
- 算法 (0)
- 记录 (3)
- javascript (5)
- div/css (1)
- http (0)
- IE (1)
- web (1)
- hadoop (0)
- extjs (4)
- hibernate (6)
- 错误记录 (5)
- mysql (4)
- json (1)
- jvm (1)
- spring (4)
- 工具 (2)
- tomcat (3)
- cxf (3)
- spring data (1)
- memcached (5)
- android-exception (2)
- 数据压缩 (1)
- 博客 (2)
- bat (0)
- nginx (3)
- svn (2)
- jpa (1)
- windows (2)
- h2 (2)
- webservice (2)
- android (5)
- oa (0)
- eclipse (2)
- jquery (2)
- jni (4)
- weblogic (1)
- work (0)
- smartclient (1)
- sql (0)
- excel (0)
- test (0)
- t (0)
- js (4)
- utils (0)
- bootstrap (0)
- sniper (0)
- ztree (0)
- google (0)
- mdb (0)
- redis (1)
- 思想 (1)
- css (0)
- appCan (0)
- activiti (0)
- 工作 (0)
- 浏览器 (1)
http://www.ibm.com/developerworks/cn/java/j-ts2.html
开发人员、设计人员和架构师经常会混淆事务模型 与事务策略。我经常会让与客户接触的架构师和技术总监描述他们项目的事务策略。我通常会获得三种回应。有时,他们会说 “我们实际上并未在应用程序中使用事务。”另一些时候,我会听到迷惑的回答:“我不明白你的意思。”但是,我也会遇到非常自信的回答:“我们使用声明式事务。”在本文中,术语声明式事务 描述的是一个事务模型,但它绝不是一种事务策略。
关于本系列
事务将改善您数据的质量、完整性以及一致性,并且让您的应用程序更加可靠。在 Java 应用程序中实现成功的事务处理并不是一项轻松的任务,它是一项与编写代码相关的设计工作。 在这个新的 系列 中,Mark Richards 将指导您设计一个有效的事务策略,它适用于各种用例,从简单的应用程序到高级性能事务处理。
Java 平台支持的三种事务模型包括:
Local Transaction 模型
Programmatic Transaction 模型
Declarative Transaction 模型
这些模型描述事务在 Java 平台中的基本运行方式,以及它们是如何实现的。但是,它们仅提供了事务处理的规则和语义。如何应用事务模型则完全由您决定。举例来说,应该如何在 REQUIRED 和 MANDATORY 事务属性之间做出选择?您应该在何时何种情况下指定事务回滚指令?您应该在何时考虑 Programmatic Transaction 模型与 Declarative Transaction 模型的优劣?您应该如何优化高性能系统的事务?事务模型本身无法回答这些问题。您必须通过开发自己的事务策略或采用本文介绍的四种主要事务策略之一来解决它们。
如本系列的 第一篇文章 所述,许多常见的事务陷阱都会影响到事务行为,并且由此会降低数据的完整性和一致性。同样,缺乏有效的(或任何)事务策略将对您数据的完整性和一致性造成负面影响。本文所描述的事务模型是开发有效事务策略的基本元素。理解这些模型之间的差异以及它们的运行方式对于理解使用它们的事务策略非常重要。在介绍完三种事务模型之后,我将讨论适用于大多数业务应用程序(从简单的 Web 应用程序到大型的高速事务处理系统)的四种事务策略。事务策略 系列的后续文章将详细讨论这些策略。
Local Transaction 模型
在 Local Transaction 模型中,事务由底层数据库资源管理程序(而非应用程序所在的容器或框架)管理,这便是它得名的原因。在这个模型中,您将管理连接,而不是事务。从 “了解事务陷阱” 一文可知,在使用对象关系映射框架,如 Hibernate、TopLink 或 the Java Persistence API (JPA),执行数据库更新时,您不能使用 Local Transaction 模型。在使用数据访问对象(DAO)或基于 JDBC 的框架和数据库存储过程时,您可以使用它。
您可以采用以下两种方式来使用 Local Transaction 模型:让数据库来管理连接,或者以编程的方式管理连接。要让数据库管理连接,您需要将 JDBC Connection 对象上的 autoCommit 属性设置为 true(默认值),这将通知底层数据库管理系统(DBMS)在完成插入、更新或删除操作之后提交事务,或者在操作失败时返回任务。清单 1 展示了这种技巧,它在 TRADE 表中插入了一条股票交易命令:
清单 1. 包括一个更新的本地事务
public class TradingServiceImpl {
public void processTrade(TradeData trade) throws Exception {
Connection dbConnection = null;
try {
DataSource ds = (DataSource)
(new InitialContext()).lookup("jdbc/MasterDS");
dbConnection = ds.getConnection();
dbConnection.setAutoCommit(true);
Statement sql = dbConnection.createStatement();
String stmt = "insert into TRADE ...";
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. 包括多个更新的本地事务
public class TradingServiceImpl {
public void processTrade(TradeData trade) throws Exception {
Connection dbConnection = null;
try {
DataSource ds = (DataSource)
(new InitialContext()).lookup("jdbc/MasterDS");
dbConnection = ds.getConnection();
dbConnection.setAutoCommit(false);
Statement sql = dbConnection.createStatement();
String stmt1 = "insert into TRADE ...";
sql.executeUpdate(stmt1);
String stmt2 = "update ACCT set balance...";
sql.executeUpdate(stmt2);
dbConnection.commit();
} catch (Exception up) {
dbConnection.rollback();
throw up;
} finally {
if (dbConnection != null)
dbConnection.close();
}
}
}
注意,在清单 2 中,autoCommit 属性设置为 false,这将通知底层 DBMS 在代码而非数据库中管理连接。在本例中,如果一切正常,您必须调用 Connection 对象上的 commit() 方法;否则,如果出现异常,则调用 rollback() 方法。通过这种方式,您可以在相同的工作单元中协调两个数据库活动。
虽然 Local Transaction 模型现在看来有些过时,但它是本文结束部分介绍的一个主要事务策略的重要元素。
回页首
Programmatic Transaction 模型
Programmatic Transaction 模型的名称来自这样一个事实:开发人负责管理事务。在 Programmatic Transaction 模型中,与 Local Transaction 模型不同,您将管理事务,并且将与底层数据库连接 相分离。
与 清单 2 中的示例相似,借助此模型,开发人员将负责从事务管理程序获取一个事务,启动该事务,提交事务,以及(如果出现异常)回滚到事务。您可能已经猜到,这会产生大量易于出错的代码,从而对应用程序中的业务逻辑造成影响。但是,一些事务策略需要使用 Programmatic Transaction 模型。
虽然概念是相同的是,但 Programmatic Transaction 模型的实现在 Spring Framework 和 EJB 3.0 规范中是不同的。我将先使用 EJB 3.0 来演示这个模型的实现,然后使用 Spring Framework 来展示相同的数据库更新。
使用 EJB 3.0 实现编程事务
在 EJB 3.0 中,您将从事务管理程序(换句话说,容器)获取一个事务,方法是对 javax.transaction.UserTransaction 执行 Java Naming and Directory Interface (JNDI) 查找。获取 UserTransaction 之后,您可以调用 begin() 方法来启动事务,调用 commit() 方法来提交事务,并且调用 rollback() 方法以便在出现错误时回滚事务。在此模型中,容器不会自动提交或回滚事务;开发人员需要自己在执行数据库更新的方法编写此行为。清单 3 通过 JPA 展示了使用 EJB 3.0 的 Programmatic Transaction 模型:
清单 3. 使用 EJB 3.0 实现的编程事务
@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class TradingServiceImpl implements TradingService {
@PersistenceContext(unitName="trading") EntityManager em;
public void processTrade(TradeData trade) throws Exception {
InitialContext ctx = new InitialContext();
UserTransaction txn = (UserTransaction)ctx.lookup("UserTransaction");
try {
txn.begin();
em.persist(trade);
AcctData acct = em.find(AcctData.class, trade.getAcctId());
double tradeValue = trade.getPrice() * trade.getShares();
double currentBalance = acct.getBalance();
if (trade.getAction().equals("BUY")) {
acct.setBalance(currentBalance - tradeValue);
} else {
acct.setBalance(currentBalance + tradeValue);
}
txn.commit();
} catch (Exception up) {
txn.rollback();
throw up;
}
}
}
在带无状态会话 bean 的 Java EE 容器环境中使用 Programmatic Transaction 模型时,您必须通知容器您正在使用编程事务。为此,您需要使用 @TransactionManagement 注释并将事务类型设置为 BEAN。如果您未使用此注释,则容器将假定您使用声明式事务管理(CONTAINER),它是 EJB 3.0 的默认事务类型。在无状态会话 bean 上下文外部的客户机层中使用编程事务时,您不需要设置事务类型。
使用 Spring 实现编程事务
Spring Framework 提供了两种实现 Programmatic Transaction 模型的方法。一种是通过 Spring TransactionTemplate 来实现,另一种是直接使用 Spring 平台事务管理程序。由于我并不热衷于编写匿名内部类和难以理解的代码,因此我将使用第二种技巧来演示 Spring 中的 Programmatic Transaction 模型。
Spring 至少有九种平台事务管理程序。最常用的包括 DataSourceTransactionManager、HibernateTransactionManager、JpaTransactionManager 和 JtaTransactionManager。我的代码示例使用的是 JPA,因此我将展示 JpaTransactionManager 的配置。
要在 Spring 中配置 JpaTransactionManager,只需要使用 org.springframework.orm.jpa.JpaTransactionManager 类在应用程序上下文 XML 文件中定义 bean,并添加一个到 JPA Entity Manager Factory bean 的引用。然后,假定包含应用程序逻辑的类是由 Spring 管理的,将事务管理程序注入到 bean 中,如清单 4 所示:
清单 4. 定义 Spring JPA 事务管理程序
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="tradingService" class="com.trading.service.TradingServiceImpl">
<property name="txnManager" ref="transactionManager"/>
</bean>
如果 Spring 未管理应用程序类,那么您可以在您的方法中引用事务管理程序,方法是在 Spring 上下文中使用 getBean() 方法。
在源代码中,您现在可以使用平台管理程序获取一个事务。执行了所有更新之后,您可以调用 commit() 方法来提交事务,或者调用 rollback() 方法来回滚事务。清单 5 展示了此技巧:
清单 5. 使用 Spring JPA 事务管理程序
public class TradingServiceImpl {
@PersistenceContext(unitName="trading") EntityManager em;
JpaTransactionManager txnManager = null;
public void setTxnManager(JpaTransactionManager mgr) {
txnManager = mgr;
}
public void processTrade(TradeData trade) throws Exception {
TransactionStatus status =
txnManager.getTransaction(new DefaultTransactionDefinition());
try {
em.persist(trade);
AcctData acct = em.find(AcctData.class, trade.getAcctId());
double tradeValue = trade.getPrice() * trade.getShares();
double currentBalance = acct.getBalance();
if (trade.getAction().equals("BUY")) {
acct.setBalance(currentBalance - tradeValue);
} else {
acct.setBalance(currentBalance + tradeValue);
}
txnManager.commit(status);
} catch (Exception up) {
txnManager.rollback(status);
throw up;
}
}
}
注意清单 5 中 Spring Framework 与 EJB 3.0 之间的差异。在 Spring 中,获取事务(随后启动它)的方法是在平台事务管理程序上调用 getTransaction()。匿名 DefaultTransactionDefinition 类包含关于事务及其行为的详细信息,包括事务名称、隔离级别、传播模式(事务属性)和事务超时(如果存在)。在本例中,我可以仅使用默认值,其名称是空字符串,底层 DBMS 的默认的隔离级别通常为 READ_COMMITTED,事务属性是 PROPAGATION_REQUIRED,以及 DBMS 的默认超时。还需注意,commit() 和 rollback() 方法是使用平台事务管理程序调用的,而不是事务(与 DJB 的情况相同)。
回页首
Declarative Transaction 模型
Declarative Transaction 模型,又称作 Container Managed Transactions (CMT),是 Java 平台中最常用的事务模型。在该模型中,容器环境负责启动、提交和回滚事务。开发人员仅负责指定事务的行为。本系列的 第一篇文章 所讨论的大多数事务陷阱都与 Declarative Transaction 模型相关。
Spring Framework 和 EJB 3.0 都利用注释来指定事务行为。Spring 使用 @Transactional 注释,而 EJB 3.0 使用 @TransactionAttribute 注释。在使用 Declarative Transaction 模型时,容器将不会针对检测到的异常自动回滚事务。开发人员必须指定出现异常时在何处以及何时回滚事务。在 Spring Framework 中,您通过使用 @Transactional 注释上的 rollbackFor 属性来指定它。在 EJB 中,您通过调用 SessionContext 上的 setRollbackOnly() 方法来指定它。
清单 6 展示了 Declarative Transaction 模型的 EJB 应用:
清单 6. 使用 EJB 3.0 的声明式事务
@Stateless
public class TradingServiceImpl implements TradingService {
@PersistenceContext(unitName="trading") EntityManager em;
@Resource SessionContext ctx;
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void processTrade(TradeData trade) throws Exception {
try {
em.persist(trade);
AcctData acct = em.find(AcctData.class, trade.getAcctId());
double tradeValue = trade.getPrice() * trade.getShares();
double currentBalance = acct.getBalance();
if (trade.getAction().equals("BUY")) {
acct.setBalance(currentBalance - tradeValue);
} else {
acct.setBalance(currentBalance + tradeValue);
}
} catch (Exception up) {
ctx.setRollbackOnly();
throw up;
}
}
}
清单 7 演示了 Declarative Transaction 模型的 Spring Framework 应用:
清单 7. 使用 Spring 的声明式事务
public class TradingServiceImpl {
@PersistenceContext(unitName="trading") EntityManager em;
@Transactional(propagation=Propagation.REQUIRED,
rollbackFor=Exception.class)
public void processTrade(TradeData trade) throws Exception {
em.persist(trade);
AcctData acct = em.find(AcctData.class, trade.getAcctId());
double tradeValue = trade.getPrice() * trade.getShares();
double currentBalance = acct.getBalance();
if (trade.getAction().equals("BUY")) {
acct.setBalance(currentBalance - tradeValue);
} else {
acct.setBalance(currentBalance + tradeValue);
}
}
}
事务属性
除了回滚指令之外,您还必须指定事务属性,这将定义事务的行为。Java 平台支持六种事务属性,而与您使用的是 EJB 还是 Spring Framework 无关:
Required
Mandatory
RequiresNew
Supports
NotSupported
Never
在描述这些事务属性时,我将使用一个假想的 methodA() 方法,事务属性将应用于这个方法。
如果为 methodA() 指定了 Required 事务属性,并且在已有事务作用域内调用了 methodA(),那么将使用已有的事务作用域。否则,methodA() 将启动一个新的事务。如果事务是由 methodA() 启动的,则它也必须由 methodA() 来终止(提交或回滚)。这是最常用的事务属性,并且是 EJB 3.0 和 Spring 的默认属性。遗憾的是,它的使用并不正确,从而造成了数据完整性和一致性问题。对于本系列后续文章将要讨论的各事务策略,我将更加详细地阐述这个事务属性。
如果为 methodA() 指定了 Mandatory 事务属性,并且在已有事务作用域内调用了 methodA(),那么将使用已有的事务作用域。但是,如果调用 methodA() 时没有事务上下文,则会抛出一个 TransactionRequiredException,指示必须在调用 methodA() 之前呈现事务。这个事务属性应用于本文下一部分将要讨论的 Client Orchestration 事务策略。
RequiresNew 事务属性非常有趣。许多时候,我会发现这个属性被误用或误解。methodA() 指定了 RequiresNew 属性,并且在有或没有事务上下文的情况下调用了 methodA(),那么新事务将始终由 methodA() 启动(和终止)。这意味着,如果在另一个事务(比如说 Transaction1)的上下文中调用了 methodA(),那么 Transaction1 将暂停,同时会启动一个新的事务(Transaction2)。当 methodA() 结束时,Transaction2 将提交或回滚,并且 Transaction1 将恢复。这显然违背了事务的 ACID(atomicity、consistency、isolation 和 durability)属性(特别是 atomicity 属性)。换句话说,所有数据库更新都不再包含在一个单一的工作单元中。如果 Transaction1 将进行回滚,则由 Transaction2 提交的更改将仍然被提交。如果是这种情况,那么事务属性有什么好处呢?如本系列第一篇文章所述,这个事务属性仅用于独立于底层事务的数据库操作(比如审计或记录,在本例中为 Transaction1)。
Supports 事务属性也是我发现大多数开发人员未完全理解或掌握的一个地方。如果为 methodA() 指定了 Supports 事务属性,并且在已有事务的作用域中调用了 methodA(),则 methodA() 将在该事务的作用域中执行。但是,如果在没有事务上下文的情况下调用了 methodA(),则不会启动任何事务。此属性主要用于针对数据库的只读操作。如果是这种情况,为何不指定 NotSupported 事务属性(在下一段中讨论)呢?毕竟,该属性将确保方法在没有事务的情况下运行。答案非常简单。在已有事务的上下文中调用查询操作会导致从数据库事务日志中读取数据(换句话说,已更新的数据),而不在事务作用域中运行会导致查询从表中读取未修改的数据。举例来说,如果您插入一个新的交易订单到 TRADE 表中,然后(在相同的事务中)获取所有交易订单的列表,则未提交的交易将出现在列表中。但是,如果您使用的是 NotSupported 事务属性,那么它会造成数据库查询从表中而不是从事务日志中读取数据。因此,在上一个示例中,您将不会看到未提交的交易。这并不一定是一件坏事;这将由您的用例和业务逻辑决定。
NotSupported 事务属性指定被调用的方法将不使用或启动事务,无论是否呈现了事务。如果为 methodA() 指定了 NotSupported 事务属性,并且在某个事务的上下文中调用了 methodA(),则该事务将暂停直到 methodA() 结束。当 methodA() 结束时,原始事务将被恢复。这个事务属性只有少许用例,并且它们主要涉及数据库存储过程。如果您尝试在已有事务上下文的作用域中调用数据库存储过程,并且数据库存储过程包含一个 BEGIN TRANS,或者对于 Sybase 的情况,在未连接模式中运行,则会抛出一个异常,指示已经存在一个事务无法启动新事务。(换句话说,内嵌事务不受支持)。几乎所有的容器都使用 Java Transaction Service (JTS) 作为默认的 JTA 事务实现。不支持内嵌事务的是 JTS 而不是 Java 平台。如果您无法修改数据库存储过程,那么您可以使用 NotSupported 属性来暂停已有事务上下文,以避免这种致命的异常。但是,其影响是您不能在相同的 LUW 中对数据库执行原子更新。这是一种权衡,但有时可以让您迅速脱离困境。
Never 或许是最有趣的事务属性。它的行为类似于 NotSupported 事务属性,但存在一个重要差异:如果使用 Never 事务属性调用方法时已经存在某个事务上下文,则会抛出一个异常,指示您在调用该方法时不允许使用这个事务。我能想到的针对此事务属性的唯一一个用例就是用于测试。它提供了一种简单的方法,用于验证当您调用特定的方法时某个事务是否存在。如果您在调用相应的方法时使用了 Never 事务属性并且接收到了一个异常,则知道事务已经呈现。如果允许执行方法,则您知道事务未被呈现。这是确保事务策略坚固的理想方法。
回页首
事务策略
本文描述的事务模型形成了即将介绍的事务策略的基础。在开始构建事务策略之前,完全理解这些模型之间的差异以及它们的运行原理是非常重要的。可以在大多数业务应用程序场景中使用的主要事务策略包括:
Client Orchestration 事务策略
API Layer 事务策略
High Concurrency 事务策略
High-Speed Processing 事务策略
我将在此处简要介绍这些策略,并将在本系列的后续文章中详细讨论它们。
当来自客户机层的基于多服务器或基于模型的调用完成单一工作单元时,将使用 Client Orchestration 事务策略。此处的客户机层可以表示来自 Web 框架、门户应用程序、桌面系统或工作流产品或业务流程管理(BPM)组件的调用。从本质上说,客户机层拥有处理流以及完成特定请求所需的 “步骤”。举例来说,要添加一个交易订单,假定您需要将交易插入到数据库中,然后更新客户的帐户余额以反映交易的值。如果应用程序的 API 层非常细粒度化,那么您需要调用客户机层中的两个方法。在此场景中,事务工作单元必须位于客户机层中以确保自动化的工作单元。
当粗粒度方法充当后端功能的主要入口点时,将使用 API Layer 事务策略。(如果愿意,可以将它们称作 services)。在此场景中,客户机(基于 Web、基于 Web 服务、基于消息或者甚至桌面)向后端发起单个调用以执行特定的请求。使用上文提到的交易订单场景,在本例中您将拥有一个入口点方法(比如说 processTrade())由客户机层调用。然后,这个方法将包含插入交易订单和更新帐户所需的业务流程。我将这个策略指定为这个名称的原因是,在大多数情况下,后端处理功能会通过使用接口或 API 向客户应用程序公开。这是最常用的事务策略之一。
High Concurrency 事务策略是 API Layer 事务策略的一个变体,它用于不支持通过 API 层长时间运行事务的应用程序(通常出于性能或可伸缩性需求的原因)。顾名思义,此策略主要在从用户的角度支持高度并发性的应用程序中使用。事务在 Java 平台的开销非常大。根据您所使用的数据库,它们可以造成在数据库中出现锁定,独占资源,在吞吐量方面拖慢应用程序,并且在某些情况下甚至造成数据库死锁。这种事务策略内部的主要思想是缩短事务作用域,以便您能够最大限度地减少数据库中的死锁,同时仍然为任何特定的客户机请求维持自动工作单元。在某些情况下,您可能需要重构应用程序逻辑,以支持这种事务策略。
High-Speed Processing 事务策略或许是事务策略的最极端。如果您需要从应用程序中获取最快的处理时间(以及吞吐量),并且仍然在处理中维持一定程序的事务原子性,那么可以使用它。虽然这种策略从数据完整性和一致的角度来说引入了一些风险,但如果正确实现,它将成为 Java 平台中速度可能是最快的事务策略。它可能是这四个事务策略中最难以实现的一个。
回页首
结束语
从这篇概述中可以看到,开发一个有效的事务策略并非总是一项简单的任务。在解决数据完整性和一致问题时需要考虑各种事项、选项、模型、框架、配置和技巧。在使用应用程序和事务的多年经验中,我发现虽然模型、选项、设置和配置的组合有时会非常伤脑筋,但大多数情况下实际上只有一些选项和设置的组合是有用的。我设计的这四种事务策略将在本系列的后续文章中详细讨论,并且应该能涵盖您可能会在 Java 平台业务应用程序开发中遇到的大多数场景。需要注意:这些策略并不是简单的 “一劳永逸” 解决方案。在某些情况下,一些实现还需要重新构建源代码或者重新设计应用程序。对于这种情况,您只需要问自己一个问题:“我的数据的完整性和一致性有多重要?”在大多数情况下,与损坏数据招致的风险和成本相比,重构工作并不算什么。
开发人员、设计人员和架构师经常会混淆事务模型 与事务策略。我经常会让与客户接触的架构师和技术总监描述他们项目的事务策略。我通常会获得三种回应。有时,他们会说 “我们实际上并未在应用程序中使用事务。”另一些时候,我会听到迷惑的回答:“我不明白你的意思。”但是,我也会遇到非常自信的回答:“我们使用声明式事务。”在本文中,术语声明式事务 描述的是一个事务模型,但它绝不是一种事务策略。
关于本系列
事务将改善您数据的质量、完整性以及一致性,并且让您的应用程序更加可靠。在 Java 应用程序中实现成功的事务处理并不是一项轻松的任务,它是一项与编写代码相关的设计工作。 在这个新的 系列 中,Mark Richards 将指导您设计一个有效的事务策略,它适用于各种用例,从简单的应用程序到高级性能事务处理。
Java 平台支持的三种事务模型包括:
Local Transaction 模型
Programmatic Transaction 模型
Declarative Transaction 模型
这些模型描述事务在 Java 平台中的基本运行方式,以及它们是如何实现的。但是,它们仅提供了事务处理的规则和语义。如何应用事务模型则完全由您决定。举例来说,应该如何在 REQUIRED 和 MANDATORY 事务属性之间做出选择?您应该在何时何种情况下指定事务回滚指令?您应该在何时考虑 Programmatic Transaction 模型与 Declarative Transaction 模型的优劣?您应该如何优化高性能系统的事务?事务模型本身无法回答这些问题。您必须通过开发自己的事务策略或采用本文介绍的四种主要事务策略之一来解决它们。
如本系列的 第一篇文章 所述,许多常见的事务陷阱都会影响到事务行为,并且由此会降低数据的完整性和一致性。同样,缺乏有效的(或任何)事务策略将对您数据的完整性和一致性造成负面影响。本文所描述的事务模型是开发有效事务策略的基本元素。理解这些模型之间的差异以及它们的运行方式对于理解使用它们的事务策略非常重要。在介绍完三种事务模型之后,我将讨论适用于大多数业务应用程序(从简单的 Web 应用程序到大型的高速事务处理系统)的四种事务策略。事务策略 系列的后续文章将详细讨论这些策略。
Local Transaction 模型
在 Local Transaction 模型中,事务由底层数据库资源管理程序(而非应用程序所在的容器或框架)管理,这便是它得名的原因。在这个模型中,您将管理连接,而不是事务。从 “了解事务陷阱” 一文可知,在使用对象关系映射框架,如 Hibernate、TopLink 或 the Java Persistence API (JPA),执行数据库更新时,您不能使用 Local Transaction 模型。在使用数据访问对象(DAO)或基于 JDBC 的框架和数据库存储过程时,您可以使用它。
您可以采用以下两种方式来使用 Local Transaction 模型:让数据库来管理连接,或者以编程的方式管理连接。要让数据库管理连接,您需要将 JDBC Connection 对象上的 autoCommit 属性设置为 true(默认值),这将通知底层数据库管理系统(DBMS)在完成插入、更新或删除操作之后提交事务,或者在操作失败时返回任务。清单 1 展示了这种技巧,它在 TRADE 表中插入了一条股票交易命令:
清单 1. 包括一个更新的本地事务
public class TradingServiceImpl {
public void processTrade(TradeData trade) throws Exception {
Connection dbConnection = null;
try {
DataSource ds = (DataSource)
(new InitialContext()).lookup("jdbc/MasterDS");
dbConnection = ds.getConnection();
dbConnection.setAutoCommit(true);
Statement sql = dbConnection.createStatement();
String stmt = "insert into TRADE ...";
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. 包括多个更新的本地事务
public class TradingServiceImpl {
public void processTrade(TradeData trade) throws Exception {
Connection dbConnection = null;
try {
DataSource ds = (DataSource)
(new InitialContext()).lookup("jdbc/MasterDS");
dbConnection = ds.getConnection();
dbConnection.setAutoCommit(false);
Statement sql = dbConnection.createStatement();
String stmt1 = "insert into TRADE ...";
sql.executeUpdate(stmt1);
String stmt2 = "update ACCT set balance...";
sql.executeUpdate(stmt2);
dbConnection.commit();
} catch (Exception up) {
dbConnection.rollback();
throw up;
} finally {
if (dbConnection != null)
dbConnection.close();
}
}
}
注意,在清单 2 中,autoCommit 属性设置为 false,这将通知底层 DBMS 在代码而非数据库中管理连接。在本例中,如果一切正常,您必须调用 Connection 对象上的 commit() 方法;否则,如果出现异常,则调用 rollback() 方法。通过这种方式,您可以在相同的工作单元中协调两个数据库活动。
虽然 Local Transaction 模型现在看来有些过时,但它是本文结束部分介绍的一个主要事务策略的重要元素。
回页首
Programmatic Transaction 模型
Programmatic Transaction 模型的名称来自这样一个事实:开发人负责管理事务。在 Programmatic Transaction 模型中,与 Local Transaction 模型不同,您将管理事务,并且将与底层数据库连接 相分离。
与 清单 2 中的示例相似,借助此模型,开发人员将负责从事务管理程序获取一个事务,启动该事务,提交事务,以及(如果出现异常)回滚到事务。您可能已经猜到,这会产生大量易于出错的代码,从而对应用程序中的业务逻辑造成影响。但是,一些事务策略需要使用 Programmatic Transaction 模型。
虽然概念是相同的是,但 Programmatic Transaction 模型的实现在 Spring Framework 和 EJB 3.0 规范中是不同的。我将先使用 EJB 3.0 来演示这个模型的实现,然后使用 Spring Framework 来展示相同的数据库更新。
使用 EJB 3.0 实现编程事务
在 EJB 3.0 中,您将从事务管理程序(换句话说,容器)获取一个事务,方法是对 javax.transaction.UserTransaction 执行 Java Naming and Directory Interface (JNDI) 查找。获取 UserTransaction 之后,您可以调用 begin() 方法来启动事务,调用 commit() 方法来提交事务,并且调用 rollback() 方法以便在出现错误时回滚事务。在此模型中,容器不会自动提交或回滚事务;开发人员需要自己在执行数据库更新的方法编写此行为。清单 3 通过 JPA 展示了使用 EJB 3.0 的 Programmatic Transaction 模型:
清单 3. 使用 EJB 3.0 实现的编程事务
@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class TradingServiceImpl implements TradingService {
@PersistenceContext(unitName="trading") EntityManager em;
public void processTrade(TradeData trade) throws Exception {
InitialContext ctx = new InitialContext();
UserTransaction txn = (UserTransaction)ctx.lookup("UserTransaction");
try {
txn.begin();
em.persist(trade);
AcctData acct = em.find(AcctData.class, trade.getAcctId());
double tradeValue = trade.getPrice() * trade.getShares();
double currentBalance = acct.getBalance();
if (trade.getAction().equals("BUY")) {
acct.setBalance(currentBalance - tradeValue);
} else {
acct.setBalance(currentBalance + tradeValue);
}
txn.commit();
} catch (Exception up) {
txn.rollback();
throw up;
}
}
}
在带无状态会话 bean 的 Java EE 容器环境中使用 Programmatic Transaction 模型时,您必须通知容器您正在使用编程事务。为此,您需要使用 @TransactionManagement 注释并将事务类型设置为 BEAN。如果您未使用此注释,则容器将假定您使用声明式事务管理(CONTAINER),它是 EJB 3.0 的默认事务类型。在无状态会话 bean 上下文外部的客户机层中使用编程事务时,您不需要设置事务类型。
使用 Spring 实现编程事务
Spring Framework 提供了两种实现 Programmatic Transaction 模型的方法。一种是通过 Spring TransactionTemplate 来实现,另一种是直接使用 Spring 平台事务管理程序。由于我并不热衷于编写匿名内部类和难以理解的代码,因此我将使用第二种技巧来演示 Spring 中的 Programmatic Transaction 模型。
Spring 至少有九种平台事务管理程序。最常用的包括 DataSourceTransactionManager、HibernateTransactionManager、JpaTransactionManager 和 JtaTransactionManager。我的代码示例使用的是 JPA,因此我将展示 JpaTransactionManager 的配置。
要在 Spring 中配置 JpaTransactionManager,只需要使用 org.springframework.orm.jpa.JpaTransactionManager 类在应用程序上下文 XML 文件中定义 bean,并添加一个到 JPA Entity Manager Factory bean 的引用。然后,假定包含应用程序逻辑的类是由 Spring 管理的,将事务管理程序注入到 bean 中,如清单 4 所示:
清单 4. 定义 Spring JPA 事务管理程序
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="tradingService" class="com.trading.service.TradingServiceImpl">
<property name="txnManager" ref="transactionManager"/>
</bean>
如果 Spring 未管理应用程序类,那么您可以在您的方法中引用事务管理程序,方法是在 Spring 上下文中使用 getBean() 方法。
在源代码中,您现在可以使用平台管理程序获取一个事务。执行了所有更新之后,您可以调用 commit() 方法来提交事务,或者调用 rollback() 方法来回滚事务。清单 5 展示了此技巧:
清单 5. 使用 Spring JPA 事务管理程序
public class TradingServiceImpl {
@PersistenceContext(unitName="trading") EntityManager em;
JpaTransactionManager txnManager = null;
public void setTxnManager(JpaTransactionManager mgr) {
txnManager = mgr;
}
public void processTrade(TradeData trade) throws Exception {
TransactionStatus status =
txnManager.getTransaction(new DefaultTransactionDefinition());
try {
em.persist(trade);
AcctData acct = em.find(AcctData.class, trade.getAcctId());
double tradeValue = trade.getPrice() * trade.getShares();
double currentBalance = acct.getBalance();
if (trade.getAction().equals("BUY")) {
acct.setBalance(currentBalance - tradeValue);
} else {
acct.setBalance(currentBalance + tradeValue);
}
txnManager.commit(status);
} catch (Exception up) {
txnManager.rollback(status);
throw up;
}
}
}
注意清单 5 中 Spring Framework 与 EJB 3.0 之间的差异。在 Spring 中,获取事务(随后启动它)的方法是在平台事务管理程序上调用 getTransaction()。匿名 DefaultTransactionDefinition 类包含关于事务及其行为的详细信息,包括事务名称、隔离级别、传播模式(事务属性)和事务超时(如果存在)。在本例中,我可以仅使用默认值,其名称是空字符串,底层 DBMS 的默认的隔离级别通常为 READ_COMMITTED,事务属性是 PROPAGATION_REQUIRED,以及 DBMS 的默认超时。还需注意,commit() 和 rollback() 方法是使用平台事务管理程序调用的,而不是事务(与 DJB 的情况相同)。
回页首
Declarative Transaction 模型
Declarative Transaction 模型,又称作 Container Managed Transactions (CMT),是 Java 平台中最常用的事务模型。在该模型中,容器环境负责启动、提交和回滚事务。开发人员仅负责指定事务的行为。本系列的 第一篇文章 所讨论的大多数事务陷阱都与 Declarative Transaction 模型相关。
Spring Framework 和 EJB 3.0 都利用注释来指定事务行为。Spring 使用 @Transactional 注释,而 EJB 3.0 使用 @TransactionAttribute 注释。在使用 Declarative Transaction 模型时,容器将不会针对检测到的异常自动回滚事务。开发人员必须指定出现异常时在何处以及何时回滚事务。在 Spring Framework 中,您通过使用 @Transactional 注释上的 rollbackFor 属性来指定它。在 EJB 中,您通过调用 SessionContext 上的 setRollbackOnly() 方法来指定它。
清单 6 展示了 Declarative Transaction 模型的 EJB 应用:
清单 6. 使用 EJB 3.0 的声明式事务
@Stateless
public class TradingServiceImpl implements TradingService {
@PersistenceContext(unitName="trading") EntityManager em;
@Resource SessionContext ctx;
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void processTrade(TradeData trade) throws Exception {
try {
em.persist(trade);
AcctData acct = em.find(AcctData.class, trade.getAcctId());
double tradeValue = trade.getPrice() * trade.getShares();
double currentBalance = acct.getBalance();
if (trade.getAction().equals("BUY")) {
acct.setBalance(currentBalance - tradeValue);
} else {
acct.setBalance(currentBalance + tradeValue);
}
} catch (Exception up) {
ctx.setRollbackOnly();
throw up;
}
}
}
清单 7 演示了 Declarative Transaction 模型的 Spring Framework 应用:
清单 7. 使用 Spring 的声明式事务
public class TradingServiceImpl {
@PersistenceContext(unitName="trading") EntityManager em;
@Transactional(propagation=Propagation.REQUIRED,
rollbackFor=Exception.class)
public void processTrade(TradeData trade) throws Exception {
em.persist(trade);
AcctData acct = em.find(AcctData.class, trade.getAcctId());
double tradeValue = trade.getPrice() * trade.getShares();
double currentBalance = acct.getBalance();
if (trade.getAction().equals("BUY")) {
acct.setBalance(currentBalance - tradeValue);
} else {
acct.setBalance(currentBalance + tradeValue);
}
}
}
事务属性
除了回滚指令之外,您还必须指定事务属性,这将定义事务的行为。Java 平台支持六种事务属性,而与您使用的是 EJB 还是 Spring Framework 无关:
Required
Mandatory
RequiresNew
Supports
NotSupported
Never
在描述这些事务属性时,我将使用一个假想的 methodA() 方法,事务属性将应用于这个方法。
如果为 methodA() 指定了 Required 事务属性,并且在已有事务作用域内调用了 methodA(),那么将使用已有的事务作用域。否则,methodA() 将启动一个新的事务。如果事务是由 methodA() 启动的,则它也必须由 methodA() 来终止(提交或回滚)。这是最常用的事务属性,并且是 EJB 3.0 和 Spring 的默认属性。遗憾的是,它的使用并不正确,从而造成了数据完整性和一致性问题。对于本系列后续文章将要讨论的各事务策略,我将更加详细地阐述这个事务属性。
如果为 methodA() 指定了 Mandatory 事务属性,并且在已有事务作用域内调用了 methodA(),那么将使用已有的事务作用域。但是,如果调用 methodA() 时没有事务上下文,则会抛出一个 TransactionRequiredException,指示必须在调用 methodA() 之前呈现事务。这个事务属性应用于本文下一部分将要讨论的 Client Orchestration 事务策略。
RequiresNew 事务属性非常有趣。许多时候,我会发现这个属性被误用或误解。methodA() 指定了 RequiresNew 属性,并且在有或没有事务上下文的情况下调用了 methodA(),那么新事务将始终由 methodA() 启动(和终止)。这意味着,如果在另一个事务(比如说 Transaction1)的上下文中调用了 methodA(),那么 Transaction1 将暂停,同时会启动一个新的事务(Transaction2)。当 methodA() 结束时,Transaction2 将提交或回滚,并且 Transaction1 将恢复。这显然违背了事务的 ACID(atomicity、consistency、isolation 和 durability)属性(特别是 atomicity 属性)。换句话说,所有数据库更新都不再包含在一个单一的工作单元中。如果 Transaction1 将进行回滚,则由 Transaction2 提交的更改将仍然被提交。如果是这种情况,那么事务属性有什么好处呢?如本系列第一篇文章所述,这个事务属性仅用于独立于底层事务的数据库操作(比如审计或记录,在本例中为 Transaction1)。
Supports 事务属性也是我发现大多数开发人员未完全理解或掌握的一个地方。如果为 methodA() 指定了 Supports 事务属性,并且在已有事务的作用域中调用了 methodA(),则 methodA() 将在该事务的作用域中执行。但是,如果在没有事务上下文的情况下调用了 methodA(),则不会启动任何事务。此属性主要用于针对数据库的只读操作。如果是这种情况,为何不指定 NotSupported 事务属性(在下一段中讨论)呢?毕竟,该属性将确保方法在没有事务的情况下运行。答案非常简单。在已有事务的上下文中调用查询操作会导致从数据库事务日志中读取数据(换句话说,已更新的数据),而不在事务作用域中运行会导致查询从表中读取未修改的数据。举例来说,如果您插入一个新的交易订单到 TRADE 表中,然后(在相同的事务中)获取所有交易订单的列表,则未提交的交易将出现在列表中。但是,如果您使用的是 NotSupported 事务属性,那么它会造成数据库查询从表中而不是从事务日志中读取数据。因此,在上一个示例中,您将不会看到未提交的交易。这并不一定是一件坏事;这将由您的用例和业务逻辑决定。
NotSupported 事务属性指定被调用的方法将不使用或启动事务,无论是否呈现了事务。如果为 methodA() 指定了 NotSupported 事务属性,并且在某个事务的上下文中调用了 methodA(),则该事务将暂停直到 methodA() 结束。当 methodA() 结束时,原始事务将被恢复。这个事务属性只有少许用例,并且它们主要涉及数据库存储过程。如果您尝试在已有事务上下文的作用域中调用数据库存储过程,并且数据库存储过程包含一个 BEGIN TRANS,或者对于 Sybase 的情况,在未连接模式中运行,则会抛出一个异常,指示已经存在一个事务无法启动新事务。(换句话说,内嵌事务不受支持)。几乎所有的容器都使用 Java Transaction Service (JTS) 作为默认的 JTA 事务实现。不支持内嵌事务的是 JTS 而不是 Java 平台。如果您无法修改数据库存储过程,那么您可以使用 NotSupported 属性来暂停已有事务上下文,以避免这种致命的异常。但是,其影响是您不能在相同的 LUW 中对数据库执行原子更新。这是一种权衡,但有时可以让您迅速脱离困境。
Never 或许是最有趣的事务属性。它的行为类似于 NotSupported 事务属性,但存在一个重要差异:如果使用 Never 事务属性调用方法时已经存在某个事务上下文,则会抛出一个异常,指示您在调用该方法时不允许使用这个事务。我能想到的针对此事务属性的唯一一个用例就是用于测试。它提供了一种简单的方法,用于验证当您调用特定的方法时某个事务是否存在。如果您在调用相应的方法时使用了 Never 事务属性并且接收到了一个异常,则知道事务已经呈现。如果允许执行方法,则您知道事务未被呈现。这是确保事务策略坚固的理想方法。
回页首
事务策略
本文描述的事务模型形成了即将介绍的事务策略的基础。在开始构建事务策略之前,完全理解这些模型之间的差异以及它们的运行原理是非常重要的。可以在大多数业务应用程序场景中使用的主要事务策略包括:
Client Orchestration 事务策略
API Layer 事务策略
High Concurrency 事务策略
High-Speed Processing 事务策略
我将在此处简要介绍这些策略,并将在本系列的后续文章中详细讨论它们。
当来自客户机层的基于多服务器或基于模型的调用完成单一工作单元时,将使用 Client Orchestration 事务策略。此处的客户机层可以表示来自 Web 框架、门户应用程序、桌面系统或工作流产品或业务流程管理(BPM)组件的调用。从本质上说,客户机层拥有处理流以及完成特定请求所需的 “步骤”。举例来说,要添加一个交易订单,假定您需要将交易插入到数据库中,然后更新客户的帐户余额以反映交易的值。如果应用程序的 API 层非常细粒度化,那么您需要调用客户机层中的两个方法。在此场景中,事务工作单元必须位于客户机层中以确保自动化的工作单元。
当粗粒度方法充当后端功能的主要入口点时,将使用 API Layer 事务策略。(如果愿意,可以将它们称作 services)。在此场景中,客户机(基于 Web、基于 Web 服务、基于消息或者甚至桌面)向后端发起单个调用以执行特定的请求。使用上文提到的交易订单场景,在本例中您将拥有一个入口点方法(比如说 processTrade())由客户机层调用。然后,这个方法将包含插入交易订单和更新帐户所需的业务流程。我将这个策略指定为这个名称的原因是,在大多数情况下,后端处理功能会通过使用接口或 API 向客户应用程序公开。这是最常用的事务策略之一。
High Concurrency 事务策略是 API Layer 事务策略的一个变体,它用于不支持通过 API 层长时间运行事务的应用程序(通常出于性能或可伸缩性需求的原因)。顾名思义,此策略主要在从用户的角度支持高度并发性的应用程序中使用。事务在 Java 平台的开销非常大。根据您所使用的数据库,它们可以造成在数据库中出现锁定,独占资源,在吞吐量方面拖慢应用程序,并且在某些情况下甚至造成数据库死锁。这种事务策略内部的主要思想是缩短事务作用域,以便您能够最大限度地减少数据库中的死锁,同时仍然为任何特定的客户机请求维持自动工作单元。在某些情况下,您可能需要重构应用程序逻辑,以支持这种事务策略。
High-Speed Processing 事务策略或许是事务策略的最极端。如果您需要从应用程序中获取最快的处理时间(以及吞吐量),并且仍然在处理中维持一定程序的事务原子性,那么可以使用它。虽然这种策略从数据完整性和一致的角度来说引入了一些风险,但如果正确实现,它将成为 Java 平台中速度可能是最快的事务策略。它可能是这四个事务策略中最难以实现的一个。
回页首
结束语
从这篇概述中可以看到,开发一个有效的事务策略并非总是一项简单的任务。在解决数据完整性和一致问题时需要考虑各种事项、选项、模型、框架、配置和技巧。在使用应用程序和事务的多年经验中,我发现虽然模型、选项、设置和配置的组合有时会非常伤脑筋,但大多数情况下实际上只有一些选项和设置的组合是有用的。我设计的这四种事务策略将在本系列的后续文章中详细讨论,并且应该能涵盖您可能会在 Java 平台业务应用程序开发中遇到的大多数场景。需要注意:这些策略并不是简单的 “一劳永逸” 解决方案。在某些情况下,一些实现还需要重新构建源代码或者重新设计应用程序。对于这种情况,您只需要问自己一个问题:“我的数据的完整性和一致性有多重要?”在大多数情况下,与损坏数据招致的风险和成本相比,重构工作并不算什么。
相关推荐
Spring-data-jpa 与 Spring 的整合非常紧密,它利用 Spring 的依赖注入和事务管理功能,使得开发者可以轻松地在应用程序中集成数据访问逻辑。下面将详细介绍整合的关键步骤: 1. **配置 JPA 实体管理器**: - 在 ...
Spring Data JPA 是Spring框架的一个子项目,它提供了基于JPA规范的数据持久化解决方案,主要目的是简化数据访问层的代码。JPA(Java Persistence API)是Java EE(现在是Jakarta EE)的一部分,提供了对象关系映射...
4. **事务管理**: Spring的@Transactional注解可以用来标记需要进行事务处理的方法,Spring-JPA会自动处理事务的开始、提交或回滚。 5. **Querydsl集成**: 可以选择集成Querydsl,提供更强大的静态类型查询能力,使...
根据提供的文档信息,我们可以深入探讨Spring Data JPA的相关知识点,主要围绕其1.3版本的特性与使用方法展开讨论。 ### Spring Data JPA 概述 Spring Data JPA 是一个强大的框架,它简化了Java Persistence API ...
1. **JPA 整合**:Spring Data JPA 提供了与 JPA 规范的无缝集成,包括实体管理、事务管理和数据源配置。 2. **EntityManager 和 Repository**:Spring Data JPA 封装了 `EntityManager` 和 `EntityTransaction`,...
- 配置 JPA 实体管理工厂 (`EntityManagerFactory`) 和事务管理器 (`PlatformTransactionManager`)。 - 使用 `@EnableJpaRepositories` 注解启用 JPA 存储库的自动配置。 - 数据源配置,连接到相应的数据库。 5....
- **事务管理**:Spring Data JPA自动支持@Transactional注解,实现数据库操作的事务性。 - **Repository自定义方法**:除了预定义的方法,还可以在Repository接口中添加自定义的查询方法,Spring Data JPA会自动...
在Spring Boot应用中,使用`spring-data-jpa`来配置MySQL多数据源是一项常见的需求,尤其是在构建大型分布式系统时,为了实现数据隔离、负载均衡或读写分离等目的。本教程将详细介绍如何在Spring Boot项目中配置多个...
### Spring Data JPA 参考文档知识点解析 #### 一、引言 Spring Data JPA 是 Spring 框架下的一个子项目,旨在简化 Java 应用程序中的数据访问层开发工作。它提供了丰富的功能来支持 JPA(Java Persistence API)...
Spring Data JPA是Spring生态中的一个强大ORM框架,它极大地提高了Java开发者在处理数据库操作时的效率。Spring Data JPA的主要优点在于其高度的开发效率、成熟的语法结构以及与Spring框架的紧密集成。 1. **开发...
8. **数据持久化**:理解JPA的保存(`save()`)、更新(`saveAndFlush()`)、查询(`findAll()`, `findById()`等)、删除(`deleteById()`)等操作的工作原理,以及事务管理的概念。 这个"springboot-spring-data-...
3. **配置Spring Data JPA**:在Spring配置中添加JPA和DataSource的相关配置,包括实体管理工厂、事务管理器、数据源等。还需要在pom.xml中引入对应的JPA和数据库驱动依赖。 4. **定义Repository接口**:Spring ...
Spring Data JPA 是 Spring Framework 的一部分,它提供了针对 Java Persistence API (JPA) 的高级数据访问功能。JPA 是 Java 社区定义的一个标准,用于管理关系数据库中的数据。它通过 ORM(对象关系映射)机制,将...
通过这个"最基础的spring-boot-jpa框架",学习者可以快速理解和实践 Spring Boot 与 JPA 结合使用的基本步骤,包括项目的构建、实体定义、数据访问以及事务处理等。这个框架为初学者提供了一个良好的起点,帮助他们...
- **事务管理**:Spring Data JPA 结合 Spring 的事务管理,提供了声明式事务控制,使得事务处理更加简单。 - **集成其他 Spring 功能**:与其他 Spring 模块无缝集成,如 Spring Security、Spring Batch 等。 **3....
根据提供的文档信息,“spring-data-jpa-reference.pdf”主要围绕Spring Data JPA展开,涉及其基本概念、使用方法以及一些高级特性。以下是对该文档中提到的一些关键知识点的详细阐述: ### 一、Spring Data JPA...
Spring Data JPA利用Spring的事务管理能力,通过@Transactional注解在方法级别控制事务。这使得开发者能够轻松地处理事务,确保数据的一致性。 六、JPA规范 Spring Data JPA遵循JPA规范,这意味着它可以与其他JPA...
在事务管理方面,Spring Data JPA 集成了 Spring 的事务管理能力,使得开发者可以在应用的任何地方声明事务边界,确保数据一致性。只需在 Service 层的方法上添加 `@Transactional` 注解,即可轻松管理事务。 ...
Spring Data JPA是Spring框架的一个模块,专门用于简化Java Persistence API (JPA)的使用,为开发者提供了便捷的数据访问层的实现方式。本教程将深入探讨Spring Data JPA的常见用法,帮助开发者快速上手并高效地进行...