知识大全 在Spring中配置Hibernate的事务
Posted 知
篇首语:三人行必有我师焉;择其善者而从之,其不善者而改之。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 在Spring中配置Hibernate的事务相关的知识,希望对你有一定的参考价值。
在Spring中配置Hibernate的事务 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!
本文主要探讨如何利用Spring来装配组件 包括其事务上下文 从J EE应用程序内部连接到单个的数据库并不是什么难事 但是 如果要装配或者集成企业级的组件 情况就复杂了 一个组件可以有一个或多个支持它的数据库 因此 当装配两个或更多的组件时 我们希望能够保持在跨组件的多个数据库中进行的操作的原子性 J EE服务器为这些组件提供了一个容器来保证事务原子性和跨组件独立性 如果使用的不是J EE服务器 则可以利用Spring来帮助我们 Spring基于Inversion of Control(控制反转)模式(也称为依赖注入) 它不仅可以连接组件服务 还可以连接关联的事务上下文 在本文中 我们将Hibernate用作对象/关系持久性存储和查询服务
装配组件事务
假设在企业组件库里 我们已经有一个审计组件 里面有可以被客户端调用的服务方法 然后 当我们想要构建一个订单处理系统时 我们发现存在这样的设计要求 OrderListManager组件服务同样需要审计组件服务 OrderListManager创建和管理订单 因此所有的OrderListManager服务都有自己的事务属性 当我们从OrderListManager服务内调用审计组件时 我们实际上是在把OrderListManager服务的事务上下文传播给审计服务 也许将来新的业务服务组件同样需要审计组件 但那时将在一个不同的事务上下文中调用它 实际结果就是 即使审计组件的功能保持不变 它也可能是由别的业务服务功能组成 包含了混搭的(mix and match)事务属性来提供不同的运行时事务性行为
在图 中有两个独立的调用上下文流程 在流程 里 如果客户端有TX上下文 那么OrderListManager既可以参与其中 也可以启动一个新的TX 这取决于客户端是否在TX中 以及为OrderListManager方法指定了什么样的TX属性 这同样适用于OrderListManager服务依次调用AuditManager方法的情况
图 装配组件事务
EJB架构允许组件装配者声明式地给出正确的事务属性 从而为他们提供这种灵活性 我们不探讨声明式事务管理的替代方案(即所谓的编程式事务控制) 因为这会牵涉到代码更改 从而产生不同的运行时事务行为 几乎所有的J EE应用服务器都按照X/Open XA规范提供了服从两阶段提交协议的分布式事务管理器 现在的问题是 我们能不能利用EJB服务器来实现相同的功能?Spring就是其中的一种解决方案 让我们来看一下Spring如何帮助我们解决事务组装的问题:
使用Spring进行事务管理
我们将看到一个轻量级的事务基础架构 它实际上可以管理组件级的事务装配 Spring是其中的一个解决方案 它的优点在于 我们不会被捆绑到J EE容器服务(如JNDI DataSource)上 最棒的一点是 如果我们想把这个轻量级事务基础架构关联到一个已可用的J EE容器基础架构 将不会有任何问题 看起来我们可以利用两者的优点
另一方面 Spring这个轻量级事务基础架构使用了一个面向方面编程(Aspect Oriented Programming AOP)框架 Spring AOP框架使用了一个支持AOP的Spring bean工厂 在特定于Spring的配置文件applicationContext xml中 通过在组件服务级指定事务特性来划分事务
<beans>
<! other code goes here >
<bean id= orderListManager class= springframework transaction interceptor TransactionProxyFactoryBean ><property name= transactionManager > <ref local= transactionManager /></property><property name= target > <ref local= orderListManagerTarget /></property><property name= transactionAttributes > <props> <prop key= getAllOrderList > PROPAGATION_REQUIRED </prop> <prop key= getOrderList > PROPAGATION_REQUIRED </prop> <prop key= createOrderList > PROPAGATION_REQUIRED </prop> <prop key= addLineItem > PROPAGATION_REQUIRED example exception FacadeException </prop> <prop key= getAllLineItems > PROPAGATION_REQUIRED readOnly </prop> <prop key= queryNumberOfLineItems > PROPAGATION_REQUIRED readOnly </prop> </props></property></bean>
</beans>
一旦我们在服务级指定了事务属性 springframework transaction PlatformTransactionManager接口的一个特定实现就会截获并解释它们 该接口如下
public interface PlatformTransactionManager TransactionStatus getTransaction (TransactionDefinition definition); void mit(TransactionStatus status); void rollback(TransactionStatus status);
Hibernate事务管理器
由于我们已决定使用Hibernate作为ORM工具 下一步要做的就是配置一个特定于Hibernate的事务管理器实现
<beans>
<! other code goes here >
<bean id= transactionManager class= springframework orm hibernate HibernateTransactionManager > <property name= sessionFactory > <ref local= sessionFactory /> </property></bean>
</beans>
设计多个组件中的事务的管理
现在 我们来讨论什么是 装配组件事务 您也许注意到了为域中的服务级组件OrderListManager所指定的各种TX属性 图 所示的业务域对象模型(Business Domain Object Model BDOM)显示了我们的域所确定的主要对象
图 业务域对象模型(BDOM)
图字 Order 订单 Audit 审计
为了更好的说明 我们来列出我们的域中的一些非功能性需求(Non Functional Requirement NFR)
业务对象需要保存在一个数据库中(appfuse ) 审计时要登录到另一个数据库中(appfuse ) 出于安全的考虑 数据库要有防火墙保护 业务组件应该可以重用 必须尽一切努力审计业务服务层的所有活动
考虑了以上要求之后 我们决定 OrderListManager服务会将所有的审计日志调用委托给已经可用的AuditManager组件 这样就得出了详细设计 如图 所示
图 组件服务的设计
这里值得注意的一点是 由于我们的NFR 我们要将与OrderListManager相关的对象映射到appfuse 数据库 而将与审计相关的对象映射到appfuse 这样 无论要审计什么 OrderListManager组件都会调用AuditManager组件 我们会看到 OrderListManager组件中的所有方法都应该是事务性的 因为我们通过服务来创建订单和线项目(line item) 那么AuditManager组件中的服务呢?因为它做的是审计跟踪 我们关心的是尽可能维持长时间的审计跟踪 并针对系统中所有可能的业务活动 这就产生了如下的需求 即使主要的业务活动失败了 也要进行审计跟踪记录 AuditManager组件同样要有自己的事务 因为它也与自己的数据库进行交互 如下所示
<beans>
<! other code goes here ><bean id= auditManager class= springframework transaction interceptor TransactionProxyFactoryBean > <property name= transactionManager > <ref local= transactionManager /> </property> <property name= target > <ref local= auditManagerTarget /> </property> <property name= transactionAttributes > <props> <prop key= log > PROPAGATION_REQUIRES_NEW </prop> </props> </property></bean>
</beans>
现在 为了演示 我们把注意力放到createOrderList和addLineItem这两个业务服务上 同时请注意 我们并没有要求最佳设计策略——你可能注意到了 addLineItem方法抛出了FacadeException异常 而createOrderList却没有 在生产设计中 您也许希望每一个服务方法都可以处理异常场景
public class OrderListManagerImpl implements OrderListManagerprivate AuditManager auditManager;public Long createOrderList (OrderList orderList) Long orderId = orderListDAO createOrderList(orderList); auditManager log(new AuditObject(ORDER + orderId CREATE)); return orderId;public void addLineItem (Long orderId LineItem lineItem) throws FacadeException Long lineItemId = orderListDAO addLineItem(orderId lineItem); auditManager log(new AuditObject(LINE_ITEM + lineItemId CREATE)); int numberOfLineItems = orderListDAO queryNumberOfLineItems(orderId); if(numberOfLineItems > ) log( Added LineItem + lineItemId + to Order + orderId + ; But rolling back *** ! ); throw new FacadeException( Make a new Order for this line item ); else log( Added LineItem + lineItemId + to Order + orderId + ); //Other code goes here
为了创建一个异常场景来进行演示 我们引入了另一种业务规则 它规定一个特定的订单不能包含多于两个的线项目 现在应该注意 我们是从createOrderList和addLineItem中调用auditManager log()方法的 您应该也注意到了为上述方法所指定的事务属性 <bean id= orderListManager class= springframework transaction interceptor TransactionProxyFactoryBean > <property name= transactionAttributes > <props><prop key= createOrderList > PROPAGATION_REQUIRED </prop> <prop key= addLineItem > PROPAGATION_REQUIRED example exception FacadeException </prop> </props> </property></bean><bean id= auditManager class= springframework transaction interceptor TransactionProxyFactoryBean > <property name= transactionAttributes > <props><prop key= log > PROPAGATION_REQUIRES_NEW </prop> </props> </property></bean>
PROPAGATION_REQUIRED等效于TX_REQUIRED 而PROPAGATION_REQUIRES_NEW等效于EJB中的TX_REQUIRES_NEW 如果我们想让服务方法始终在事务中运行 我们可以使用PROPAGATION_REQUIRED 当使用PROPAGATION_REQUIRED时 如果已经运行了一个TX bean方法就会加入到该TX中 否则的话 Spring的轻量级TX管理器就会启动一个TX 如果在调用组件服务时我们总是希望开始新的事务 那么可以利用PROPAGATION_REQUIRES_NEW属性
我们还指定 当方法抛出FacadeException类型的异常时 addLineItem就总是回滚事务 这就达到了另一个粒度级别 在异常场景中 我们的控制可以精细到TX的具体结束方式 前缀符号 指定回滚TX 而前缀符号 + 指定提交TX
接下来的问题是 为什么我们要为log方法设置PROPAGATION_REQUIRES_NEW属性呢?这是由我们的以下需求决定的 无论主服务方法发生什么情况 对所有创建订单以及向系统添加线项目的尝试都要记录审计跟踪 也就是说 即使在createOrderList和addLineItem的实现过程中出现了异常也要记录审计跟踪 这仅在启动一个新的TX并在这个新的TX上下文中调用log的时候起作用 这就是为什么要为log设置PROPAGATION_REQUIRES_NEW TX属性的原因 如果对下述方法的调用成功了
auditManager log(new AuditObject (LINE_ITEM +lineItemId CREATE));
auditManager log()就将在新的TX上下文中执行 而且只要auditManager log()本身成功(即 没有抛出异常) 新的上下文就会被提交
设置演示环境
准备演示环境时 我参考了Spring Live这本书的流程
- 下载并安装以下组件 这时请注意使用准确的版本 不然就会引起版本不兼容问题 JDK _ _ _ 或更高版本 Apache Tomcat Apache Ant Equinox 在系统中设置以下环境变量 JAVA_HOME CATALINA_HOME ANT_HOME 把下列目录添加到您的PATH环境变量中 或者使用完全路径来执行脚本 JAVA_HOMEinCATALINA_HOMEinANT_HOMEin 要设置Tomcat 在文本编辑器中打开 /conf/tomcat users xml文件 验证以下各行是否存在 如果不存在 必须手动添加进去
<role rolename= manager /> <user username= admin password= admin roles= manager />
- 要创建基于Struts Spring和Hibernate的Web应用程序 必须用Equinox来构建一个基本的框架程序(bare bones starter application) 它将包含预定义的文件夹结构 所有需要用到的 jar文件以及Ant构建脚本 把Equinox解压到一个文件夹中 它将创建一个 equinox文件夹 将目录更改为 equinox文件夹 输入命令ANT_HOMEinnt new Dapp name=myusers 这样就会创建一个与 equinox同级的文件夹 myusers 该文件夹的具体内容如下
图 Equinox的myusers应用程序文件夹模板
删除myuserswebWEB INF文件夹下的所有 xml文件
复制equinoxxtrasstrutswebWEB INFibstruts* jar文件至myuserswebWEB INFib文件夹下 这样 这个示例应用程序就可以利用struts了
从参考资料小节的示例代码中 解压myusersextra zip到一个合适的位置 将目录更改为新创建的myusersextra文件夹 复制myusersextra文件夹中的所有内容 并将它们粘贴到myusers文件夹
打开命令提示符 将目录转至myusers目录下 执行CATALINA_HOMEinstartup 要从myusers文件夹启动Tomcat 这一点非常重要 否则数据库将不会创建在myusers文件夹中 从而导致在执行一些定义在build xml中的任务时出现错误
再次打开命令提示符并将目录转至myusers目录下 执行ANT_HOMEinnt install 这将构建应用程序并把它部署到Tomcat中 这时 我们可以看到myusers中多了一个db目录 以便存放数据库appfuse 和appfuse
打开浏览器并验证myusers应用程序已经部署//localhost /myusers/上了
要重新安装应用程序 执行ANT_HOMEinnt remove 然后执行CATALINA_HOMEinshutdown关闭Tomcat 现在 从CATALINA_HOMEwebapps文件夹删除所有的myusers文件夹 然后执行CATALINA_HOMEinstartup重新启动Tomcat 并通过执行ANT_HOMEinnt install重新安装应用程序
运行演示
为了运行测试用例 myusers estmxampleservice中提供了一个JUnit测试类 OrderListManagerTest 要执行它 可以在构建应用程序的命令提示符中输入以下命令
CATALINA_HOMEinnt test Dtestcase=OrderListManager 测试用例分为两个主要部分 第一部分创建一个由两个线项目组成的订单 然后把这两个线项目链接到订单中 它可以成功运行 如下所示
OrderList orderList = new OrderList();Long orderId = orderListManager createOrderList(orderList );log( Created OrderList with id + orderId + );orderListManager addLineItem(orderId lineItem );orderListManager addLineItem(orderId lineItem );
第二部分执行类似的操作 但是这次我们试图向订单添加三个线项目 这将产生一个异常 OrderList orderList = new OrderList();Long orderId = orderListManager createOrderList(orderList );log( Created OrderList with id + orderId + );orderListManager addLineItem(orderId lineItem );orderListManager addLineItem(orderId lineItem );//We know we will have an exception here still want to proceedtry orderListManager addLineItem (orderId lineItem );catch(FacadeException facadeException) log( ERROR : + facadeException getMessage());
控制台的输出如图 所示
图 客户端控制台输出
我们创建了Order 并向其添加了两个ID为 和 的线项目 然后我们创建Order 并尝试添加 个项目 前两个(ID为 和 )添加成功 但是图 显示 添加第三个项目(ID为 )时业务方法遇到了异常 因此 业务方法TX被回滚 数据库中没有ID为 的线项目 从控制台执行以下命令 就可以通过图 和图 进行验证
CATALINA_HOMEinnt browse
图 appfuse 数据库中创建的订单
图 appfuse 数据库中创建的线项目
在接下来的也是最重要的演示部分中可以看出 订单和线项目保存在appfuse 数据库中 而审计对象保存在appfuse 数据库中 实际上 OrderListManager中的服务方法可以与多个数据库交互 启动appfuse 数据库 查看审计跟踪 如下所示
CATALINA_HOMEinnt browse
图 创建到appfuse 数据库中的审计跟踪 包括失败TX的记录项
表 最后一行尤其值得注意 RESOURCE列显示这一行对应的是LineItem 但是当我们回过来看图 时 却发现并没有对应于LineItem 的线项目 哪里出错了呢?事实上并没有出错 图 没有的那一行其实正是这篇文章的关键所在 让我们来看看是怎么回事
我们知道 addLineItem()方法包含PROPAGATION_REQUIRED属性 而log()方法有PROPAGATION_REQUIRES_NEW属性 而且addLineItem()在内部调用了log()方法 因此 当我们试图向Order 添加第三个线项目时 就(按照我们的业务规则)引发了异常 于是这个线项目的创建以及将其链接到Order 的操作都被回滚了 但是 因为还从addLineItem()中调用了log() 还因为log()具有PROPAGATION_REQUIRES_NEW TX属性 回滚addLineItem()将不会造成回滚log() 因为log()是在一个新的TX中执行
让我们对log()的TX属性做一下改动 把PROPAGATION_REQUIRES_NEW替换为PROPAGATION_SUPPORTS PROPAGATION_SUPPORTS属性允许服务方法在客户端有TX上下文时在客户端TX中运行 如果客户端没有TX 就不用TX而直接运行 您可能必须重新安装应用程序 以便数据库中已经可用的数据可以自动刷新 重新安装请按照 设置演示环境 中的步骤 进行
如果再次运行 我们会发现一点不同 这次 在试图向Order 添加第三个线项目时依然有异常 这将回滚试图添加第三个线项目的事务 而这个方法又调用了log()方法 但是由于它的PROPAGATION_SUPPORTS TX属性 log()将在与addLineItem()方法相同的TX上下文中调用 由于addLineItem()回滚 log()也回滚了 没有留下回滚的TX的审计跟踪 所以在图 中没有对应于失败TX的审计跟踪记录项!
图 创建在appfuse 数据库中的审计跟踪 没有失败TX的记录项
我们改动的仅仅是Spring配置文件中的TX属性 就产生了这样不同的事务行为 如下所示 <bean id= auditManager class= springframework transaction interceptor TransactionProxyFactoryBean > <property name= transactionAttributes > <props><! prop key= log > PROPAGATION_REQUIRES_NEW </prop > <prop key= log > PROPAGATION_SUPPORTS </prop> </props> </property></bean>
这就是声明式事务管理的效果 自从EJB出现以来我们就一直在使用这种方法 但是我们需要一个高端的应用服务器来驻留EJB组件 现在 我们可以看到 利用Spring 没有EJB服务器也可以达到类似的结果
结束语 这篇文章重点介绍了J EE领域的强大组合之一 Spring和Hibernate 利用二者的功能 现在对于容器管理持久性(Container Managed Persistence CMP) 容器管理关系(Container Managed Relationships CMR)和声明式事务管理 我们多了一种技术选择 虽然Spring不能视为EJB的替代方案 但是它提供的许多功能 例如普通Java对象的声明式事务管理 使得在许多项目中没有EJB也完全可以
cha138/Article/program/Java/ky/201311/28139相关参考
知识大全 SPring管理Hibernate事务出现异常处理
SPring管理Hibernate事务出现异常处理 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!
Spring配置事务在DAO层和业务逻辑层 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!Sprin
<!构建HibernateTransactionManager用于获得session管理事务> <beanid=transactionManagerclass=springfr
对Spring事务配置方式的深入研究 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 前段时间对S
Spring事务配置的五种方式 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 前段时间对Spri
Spring中的四种声明式事务的配置 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! Spring
知识大全 Spring 结合 Hibernate 配置 C3P0
Spring结合Hibernate配置C3P0 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! a
以前项目中经常用spring事务处理还没有亲自配置过惭愧现在马上上路. 首先在spring容器中配置transactionManager这个有好多实现这里以HibernateTransactio
知识大全 spring+hibernate+jbpm整合成功
终于搞定了在此感谢chenjin的指点 从日整合失败后这块就一直是我的心病我甚至都跑去了去发了一个帖这还是我第一次用英文问问题呢 最后的配置结果是 hibernatecfgxmljbpmcf
代码管理的事务处理TransactonTemplate的execute方法中的内部类TransactionCallback中的doInTransaction方法中使用publicvoidmake()?