spring使用Hibernate配置双数据源事务问题怎么解决

spring使用Hibernate配置双数据源事务问题怎么解决,第1张

在SpringSide 3 中,白衣提供的预先配置好的环境非常有利于用户进行快速开发,但是同时也会为扩展带来一些困难。最直接的例子就是关于在项目中使用多个数据源的问题,似乎 很难搞。在上一篇中,我探讨了SpringSide 3 中的数据访问层,在这一篇中,我立志要解决多数据源配置的难题,我的思路是这样的:

第一步、测试能否配置多个DataSource

第二步、测试能否配置多个SessionFactory

第三步、测试能否配置多个TransactionManager

第四步、测试能否使用多个TransactionManager,也就是看能否配置多个

基本上到第四步就应该走不通了,因为Spring中似乎不能配置多个,而且@transactional注解也无法让用户选择具体使用哪个TransactionManager。也就是说,在SpringSide的应用中,不能让不同的数据源分别属于不同的事务管理器,多数据源只能使用分布式事务管理器,那么测试思路继续如下进行:

第五步、测试能否配置JTATransactionManager

如果到这一步,项目还能顺利在Tomcat中运行的话,我们就算大功告成了。但我总认为事情不会那么顺利,我总觉得JTATransactionManager需要应用服务器的支持,而且需要和JNDI配合使用,具体是不是这样,那只有等测试后才知道。如果被我不幸言中,那么进行下一步:

第六步、更换Tomcat为GlassFish,更换JDBC的DataSource为JNDI查找的DataSource,然后配置JTATransactionManager

下面测试开始,先假设场景,还是继续用上一篇中提到的简单的文章发布系统,假设该系统运行一段时间后非常火爆,单靠一台服务器已经无法支持巨大的用户数, 这时候,站长想到了把数据进行水平划分,于是,需要建立一个索引数据库,该索引数据库需保存每一篇文章的Subject及其内容所在的Web服务器,而每 一个Web服务器上运行的项目,需要同时访问索引数据库和内容数据库。所以,需要创建索引数据库,如下:

[java] view plain copy print

create database puretext_index;

use puretext_index;

create table articles(

id int primary key auto_increment,

subject varchar(256),

webserver varchar(30)

);

create database puretext_index;

use puretext_index;

create table articles(

id int primary key auto_increment,

subject varchar(256),

webserver varchar(30)

);

第一步测试,配置多个DataSource,配置文件如下:

applicationproperties

Spring+Hibernate的实质:

就是把Hibernate用到的数据源Datasource,Hibernate的SessionFactory实例,事务管理器HibernateTransactionManager,都交给Spring管理。

那么再没整合之前Hibernate是如何实现事务管理的呢?

通过ServletFilter实现数据库事务的管理,这样就避免了在数据库 *** 作中每次都要进行数据库事务处理。

一事务的4个特性:

原子性:一个事务中所有对数据库的 *** 作是一个不可分割的 *** 作序列,要么全做,要么全部做。

一致性:数据不会因为事务的执行而遭到破坏。

隔离性:一个事务的执行,不受其他事务(进程)的干扰。既并发执行的个事务之间互不干扰。

持久性:一个事务一旦提交,它对数据库的改变将是永久的。

二事务的实现方式:

实现方式共有两种:编码方式;声明式事务管理方式。

基于AOP技术实现的声明式事务管理,实质就是:在方法执行前后进行拦截,然后在目标方法开始之前创建并加入事务,执行完目标方法后根据执行情况提交或回滚事务。

声明式事务管理又有两种方式:基于XML配置文件的方式;另一个是在业务方法上进行@Transactional注解,将事务规则应用到业务逻辑中。

三创建事务的时机:

是否需要创建事务,是由事务传播行为控制的。读数据不需要或只为其指定只读事务,而数据的插入,修改,删除就需要事务管理了。

一种常见的事务管理配置:事务拦截器TransactionInterceptor和事务自动代理BeanNameAutoProxyCreator相结合的方式

<!--定义Hibernate的事务管理器HibernateTransactionManager -->

<bean id="transactionManager"

class="orgspringframeworkormhibernate3HibernateTransactionManager">

<!-- 依赖注入上面定义的sessionFactory -->

<property name="sessionFactory" ref="sessionFactory"/>

</bean>

<!--定义Spring的事务拦截器TransactionInterceptor -->

