多数据源 spring怎么管理事务的

多数据源 spring怎么管理事务的,第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
create database puretext_index;
use puretext_index;
create table articles(
id int primary key auto_increment,
subject varchar(256),
webserver varchar(30)
);
第一步测试,配置多个DataSource,配置文件如下:
applicationproperties:
[java] view plain copy
jdbcurlContent=jdbc:mysql://localhost:3306/PureText useUnicode=true&characterEncoding=utf8
jdbcurlIndex=jdbc:mysql://localhost:3306/PureText_Index useUnicode=true&characterEncoding=utf8

@EnableTransactionManagement // 启注解事务管理,等同于xml配置方式的 <tx:annotation-driven />
@SpringBootApplication
public class ProfiledemoApplication {
@Bean
public Object testBean(PlatformTransactionManager platformTransactionManager){
Systemoutprintln(">>>>>>>>>>" + platformTransactionManagergetClass()getName());
return new Object();
}
public static void main(String[] args) {
SpringApplicationrun(ProfiledemoApplicationclass, args);
}
}

第一种方式:每个Bean都有一个代理

<bean id="sessionFactory"

class="orgspringframeworkormhibernate3LocalSessionFactoryBean">

<property name="configLocation" value="classpath:hibernatecfgxml" />

<property name="configurationClass" value="orghibernatecfgAnnotationConfiguration" />

</bean>

<!-- 定义事务管理器(声明式的事务) -->

<bean id="transactionManager"

class="orgspringframeworkormhibernate3HibernateTransactionManager">

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

</bean>

<!-- 配置DAO -->

<bean id="userDaoTarget" class="comblueskyspringdaoUserDaoImpl">

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

</bean>

<bean id="userDao"

class="orgspringframeworktransactioninterceptorTransactionProxyFactoryBean">

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

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

<property name="target" ref="userDaoTarget" />

<property name="proxyInterfaces" value="comblueskyspringdaoGeneratorDao" />

<!-- 配置事务属性 -->

<property name="transactionAttributes">

<props>

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

</props>

</property>

</bean>

</beans>

第二种方式:所有Bean共享一个代理基类

<bean id="sessionFactory"

class="orgspringframeworkormhibernate3LocalSessionFactoryBean">

<property name="configLocation" value="classpath:hibernatecfgxml" />

<property name="configurationClass" value="orghibernatecfgAnnotationConfiguration" />

</bean>

<!-- 定义事务管理器(声明式的事务) -->

<bean id="transactionManager"

class="orgspringframeworkormhibernate3HibernateTransactionManager">

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

</bean>

<bean id="transactionBase"

class="orgspringframeworktransactioninterceptorTransactionProxyFactoryBean"

lazy-init="true" abstract="true">

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

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

<!-- 配置事务属性 -->

<property name="transactionAttributes">

<props>

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

</props>

</property>

</bean>

<!-- 配置DAO -->

<bean id="userDaoTarget" class="comblueskyspringdaoUserDaoImpl">

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

</bean>

<bean id="userDao" parent="transactionBase" >

<property name="target" ref="userDaoTarget" />

</bean>

</beans>

第三种方式:使用拦截器

<bean id="sessionFactory"

class="orgspringframeworkormhibernate3LocalSessionFactoryBean">

<property name="configLocation" value="classpath:hibernatecfgxml" />

<property name="configurationClass" value="orghibernatecfgAnnotationConfiguration" />

</bean>

<!-- 定义事务管理器(声明式的事务) -->

<bean id="transactionManager"

class="orgspringframeworkormhibernate3HibernateTransactionManager">

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

</bean>

<bean id="transactionInterceptor"

class="orgspringframeworktransactioninterceptorTransactionInterceptor">

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

<!-- 配置事务属性 -->

<property name="transactionAttributes">

<props>

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

</props>

</property>

</bean>

<bean class="orgspringframeworkaopframeworkautoproxyBeanNameAutoProxyCreator">

<property name="beanNames">

<list>

<value>Dao</value>

</list>

</property>

<property name="interceptorNames">

<list>

<value>transactionInterceptor</value>

</list>

</property>

</bean>

<!-- 配置DAO -->

<bean id="userDao" class="comblueskyspringdaoUserDaoImpl">

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

</bean>

</beans>

第四种方式:使用tx标签配置的拦截器

<bean id="sessionFactory"

class="orgspringframeworkormhibernate3LocalSessionFactoryBean">