<bean id="transactionInterceptor" class="orgspringframeworktransactioninterceptorTransactionInterceptor">

<!-- 依赖注入上面定义的事务管理器transactionManager -->

<property name="transactionManager" ref="transactionManager"/>

<!-- 定义需要进行事务拦截的方法及所采用的事务控制类型 -->

<property name="transactionAttributes">

<props>

<!-- 以browse、list、load、get及is开头的所有方法采用只读型事务控制类型 -->

<prop key="browse">PROPAGATION_REQUIRED,readOnly</prop>

<prop key="list">PROPAGATION_REQUIRED,readOnly</prop>

<prop key="load">PROPAGATION_REQUIRED,readOnly</prop>

<prop key="get">PROPAGATION_REQUIRED,readOnly</prop>

<prop key="is">PROPAGATION_REQUIRED,readOnly</prop>

<!-- 所有方法均进行事务控制,如果当前没有事务,则新建一个事务 -->

<prop key="">PROPAGATION_REQUIRED</prop>

</props>

</property>

</bean>

<!-- 定义BeanNameAutoProxyCreatorf进行Spring的事务处理-->

<bean class="orgspringframeworkaopframeworkautoproxyBeanNameAutoProxyCreator">

<!-- 针对指定的bean自动生成业务代理 -->

<property name="beanNames">

<list>

<value>adminService</value>

<value>columnsService</value>

<value>newsService</value>

<value>crawlService</value>

<value>memberLevelService</value>

<value>memberService</value>

<value>categoryService</value>

<value>merService</value>

<value>cartService</value>

<value>ordersService</value>

<value>trafficService</value>

</list>

</property>

<!-- 这个属性为true时,表示被代理的是目标类本身而不是目标类的接口 -->

<property name="proxyTargetClass">

<value>true</value>

</property>

<!-- 依赖注入上面定义的事务拦截器transactionInterceptor -->

<property name="interceptorNames">

<list>

<value>transactionInterceptor</value>

</list>

</property>

</bean>

尤其注意:如下

以上的事务拦截器和事务自动代理方式实现原理:像Struts2一样,都是凭借强大的拦截器功能对业务逻辑方法的调用进行拦截,然后又BeanNameAutoProxyCreator自动生成事务代理,最后送事务管理器,统一管理

Spring+Hibernate的实质:

就是把Hibernate用到的数据源Datasource,Hibernate的SessionFactory实例,事务管理器HibernateTransactionManager,都交给Spring管理。

那么再没整合之前Hibernate是如何实现事务管理的呢?

通过ServletFilter实现数据库事务的管理,这样就避免了在数据库 *** 作中每次都要进行数据库事务处理。

一事务的4个特性:

原子性:一个事务中所有对数据库的 *** 作是一个不可分割的 *** 作序列,要么全做,要么全部做。

一致性:数据不会因为事务的执行而遭到破坏。

隔离性:一个事务的执行,不受其他事务(进程)的干扰。既并发执行的个事务之间互不干扰。

持久性:一个事务一旦提交,它对数据库的改变将是永久的。

二事务的实现方式:

实现方式共有两种:编码方式;声明式事务管理方式。

基于AOP技术实现的声明式事务管理,实质就是:在方法执行前后进行拦截,然后在目标方法开始之前创建并加入事务,执行完目标方法后根据执行情况提交或回滚事务。

声明式事务管理又有两种方式:基于XML配置文件的方式;另一个是在业务方法上进行@Transactional注解,将事务规则应用到业务逻辑中。

三创建事务的时机:

是否需要创建事务,是由事务传播行为控制的。读数据不需要或只为其指定只读事务,而数据的插入,修改,删除就需要事务管理了。

一种常见的事务管理配置:事务拦截器TransactionInterceptor和事务自动代理BeanNameAutoProxyCreator相结合的方式

<!--定义Hibernate的事务管理器HibernateTransactionManager -->

<bean id="transactionManager"

class="orgspringframeworkormhibernate3HibernateTransactionManager">

<!-- 依赖注入上面定义的sessionFactory -->

<property name="sessionFactory" ref="sessionFactory"/>

</bean>

<!--定义Spring的事务拦截器TransactionInterceptor -->

<bean id="transactionInterceptor" class="orgspringframeworktransactioninterceptorTransactionInterceptor">

<!-- 依赖注入上面定义的事务管理器transactionManager -->

<property name="transactionManager" ref="transactionManager"/>