<property name="configLocation" value="classpath:hibernatecfgxml" />

<property name="configurationClass" value="orghibernatecfgAnnotationConfiguration" />

</bean>

<!-- 定义事务管理器(声明式的事务) -->

<bean id="transactionManager"

class="orgspringframeworkormhibernate3HibernateTransactionManager">

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

</bean>

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

<tx:attributes>

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

</tx:attributes>

</tx:advice>

<aop:config>

<aop:pointcut id="interceptorPointCuts"

expression="execution( comblueskyspringdao())" />

<aop:advisor advice-ref="txAdvice"

pointcut-ref="interceptorPointCuts" />

</aop:config>

</beans>

第五种方式:全注

<tx:annotation-driven transaction-manager="transactionManager"/>

<bean id="sessionFactory"

class="orgspringframeworkormhibernate3LocalSessionFactoryBean">

<property name="configLocation" value="classpath:hibernatecfgxml" />

<property name="configurationClass" value="orghibernatecfgAnnotationConfiguration" />

</bean>

<!-- 定义事务管理器(声明式的事务) -->

<bean id="transactionManager"

class="orgspringframeworkormhibernate3HibernateTransactionManager">

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

</bean>

</beans>

多线程的困惑

由于Spring事务管理器是通过线程相关的ThreadLocal来保存数据访问基础设施 再结合IOC和AOP实现高级声明式事务的功能 所以Spring的事务天然地和线程有着千丝万缕的联系

我们知道Web容器本身就是多线程的 Web容器为一个>

大家好,一直以来我都本着用最通俗的话理解核心的知识点, 我认为所有的难点都离不开 「基础知识」 的铺垫。目前正在出一个 SpringBoot 长期系列教程,从入门到进阶, 篇幅会较多~

「大佬可以绕过 ~」

如果你是一路看过来的,很高兴你能够耐心看完。之前带大家学了 Springboot 基础部分,对基本的使用有了初步的认识, 接下来的几期内容将会带大家进阶使用,会先讲解基础 中间件 的使用和一些场景的应用,或许这些技术你听说过,没看过也没关系,我会带大家一步一步的入门,耐心看完你一定会有 收获 ~

上期带大家学习了 SpringBoot 中如何去拦截请求, 本期将带大家学习 MyBatis 中如何进行 事务管理 ,同样的,我们集成到 Springboot 中。最近github可能会被墙,所以我把源码放到了国内gitee上,本节我们依然使用上期的代码

我们先了解一下它的基本概念。其实 事务 它不仅是在这里我们提到的 mybatis ,其实它在数据库中也是存在的。 事务 我们从字面意思理解,它好比烤面包,经过一些列的步骤之后,最终提供给客户完整的面包,也就是说中间出现差错,就得回退。可能举这个例子不大合适,再举一个我们业务中的场景吧。用户购买一个商品,首先下单,下完单之后进行支付,支付成功后订单为支付成功状态,跳转成功页,这一系列 *** 作就是一个事务,要么成功要么失败。

在通过上面的例子有了大概了解之后,我们再看看它的基本概念。

下面带大家看看 sql 如何执行事务 *** 作。下面举个例子比较一下

没有事务 *** 作的时候:

以之前的场景给大家举例, 用户支付减少余额 并改订单状态为成功。 当我们的程序执行了上边的两条 sql ,大家觉得有问题吗?这肯定得出事,这不得被人薅死。虽然语句没报错,但是逻辑错了,为啥 因为余额变成负数了,这不是没钱白嫖,还指望用户给你冲上吗。然后订单还给成功了,如果遇到并发大的时候,这得多少钱,发还是不发货呢?告诉用户系统问题?老板看了得哭死。

所以不管是程序上的错误(sql执行错误),还是逻辑上的错误都不能进行下一步 *** 作,所以事务显的尤为重要。那么 sql 怎么提交事务呢

上边只是给大家举个例子,生成中我们还得用 mybatis 去 *** 作。

SpringBoot 中执行事务非常简单,首先要开启事务 @EnableTransactionManagement ,在启动类上加上:

添加控制器方法:

我们访问 >Spring框架简介
Spring框架是一个2003年2月才出现的开源项目,该开源项目起源自Rod Johnson在2002年末出版的《Expert One-on-One J2EE Design and Development》一书中的基础性代码。在该书中,Rod Johnson倡导J2EE实用主义的设计思想,而Spring框架正是这一思想的更全面和具体的实现。Spring框架由一个容器,一个配置和组织组件 的框架,和一组内置的为事务、持久化和Web用户接口提供的服务组成。作为一种轻量级的J2EE框架,Spring提供了一种有效的方式来建立和组织 J2EE应用程序。
Spring特性
IoC(Inversion of Control;控制反转);又称DI(Dependency Injection;依赖注入);是面向对象领域新兴的编程思想;也是Spring的精髓所在。简单地说;IoC就是指程序之间的关系由容器来控制;而不 是传统实现中由程序代码直接 *** 控。这也就是所谓“控制反转”的概念所在:控制权由应用代码转到外部容器,控制权的转移,也就是所谓的反转。IoC将控制创 建的职责搬进了框架中;并把它从应用代码脱离开来。当使用Spring的IoC容器时只需指出组件需要的对象,在运行时Spring的IoC容器会根据 XML配置数据提供给它。
Spring IoC,借助于依赖注入设计模式,使得开发者不用理会对象自身的生命周期及其关系,而且能够改善开发者对模式的使用。对于一个对象的管理不是什么困难,难 就难在对整个对象群的管理。依赖注入可以让容器管理对象,即“Don’t call me, I will call you”。这样对象本身的生命周期以及对象之间的关系就不再让开发者费神了。
Spring AOP,借助于Spring实现拦截器,开发者能够实现以声名方式使用企业级服务,比如安全性服务、事务服务。AOP 合理的补充了OOP,借助于Spring AOP,开发者能够高效的使用J2EE服务。
Spring服务抽象,借助于各种J2EE API抽象,使得开发者能够一致地使用J2EE 技术,而不管具体是使用什么J2EE API,借助于Spring服务抽象,使代码大大减少,满足“更少代码,更少BUG”的软件设计原则。
Spring IoC+Spring AOP+Spring服务抽象,一起形成Spring,这样一个有机体,使构建轻量级J2EE成为可能。
Spring事务管理
Spring事务管理可以分为两类:编程式的和声明式的。编程式的,比较灵活,但是代码量大,存在重复的代码比较多;声明式的比编程式的更灵活方便。
1传统使用JDBC的事务管理
以往使用JDBC进行数据 *** 作,使用DataSource,从数据源中得到Connection,我们知道数据源是线程安全的,而连接不是线程安全 的,所以对每个请求都是从数据源中重新取出一个连接。一般的数据源由容器进行管理,包括连接池。例如TOMCAT,WEBSPHERE,WEBLOGIC 等这些J2EE商业容器都提供了这个功能。
以往的我们使用JDBC在写代码时,事务管理可能会是这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Connection conn = null;
try{
conn = DBConnectionFactorygetConnection;
connsetAutoCommit(false);
//do something
conncommit(); //commit transcation
}catch(Exception e)
finally catch(SQLException se){ //do sth}
//close ResultSet,PreparedStatement,Connection
//notice:Maybe ocurr Exception when u close rs,pstmt,conn
}
按照以往的思路来写代码,代码量比较长,而且容易疏忽,忘掉一些try/catch,引发一些异常无法catch,虽然有时候我们会写DBTool类,来关闭这些资源,并且保证在关闭这些资源时,不向外抛异常,但是这样做会导致额外的麻烦。
2Spring事务管理提供的编程式
Spring提供了几个关于事务处理的类:
TransactionDefinition //事务属性定义
TranscationStatus //代表了当前的事务,可以提交
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>

两种Spring事务管理方式:编程式、声明式。
Spring提供两种方式的编程式事务管理,分别是:使用TransactionTemplate和直接使用PlatformTransactionManager。
1TransactionTempale采用和其他Spring模板,如JdbcTempalte和HibernateTemplate一样的方法。它使用回调方法,把应用程序从处理取得和释放资源中解脱出来。如同其他模板,TransactionTemplate是线程安全的。代码片段:
Object result = ttexecute(new TransactionCallback(){
public Object doTransaction(TransactionStatus status){
updateOperation();
return resultOfUpdateOperation();
}
});
使用TransactionCallback()可以返回一个值。如果使用TransactionCallbackWithoutResult则没有返回值。
2也可以使用PlatformTransactionManager直接管理事务。简单地通过一个bean引用给你的bean传递一个你使用的 PlatformTransaction对象。然后,使用TransactionDefinition和TransactionStatus对象就可以发起、回滚、提交事务。如下片段:
DefaultTransactionDefinition def= new DefaultTransactionDefinition(); //new 一个事务 defsetPropagationBehavior(TransactionDefinitionPROPAGATION_REQUIRED); // 初始化事务,参数定义事务的传播类型; TransactionStatus status = transactionManagergetTransaction(def); //获得事务状态 try{ …………… transactionManagercommit(status); //提交事务; }catch(…){ transactionManagerrollback(status); //回滚事务; }
Spring也提供声明式事务管理。这是通过AOP实现的。大多数Spring用户选择声明式事务管理,这是最少影响应用代码的选择,因而这是和非侵入性的轻量级容器的观念是一致的。
1)通常通过TransactionProxyFactoryBean设置Spring事务代理。需要一个目标对象包装在事务代理中。这个目标对象一般是一个普通Javabean。当我们定义TransactionProxyFactoryBean时,必须提供一个相关的 PlatformTransactionManager的引用和事务属性。事务属性含有事务定义。例如:
<bean id="transactionService"class="orgspringframework transactioninterceptorTransactionProxyFactoryBean">
<property name="transactionManager">
<ref local="transactionManager"/>
property>
<property name="target">
<ref local="transactionServiceControl"/>
property>
<property name="transactionAttributes">
<props>
<prop key=”insert”>PROPAGATION_REQUIRED,-MyCheckedExceptionprop>
<prop key=”update”>PROPAGATION_REQUIREDprop>
<prop key=””>PROPAGATION_REQUIRED,readOnlyprop>
props>
property>
bean>
事务代理会实现目标对象的接口:这里是属性名是target的引用。id是transactionServiceControl。(使用CGLIB也可以实现具体类的代理。只要设置proxyTargetClass属性为true即可。如果目标对象没有实现任何接口,这将自动设置该属性为true。通常,我们希望面向接口编程。)使用proxyInterfaces属性来限定事务代理来代理指定接口也是可以。 也可以通过从orgspringframeworkaopframeworkProxyConfig继承或所有AOP代理工厂共享的属性来定制 TransactionProxyFactoryBean行为。
然后,说说属性名是transactionAttributes意义:
这里的transactionAttributes属性是定义在 orgspringframeworktransactioninterceptorNameMathTransactionAttributeSource 中的属性格式设置。这个包括通配符的方法名称映射是很直观的,如”insert”。注意insert的映射的值包括回滚规则。”- MyCheckException”指定如果方法抛出MyCheckException或它的子类,事务会自动回滚。可以用逗号分隔多个回滚规则。“-” 前缀强制回滚,“+”前缀指定提交(这允许即使抛出unchecked异常时也可以提交事务)。“PROPAGATION_REQUIRED”指定事务传播范围。
TransactionProxyFactoryBean允许你通过“preInterceptors”和 “postInterceptors”属性设置前或后的拦截 *** 作。可以设置任意数量的前和后通过,它们的类型可以是Advistor(切入点),MethodInterceptor或被当前Spring配置支持的通知类型。例如:ThrowAdvice,AfterReturningAdvice或BeforeAdvice。这些通知必须支持实例共享模式。如果你需要高级 AOP特性 *** 作事务,通过orgspringframeworkaopframeworkProxyFactoryBean,而不是 TransactionProxyFactory实用代理创建者。
2)另一种声明方式:BeanNameAutoProxyCreator
使用TransactionProxyFactoryBean当事务代理包装对象,你可以完全控制代理。如果需要用一致方式包装大量bean。使用一个 BeanFactoryPostProcessor的一个实现,BeanNameAutoProxyCreator,可以提供另外一种方法。(Spring中,一旦ApplicationContext读完它的初始化信息,它将初始化所有实现BeanPostProcessor接口的 bean,并且让它们后处理ApplicationContext中所有其他的bean。所以使用这种机制,正确配置的 BeanNameAutoProxyCreator可以用来后处理所有ApplicationContext中所有其他的bean),并且把它们用事务代理包装起来。真正生成的事务代理和使用TransactionProxyFactoryBean生成的基本一致。
最后,总结一下Spring的优点:
◆为不同的事务API提供一致的编程模型,如JTA、JDBC、Hibernate、iBATIS数据库层JDO
◆提供比大多数事务API更简单的、易于使用的编程式事务管理API
◆整合Spring数据访问抽象
◆支持Spring声明式事务管理


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

原文地址: http://outofmemory.cn/yw/12709397.html

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

发表评论

登录后才能评论

评论列表(0条)