<!-- 定义需要进行事务拦截的方法及所采用的事务控制类型 -->

<property name="transactionAttributes">

<props>

<!-- 以browse、list、load、get及is开头的所有方法采用只读型事务控制类型 -->

<prop key="browse">PROPAGATION_REQUIRED,readOnly</prop>

<prop key="list">PROPAGATION_REQUIRED,readOnly</prop>

<prop key="load">PROPAGATION_REQUIRED,readOnly</prop>

<prop key="get">PROPAGATION_REQUIRED,readOnly</prop>

<prop key="is">PROPAGATION_REQUIRED,readOnly</prop>

<!-- 所有方法均进行事务控制,如果当前没有事务,则新建一个事务 -->

<prop key="">PROPAGATION_REQUIRED</prop>

</props>

</property>

</bean>

<!-- 定义BeanNameAutoProxyCreatorf进行Spring的事务处理-->

<bean class="orgspringframeworkaopframeworkautoproxyBeanNameAutoProxyCreator">

<!-- 针对指定的bean自动生成业务代理 -->

<property name="beanNames">

<list>

<value>adminService</value>

<value>columnsService</value>

<value>newsService</value>

<value>crawlService</value>

<value>memberLevelService</value>

<value>memberService</value>

<value>categoryService</value>

<value>merService</value>

<value>cartService</value>

<value>ordersService</value>

<value>trafficService</value>

</list>

</property>

<!-- 这个属性为true时,表示被代理的是目标类本身而不是目标类的接口 -->

<property name="proxyTargetClass">

<value>true</value>

</property>

<!-- 依赖注入上面定义的事务拦截器transactionInterceptor -->

<property name="interceptorNames">

<list>

<value>transactionInterceptor</value>

</list>

</property>

</bean>

尤其注意:如下

以上的事务拦截器和事务自动代理方式实现原理:像Struts2一样,都是凭借强大的拦截器功能对业务逻辑方法的调用进行拦截,然后又BeanNameAutoProxyCreator自动生成事务代理,最后送事务管理器,统一管理

一个项目中使用多个数据源的需求,我们在日常工作中时常会遇到。

以商城系统为例,有一个 MySQL 的数据库负责存储交易数据。公司还有一套 ERP 企业信息化管理系统,要求订单信息同步录入 ERP 数据库,便于公司统一管理,而该 ERP 系统采用的数据库为 SQL Server 。

此时,就可以在 Spring Boot 项目中配置多个数据源。另外,使用多数据源后,需要采用分布式事务来保持数据的完整性。

本小节我们使用 Spring Boot 开发一个商城系统的订单生成功能,订单信息同时进入 MySQL 与 SQL Server 数据库。

首先创建 MySQL 数据库 shop ,并新建订单表 order ,表结构如下:

order 表结构

然后创建 SQL Server 数据库 erpshop ,并新建订单表 erp_order ,表结构如下。注意 id 是自增长的唯一标识,out_id 是对应订单在 MySQL 数据库中的唯一标识,以便在两个库中比对订单。

erp_order 结构

接下来,我们开始实现 Spring Boot 后端项目,数据持久层采用 MyBatis 框架,同时访问两个数据源。

Spring Boot 版本选择 225 ,Group 为 comimooc , Artifact 为 spring-boot-multidb,生成项目后导入 Eclipse 开发环境。

我们引入热部署依赖、 Web 依赖、数据库访问相关依赖及测试相关依赖,具体如下:

实例:

由于我们要同时访问两个数据库,所以需要在配置文件中添加两个数据源的配置信息。注意配置多数据源时, url 配置需要使用 springdatasourcedb1jdbc-url=xxx 的形式。

实例:

多个数据源的情况下, 我们需要通过配置类,将数据源注册为组件放入 Spring 容器中。

实例:

通过这个配置类, Spring 容器中就有两个数据源组件,这两个组件分别采用 springdatasourcedb1 和 springdatasourcedb2 开头的配置信息。所以通过这两个组件,就能分别 *** 作 MySQL 数据源 1 和 SQL Sever 数据源 2 。

多数据源情况下, MyBatis 中的关键组件 SqlSessionFactory 和 SqlSessionTemplate 也需要单独配置,我们需要为两个数据源分别配置一套组件。

实例:

通过上面的配置类, comimoocspringbootmultidbmapper1 包中的 DAO 数据访问接口会自动调用 sqlSessionTemplate1 组件实现具体数据库 *** 作,而 sqlSessionTemplate1 *** 作的数据源已经通过配置类设置为 db1 。同时, DAO 数据访问接口对应的映射文件已经指定到 classpath:mapper1/ 目录去寻找。这样数据源 – DAO 数据访问接口 – 映射文件三者的对应关系就建立起来了。

数据源 2 的配置方法是一样的, comimoocspringbootmultidbmapper2 包中的 DAO 数据访问接口会自动调用 sqlSessionTemplate2 组件,其 *** 作的数据源即为 db2 ,其对应的映射文件指定到 classpath:mapper2/ 目录去寻找。

实例:

数据访问接口的位置已经在配置类指定,首先在 comimoocspringbootmultidbmapper1 创建 OrderDao , *** 作的是数据源 1 中的 order 表。

实例:

然后在 comimoocspringbootmultidbmapper2 创建 ErpOrderDao , *** 作的是数据源 2 中的 erporder 表。

实例:

这两个接口中使用的数据对象比较简单,代码如下:

实例:

分别针对 OrderDao 、 ErpOrderDao 编写对应的映射文件,然后按照配置类指定的位置,两个文件分别放到 resources/mapper1 和 resources/mapper2 目录下。

实例:

实例:

数据 *** 作接口与对应的映射文件均已编写完毕,现在可以通过测试类进行多数据源测试了,我们在测试类中同时向两个库插入记录。

实例:

运行测试方法后,两个数据库表中均新增数据成功,这样我们就成功的使用 Spring Boot 同时 *** 作了两个数据源。

采用多数据源之后,事务的实现方式也随之发生变化。当某个数据源 *** 作出现异常时,该数据源和其他数据源的事务都需要回滚。这种涉及多个数据源的事务,称为分布式事务,接来下我们就来具体实现一下。

在 pomxml 引入 Atomikos 事务管理器相关的依赖项, Atomikos 是一个开源的事务管理器,支持分布式事务。

实例:

需要将默认的数据源更换为支持分布式事务的数据源, MySQL 对应的数据源为 MysqlXADataSource , SQL Server 对应的数据源为 SQLServerXADataSource 。

实例:

继续修改 DataSourceConfig 类,在其中配置分布式事务管理器组件。当项目中使用事务时,会通过配置的分布式事务管理器管理分布式事务 *** 作。

实例:

在测试方法上添加 @Transactional 开启事务,然后在两个数据源 *** 作中间模拟抛出异常。

实例:

此时运行测试类,可以发现数据源 1 的事务已回滚,验证成功!

在开发 Spring Boot 项目时,如果默认配置满足不了我们的需求,可以通过手工配置组件实现我们需要的功能。这些组件可能是各个公司提供的,我们根据相应文档,为其配置各个属性即可。

给你一个我的配置文件你看看吧。

<!-- 配置sessionFactory -->

<bean id="sessionFactory" class="orgspringframeworkormhibernate3LocalSessionFactoryBean">

<property name="configLocation">

<value>classpath:hibernatecfgxml</value>

</property>

</bean>

<!-- 配置事务管理器 -->

<bean id="transactionManager" class="orgspringframeworkormhibernate3HibernateTransactionManager">

<property name="sessionFactory">

<ref bean="sessionFactory"/>

</property>

</bean>

<!-- 配置事务的传播特性 -->

<tx:advice id="txAdvice" transaction-manager="transactionManager">

<tx:attributes>

<tx:method name="add" propagation="REQUIRED"/>

<tx:method name="del" propagation="REQUIRED"/>

<tx:method name="modify" propagation="REQUIRED"/>

<tx:method name="" propagation="REQUIRED"/>

</tx:attributes>

</tx:advice>

<!-- 那些类的哪些方法参与事务 -->

<aop:config>

<aop:pointcut id="allManagerMethod" expression="execution( comesoftsalemanager())"/>

<aop:advisor pointcut-ref="allManagerMethod" advice-ref="txAdvice"/>

</aop:config>

使用步骤:

事务的传播行为和隔离级别

大家在使用spring的注解式事务管理时,对事务的传播行为和隔离级别可能有点不知所措,下边就详细的介绍下以备方便查阅。

事物注解方式: @Transactional

当标于类前时, 标示类中所有方法都进行事物处理 , 例子:

当类中某些方法不需要事物时:

事物超时设置:

@Transactional(timeout=30) //默认是30秒

事务隔离级别:

@Transactional(isolation = IsolationREAD_UNCOMMITTED)

读取未提交数据(会出现脏读, 不可重复读) 基本不使用

@Transactional(isolation = IsolationREAD_COMMITTED)

读取已提交数据(会出现不可重复读和幻读)

@Transactional(isolation = IsolationREPEATABLE_READ)

可重复读(会出现幻读)

@Transactional(isolation = IsolationSERIALIZABLE)

串行化

MYSQL: 默认为REPEATABLE_READ级别

SQLSERVER: 默认为READ_COMMITTED

脏读 : 一个事务读取到另一事务未提交的更新数据

不可重复读 : 在同一事务中, 多次读取同一数据返回的结果有所不同, 换句话说,

后续读取可以读到另一事务已提交的更新数据 相反, "可重复读"在同一事务中多次

读取数据时, 能够保证所读数据一样, 也就是后续读取不能读到另一事务已提交的更新数据

幻读 : 一个事务读到另一个事务已提交的insert数据

@Transactional注解中常用参数说明

续表)

注意的几点:

1 @Transactional 只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能

2用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚默认遇到运行期例外(throw new RuntimeException("注释");)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception("注释");)不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需我们指定方式来让事务回滚 要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exceptionclass,其它异常}) 如果让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeExceptionclass)

如下:

@Transactional(rollbackFor=Exceptionclass) //指定回滚,遇到异常Exception时回滚

public void methodName() {

throw new Exception("注释");

}

@Transactional(noRollbackFor=Exceptionclass)//指定不回滚,遇到运行期例外(throw new RuntimeException("注释");)会回滚

public ItimDaoImpl getItemDaoImpl() {

throw new RuntimeException("注释");

}

3、@Transactional 注解应该只被应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。

4、@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。然而,请注意仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅 是一种元数据,能够被可以识别 @Transactional 注解和上述的配置适当的具有事务行为的beans所使用。上面的例子中,其实正是 元素的出现 开启 了事务行为。

5、Spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。你当然可以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是 不能继承 的,这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装(将被确认为严重的)。因 此,请接受Spring团队的建议并且在具体的类上使用 @Transactional 注解。

本质上其实是同一个概念,spring的事务是对数据库的事务的封装,最后本质的实现还是在数据库,假如数据库不支持事务的话,spring的事务是没有作用的数据库的事务说简单就只有开启,回滚和关闭,spring对数据库事务的包装,原理就是拿一个数据连接,根据spring的事务配置, *** 作这个数据连接对数据库进行事务开启,回滚或关闭 *** 作但是spring除了实现这些,还配合spring的传播行为对事务进行了更广泛的管理其实这里还有个重要的点,那就是事务中涉及的隔离级别,以及spring如何对数据库的隔离级别进行封装事务与隔离级别放在一起理解会更好些以上回答希望能帮助到你

在SpringMVC中,我们可以使用注解的方式配置事务,从而避免使用XML进行配置。具体步骤如下:

1 首先,在SpringMVC的配置类中加入@EnableTransactionManagement注解,开启事务支持。

2 在需要进行事务管理的方法上,使用@Transactional注解,标注该方法需要进行事务管理。同时,可以在注解中指定事务管理的属性,如事务传播属性、隔离级别、超时时间等等。

3 在SpringMVC的配置类中,需要配置一个事务管理器。可以使用Spring提供的DataSourceTransactionManager或者Hibernate的HibernateTransactionManager。在配置事务管理器时,需要指定数据源和事务管理属性。

4 如果需要在同一事务中进行多个数据库 *** 作,可以将多个 *** 作放在同一个方法中,并使用@Transactional注解进行标注。这样,在方法执行时,就会自动开启一个事务,并将多个 *** 作放在同一个事务中进行。

使用注解配置事务可以简化配置过程,减少代码量,提高开发效率。同时,也可以使代码更加清晰易懂,便于维护。不过,在使用注解配置事务时,需要注意事务的传播属性和隔离级别,避免出现事务管理的问题。

以上就是关于spring使用Hibernate配置双数据源事务问题怎么解决全部的内容,包括:spring使用Hibernate配置双数据源事务问题怎么解决、spring的事务管理有几种方式实现,如何实现、15《Spring Boot 入门教程》多数据源与分布式事务等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/sjk/10173381.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-06
下一篇 2023-05-06

发表评论

登录后才能评论

评论列表(0条)

保存