spring事务
Spring事务注解@Transactional失效的八种场景分析
Spring 事务注解@Transactional 失效的⼋种场景分析⾸先说⼀下最近⾃⼰遇到的⼀个坑:123456789@Transactional service A (){try{insert();serviceB.update();}catch(){throw new RunTimeException();}}123456789serviceB(){@Transactional update(){try{mapperB.update();}catch(){throw new RunTimeException();}}}1234567mapperB (){try{//doSomething....return true;}catch(){return false;}}上⾯的例⼦中我的异常出现再//doSomething…的⼀个sql 执⾏异常了,serviceA 中的insert 却⼀直不能回滚,根本原因在于异常处只返回了false,并没有抛出运⾏异常,没有往上⼀层抛,serviceB 中有抛出运⾏异常,却只拿到了⼀个false,故不会回滚。
下⾯总结下常见的事务不会回滚的集中情况:这⾥以 MySQL 为例,其 MyISAM 引擎是不⽀持事务操作的,InnoDB 才是⽀持事务的引擎,⼀般要⽀持事务都会使⽤InnoDB 。
如下⾯例⼦所⽰:12345678// @Servicepublic class OrderServiceImpl implements OrderService {@Transactional public void updateOrder(Order order) { // update order }}如果此时把 @Service 注解注释掉,这个类就不会被加载成⼀个 Bean ,那这个类就不会被 Spring 管理了,事务⾃然就失效了。
@Transactional 只能⽤于 public 的⽅法上,否则事务不会失效,如果要⽤在⾮ public ⽅法上,可以开启 AspectJ 代理模式。
Spring事务配置的五种方式
Spring事务原理统观spring事务,围绕着两个核心PlatformTransactionManager和TransactionStatusspring提供了几个关于事务处理的类:TransactionDefinition //事务属性定义TranscationStatus //代表了当前的事务,可以提交,回滚。
PlatformTransactionManager这个是spring提供的用于管理事务的基础接口,其下有一个实现的抽象类AbstractPlatformTransactionManager,我们使用的事务管理类例如DataSourceTransactionManager等都是这个类的子类。
一般事务定义步骤:TransactionDefinition td = new TransactionDefinition();TransactionStatus ts = transactionManager.getTransaction(td);try{ //do sthmit(ts);}catch(Exception e){transactionManager.rollback(ts);}spring提供的事务管理可以分为两类:编程式的和声明式的。
编程式的,比较灵活,但是代码量大,存在重复的代码比较多;声明式的比编程式的更灵活。
编程式主要使用transactionTemplate。
省略了部分的提交,回滚,一系列的事务对象定义,需注入事务管理对象.void add(){transactionTemplate.execute( new TransactionCallback(){pulic Object doInTransaction(TransactionStatus ts){ //do sth}}}声明式:使用TransactionProxyFactoryBean:<bean id="userManager" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"><property name="transactionManager"><ref bean="transactionManager"/></property><property name="target"><ref local="userManagerTarget"/></property><property name="transactionAttributes"><props><prop key="insert*">PROPAGATION_REQUIRED</prop><prop key="update*">PROPAGATION_REQUIRED</prop><prop key="*">PROPAGATION_REQUIRED,readOnly</prop></props></property></bean>围绕Poxy的动态代理能够自动的提交和回滚事务org.springframework.transaction.interceptor.TransactionProxyFactoryBeanPROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。
关于Spring事务的原理,以及在事务内开启线程,连接池耗尽问题.
关于Spring事务的原理,以及在事务内开启线程,连接池耗尽问题.主要以结果为导向解释Spring 事务原理,连接池的消耗,以及事务内开启事务线程要注意的问题.Spring 事务原理这⾥不多说,⽹上⼀搜⼀⼤堆,也就是基于AOP配合ThreadLocal实现.这⾥强调⼀下Spring Aop 以及Spring 注解式注⼊在⾮Spring容器管理的类中是⽆效的.因为Spring Aop是在运⾏时实现字节码增强,字节码增强有多种实现⽅法,请⾃⾏了解,原⽣AspectJ是编译时织⼊,但是需要特定的编译器.语法并没有Spring Aop好理解.先看下Spring的事务传播⾏为类型事务传播⾏为类型说明PROPAGATION_REQUIRED如果当前没有事务,就新建⼀个事务,如果已经存在⼀个事务中,加⼊到这个事务中。
这是最常见的选择。
PROPAGATION_SUPPORTS⽀持当前事务,如果当前没有事务,就以⾮事务⽅式执⾏。
PROPAGATION_MANDATORY使⽤当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED以⾮事务⽅式执⾏操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER以⾮事务⽅式执⾏,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执⾏。
如果当前没有事务,则执⾏与 PROPAGATION_REQUIRED类似的操作。
打开⽇记debug模式,留意控制台输出以下测试为了可读性以及更容易理解全是基于Spring注解式事务,⽽没有配置声明式事务.测试1:可以看见只创建了⼀个SqlSession以及⼀个事务,在⽅法内所有操作都使⽤同⼀个连接,同⼀个事务@RequestMapping(value="/testThreadTx",method = RequestMethod.GET)@Transactional(propagation = Propagation.REQUIRED)public void testThreadTx(){//此⽅法没有事务(当前⽅法是 Propagation.REQUIRED)Quotation quotation = quotationService.findEntityById(new String("1"));//此⽅法没有事务(当前⽅法是 Propagation.REQUIRED)quotationService.updateEntity(quotation);}//查看控制台输出(红字关键部分,第三个查询是更新⽅法内部需要先查询⼀次再更新,可以⽆视)Creating a new SqlSessionRegistering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@230376b4]ansaction(line/:54) -JDBC Connection [1068277098(com.mysql.jdbc.JDBC4Connection@5d92bace)] will be managed by SpringotationMapper.findEntityById(line/:54) -==> Preparing: SELECT * FROM table WHERE id = 1otationMapper.findEntityById(line/:54) -==> Parameters:otationMapper.findEntityById(line/:54) -<== Total: 1Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@230376b4]Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@230376b4] from current transactionReleasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@230376b4]Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@230376b4] from current transactionotationMapper.updateEntity(line/:54) -==> Preparing: update ….. where id = 1otationMapper.updateEntity(line/:54) -==> Parameters:otationMapper.updateEntity(line/:54) -<== Updates: 1Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@230376b4]erOperationLogServiceImpl(line/:41) -请求所⽤时间:207erOperationLogServiceImpl(line/:42) -请求结束*******************************************************************************Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@230376b4]Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@230376b4]Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@230376b4]测试2:不使⽤事务可以看出在⾮事务操作数据库,会使⽤多个连接,⾮常不环保,这⾥给稍微多线程插⼊埋下⼀个陷阱@RequestMapping(value="/testThreadTx",method = RequestMethod.GET)// @Transactional(propagation = Propagation.REQUIRED)public void testThreadTx(){Quotation quotation = quotationService.findEntityById(new String("1"));quotationService.updateEntity(quotation);}//查看控制台输出(红字关键部分,第三个查询是更新⽅法内部需要先查询⼀次再更新,可以⽆视)Creating a new SqlSessionSqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7f7b94f] was not registered for synchronization because synchronizationansaction(line/:54) -JDBC Connection [352410768(com.mysql.jdbc.JDBC4Connection@c63fcb6)] will not be managed by SpringotationMapper.findEntityById(line/:54) -==> Preparing: SELECT * FROM table WHERE id = 1otationMapper.findEntityById(line/:54) -==> Parameters:otationMapper.findEntityById(line/:54) -<== Total: 1Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7f7b94f]Creating a new SqlSessionSqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7a41785a] was not registered for synchronization because synchronizationansaction(line/:54) -JDBC Connection [1615108970(com.mysql.jdbc.JDBC4Connection@38377d86)] will not be managed by SpringotationMapper.findEntityById(line/:54) -==> Preparing: SELECT * FROM table WHERE id = 1otationMapper.findEntityById(line/:54) -==> Parameters:otationMapper.findEntityById(line/:54) -<== Total: 1Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7a41785a]Creating a new SqlSessionSqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@181e5a22] was not registered for synchronization because synchronizationansaction(line/:54) -JDBC Connection [2096339748(com.mysql.jdbc.JDBC4Connection@5d4e9892)] will not be managed by SpringotationMapper.updateEntity(line/:54) -==> Preparing: update …. where id = 1otationMapper.updateEntity(line/:54) -==> Parameters:otationMapper.updateEntity(line/:54) -<== Updates: 1Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@181e5a22]erOperationLogServiceImpl(line/:41) -请求所⽤时间:614erOperationLogServiceImpl(line/:42) -请求结束*******************************************************************************测试3:@RequestMapping(value="/testThreadTx",method = RequestMethod.GET)@Transactional(propagation = Propagation.REQUIRED)public void testThreadTx(){final ExecutorService executorService = Executors.newFixedThreadPool(3);Quotation quotation = quotationService.findEntityById(new String("1"));quotationService.updateEntity(quotation);List<Future<Integer>> futures = new ArrayList<Future<Integer>>(3);for(int i=0;i<3;i++){Callable<Integer> task = new Callable<Integer>() {@Overridepublic Integer call() throws Exception {Quotation quotation = quotationService.findEntityById(new String("1"));quotationService.updateEntity(quotation);return null;}};futures.add(executorService.submit(task));}executorService.shutdown();}//查看控制台输出(红字关键部分,第三个查询是更新⽅法内部需要先查询⼀次再更新,可以⽆视)为了节篇幅,这⾥不贴出控制台数据⼤概就是输出了10个Creating a new SqlSession(⼤概有些同学使⽤了多线程,把线程池耗完了也没弄明⽩原因)外层⽅法启动⼀个,内部3个线程,每个线程3个.⼀共是使⽤了10个连接.为什么?这涉及到ThreadLocal以及线程私有栈的概念.如果Spring 事务使⽤InhertableThreadLocal就可以把连接传到⼦线程,但是为什么Spring不那么⼲呢?因为这样毫⽆意义,如果把同⼀个连接传到⼦线程,那就是SQL操作会串⾏执⾏,那何必还多线程呢?有关于ThreadLocal,InhertableThreadLocal配合线程池的⼀些陷阱请看我另⼀篇⽂章:测试4:既然使⽤同⼀个事务,不能实现并发操作,那么只能折中,在每⼀个线程开启⼀个事务,减少创建更多的连接,执⾏完毕以后可以返回操作成功失败结果,反馈给⽤户@RequestMapping(value="/testThreadTx",method = RequestMethod.GET)// @Transactional(propagation = Propagation.REQUIRED)public void testThreadTx(){ExecutorService executorService = Executors.newFixedThreadPool(3);List<Future<Integer>> futures = new ArrayList<Future<Integer>>(3);for(int i=0;i<3;i++){Callable<Integer> task = new Callable<Integer>() {@Overridepublic Integer call() throws Exception {quotationService.doSomeThing();return null;}};futures.add(executorService.submit(task));}executorService.shutdown();}//封装⼀下@Override@Transactional(propagation =Propagation.REQUIRED)public void doSomeThing(){Quotation quotation = this.findEntityById(new String("1"));this.updateEntity(quotation);}//查看控制台输出,只会创建3个连接,为节省篇幅,这⾥不贴出控制台所有数据Creating a new SqlSessionCreating a new SqlSessionCreating a new SqlSession最后⼩技巧PROPAGATION_NOT_SUPPORTED(仅仅为了让Spring能获取ThreadLocal的connection),如果不使⽤事务,但是同⼀个⽅法多个对数据库操作,那么使⽤这个传播⾏为可以减少消耗数据库连接@RequestMapping(value="/testThreadTx",method = RequestMethod.GET)@Transactional(propagation = Propagation.NOT_SUPPORTED)public void testThreadTx(){Quotation quotation = quotationService.findEntityById(new String("1"));quotation.setStatus(ClassDataManager.STATE_N);quotationService.updateEntity(quotation);}//这样只会创建⼀个SqlSession。
spring 事务 实现原理
spring 事务实现原理Spring事务的实现原理主要依赖于数据库或者其他存储系统的事务功能。
1. 数据库事务支持:Spring事务的实现依赖于数据库的事务功能。
通常情况下,数据库支持ACID(原子性、一致性、隔离性和持久性)事务特性,可以通过SQL的BEGIN、COMMIT 和ROLLBACK等命令来控制事务的开始、提交和回滚操作。
2. 事务管理器:Spring框架提供了事务管理器(TransactionManager)接口,用于管理事务的开始、提交和回滚等操作。
事务管理器可以根据配置信息选择不同的实现,如使用JDBC事务管理器(DataSourceTransactionManager)或者JTA事务管理器(JtaTransactionManager)等。
3. 编程式事务:Spring框架提供了编程式事务管理的支持,通过编写代码显式地控制事务的边界。
开发者可以在代码中使用事务管理器的API来手动管理事务,包括事务的开始、提交和回滚等操作。
这种方式灵活性较高,但也需要开发者手动处理事务的细节。
4. 声明式事务:Spring框架还提供了声明式事务管理的支持,通过使用注解或者XML配置来定义事务的属性和边界。
开发者可以使用@Transactional注解或者事务配置元素来标记方法或者类,指示该方法或者类在执行时需要开启事务。
Spring会在方法调用前后自动地管理事务的开始、提交和回滚等操作。
5. 事务传播行为:Spring事务还支持事务传播行为的定义,即一个方法调用另一个带有事务的方法时事务该如何传播。
例如,当前方法可能会暂停事务、加入到现有的事务中或者创建一个新的事务。
Spring提供了多种事务传播行为的选项,如REQUIRED、REQUIRES_NEW、NESTED等,可以根据需要进行配置。
总的来说,Spring事务的实现原理是通过与数据库或其他存储系统的事务功能结合,通过事务管理器来管理事务的开始、提交和回滚等操作。
spring事务原理
spring事务原理数据库系统内,事务指一系列连续的操作,这系列操作被看作一个整体,要么完全执行,要么完全不执行,不会出现执行部分的情况,这是数据库系统的基本特征之一。
在传统数据库系统中,事务的管理是由数据库系统自身提供的一种功能,然而随着数据库系统的复杂性的增加,数据库中的事务管理越来越复杂,对数据库操作有更多的要求,这样就催生了应用层事务管理技术,而当前最流行的事务管理技术是Spring事务。
Spring事务是一种基于AOP(面向切面编程)的分布式事务管理技术,它能够帮助系统更好的控制事务的处理过程,从而使系统内的数据更加有效率的共享,降低数据库事务的管理复杂度,提高数据库系统的可用性及性能。
Spring事务原理包括几个要素:事务模型、分布式事务处理框架、事务实现机制以及事务管理工具等。
一、事务模型Spring事务原理的核心是事务模型,即它把事务分为两种:提交成功事务和失败事务,在Spring提供的事务模型中,每个事务都只有两种结果,要么提交成功,要么提交失败,而不存在半提交的状态。
在Spring的事务模型中,事务的分类还要求事务的原子性,它要求一旦提交事务,事务就不会再次改变,或者改变的程度会很小。
原子性还表明事务的执行要么完全成功,要么完全失败,不会出现半成功半失败的情况。
二、分布式事务处理框架Spring提供了基于AOP技术的分布式事务处理框架,这种分布式事务处理框架能够有效地支持不同数据库之间的事务处理,它包括三个部分:事务管理器(TransactionManager)、事务拦截器(TransactionInterceptor)和事务事件监听器(TransactionListener)。
事务管理器是Spring对分布式事务的抽象,它可以处理不同类型的事务,它的实现通常会涉及到一些事务拦截器。
事务拦截器是Spring提供的一种安全机制,它能够在事务处理过程中,根据配置的规则,来拦截事务的执行,以便能够在事务处理过程中发现任何可能存在的问题,并对其进行调整,使得事务能够顺利的提交。
spring事务详解(基于注解和声明的两种实现方式)
spring事务详解(基于注解和声明的两种实现⽅式)Spring事务( Transaction )事务的概念事务是⼀些sql语句的集合,作为⼀个整体执⾏,⼀起成功或者⼀起失败。
使⽤事务的时机⼀个操作需要多天sql语句⼀起完成才能成功程序中事务在哪⾥说明加在业务类的⽅法上⾯(public⽅法上⾯),表⽰业务⽅法执⾏时,需要事务的⽀持。
不同的事务管理器不同的数据库访问技术,处理事务是不同的1. 使⽤jdbc访问数据库,事务处理public void updateAccount(){Connection con = .....;con.setAutoCommit(false);state.insert();state.update();mit();con.setAutoCommit(true);}2. MyBatis执⾏数据库,处理事务public void updateAccount(){SqlSession sqlSession = SqlSessionFactory.openSession(false);try{sqlSession.insert(...);sqlSession.update(...);mit();}catch(Exception e){sqlSession.rollback();}}spring统⼀管理事务,把不同的数据库访问技术的事务处理统⼀起来使⽤spring的事务管理器,管理不同数据库访问技术的事务处理。
开发⼈员只需要掌握spring的事务处理⼀个⽅案,就可以实现使⽤不同数据库访问技术的事务管理。
尽管事务⾯向的是spring,有spring管理事务,做事务提交和回滚。
spring事务管理器spring框架使⽤事务管理器对象,管理所有的事务。
事务管理器接⼝: PlatFormTransactionManager作⽤:定义了事务的操作,主要是commit() , rollback()事务管理器有很多的实现类:⼀种数据库访问计数有⼀个实现类。
Spring事务的传播行为 @Transactional
Spring事务的传播行为@TransactionalSpring事务的传播行为在service类前加上@Transactional,声明这个service所有方法需要事务管理。
每一个业务方法开始时都会打开一个事务。
Spring默认情况下会对运行期例外(RunTimeException)进行事务回滚。
这个例外是unchecked如果遇到checked意外就不回滚。
如何改变默认规则:1 让checked例外也回滚:在整个方法前加上@Transactional(rollbackFor=Exception.class)2 让unchecked例外不回滚:@Transactional(notRollbackFor=RunTimeException.class )3 不需要事务管理的(只查询的)方法:@Transactional(propagation=Propagation.NOT_SUPPOR TED)注意:如果异常被try{}catch{}了,事务就不回滚了,如果想让事务回滚必须再往外抛try{}catch{throwException}。
spring——@Transactional事务不管理jdbc,所以要自己把jdbc事务回滚。
下面给出了回滚JDBC事务的代码示例:Java代码public void processT(Stringorders) {ContextinitCtx = new InitialContext();javax.sql.DataSourceds = javax.sql.DataSource)initCtx.lookup(“java:comp/env/jdbc/OrdersDB”);java.sql.Connectionconn = ds.getConnection();try {conn.setAutoCommit( false ); //更改JDBC事务的默认提交方式orderNo= createOrder( orders );updateOrderStatus(orderNo,“orders created”);mit(); //提交JDBC事务} catch (Exception e ){try {conn.rollback(); //回滚sJDBC事务throw new EJBException(“事务回滚:“ + e.getMessage());} catch (SQLException sqle ){throw new EJBException(“出现SQL操作错误:“ + sqle.getMessage());}}}下面给出了JTA事务代码示例:Java代码public void processOrder(StringorderMessage) {UserTransactiontransaction = mySessionContext.getUserTransaction(); //获得JTA事务try {transaction.begin(); //开始JTA事务orderNo= sendOrder(orderMessage);updateOrderStatus(orderNo,“order sent”);mit(); //提交JTA事务} catch (Exceptione){try {transaction.rollback(); //回滚JTA事务} catch (SystemExceptionse){se.printStackTrace();}throw new EJBException(“事务回滚:“ + e.getMessage());}}在整个方法运行前就不会开启事务还可以加上:@Transactional(propagation=Propagation.NOT_SUPPOR TED,readOnly=true),这样就做成一个只读事务,可以提高效率。
事务之六:spring嵌套事务
事务之六:spring嵌套事务⼀、基本概念事务的隔离级别,事务传播⾏为见《》⼆、嵌套事务⽰例2.1、Propagation.REQUIRED+Propagation.REQUIRES_NEWpackage dxz.demo1;@Servicepublic class ServiceAImpl implements ServiceA {@Autowiredprivate ServiceB serviceB;@Autowiredprivate VcSettleMainMapper vcSettleMainMapper;@Override@Transactional(propagation = Propagation.REQUIRED, readOnly = false)public void methodA() {String id = IdGenerator.generatePayId("A");VcSettleMain vc = buildModel(id);vcSettleMainMapper.insertVcSettleMain(vc);System.out.println("ServiceAImpl VcSettleMain111:" + vc);serviceB.methodB();VcSettleMain vc2 = buildModel(id);vcSettleMainMapper.insertVcSettleMain(vc2);System.out.println("ServiceAImpl VcSettleMain22222:" + vc2);}private VcSettleMain buildModel(String id) {VcSettleMain vc = new VcSettleMain();vc.setBatchNo(id);vc.setCreateBy("dxz");vc.setCreateTime(LocalDateTime.now());vc.setTotalCount(11L);vc.setTotalMoney(BigDecimal.ZERO);vc.setState("5");return vc;}}ServiceBpackage dxz.demo1;@Servicepublic class ServiceBImpl implements ServiceB {@Autowiredprivate VcSettleMainMapper vcSettleMainMapper;@Override@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = false)public void methodB() {String id = IdGenerator.generatePayId("B");VcSettleMain vc = buildModel(id);vcSettleMainMapper.insertVcSettleMain(vc);System.out.println("---ServiceBImpl VcSettleMain:" + vc);}}controllerpackage dxz.demo1;@RestController@RequestMapping("/dxzdemo1")@Api(value = "Demo1", description="Demo1")public class Demo1 {@Autowiredprivate ServiceA serviceA;/*** 嵌套事务测试*/@PostMapping(value = "/test1")public String methodA() throws Exception {serviceA.methodA();return "ok";}}结果:看数据库表记录:这种情况下, 因为 ServiceB#methodB 的事务属性为 PROPAGATION_REQUIRES_NEW,ServiceB是⼀个独⽴的事务,与外层事务没有任何关系。
Spring事务的传播机制
Spring事务的传播机制Spring事务的传播机制是指在多个事务方法相互调用的情况下,事务的传播规则和行为。
Spring框架提供了七种事务传播行为,分别为REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER、NESTED。
1.REQUIRED(默认):如果当前存在事务,则加入该事务,如果不存在事务,则创建一个新事务。
该传播行为是最常用的一种,适用于大多数业务场景。
例如,A方法调用B方法,B方法中使用REQUIRED传播行为,则B方法会加入A方法的事务中。
2.SUPPORTS:如果当前存在事务,则加入该事务,如果不存在事务,则以非事务的方式执行。
该传播行为适用于不需要强制事务的情况。
例如,A方法调用B方法,B方法中使用SUPPORTS传播行为,则B方法会根据当前是否存在事务来决定是否加入。
3.MANDATORY:如果当前存在事务,则加入该事务,如果不存在事务,则抛出异常。
该传播行为适用于必须在事务中执行的情况。
例如,A方法调用B方法,B方法中使用MANDATORY传播行为,则B方法会检查当前是否存在事务,如果不存在则抛出异常。
4.REQUIRES_NEW:无论当前是否存在事务,都创建一个新事务,并在新事务中执行。
该传播行为适用于需要独立事务执行的情况。
例如,A方法调用B方法,B方法中使用REQUIRES_NEW传播行为,则B方法会创建一个新事务并在其中执行。
5.NOT_SUPPORTED:以非事务的方式执行方法,如果当前存在事务,则将事务挂起。
该传播行为适用于不需要事务的情况。
例如,A方法调用B方法,B方法中使用NOT_SUPPORTED传播行为,则B方法会以非事务的方式执行。
6.NEVER:以非事务的方式执行方法,如果当前存在事务,则抛出异常。
该传播行为适用于必须在非事务中执行的情况。
例如,A方法调用B方法,B方法中使用NEVER传播行为,则B方法会检查当前是否存在事务,如果存在则抛出异常。
Spring声明式事务@Transactional详解,事务隔离级别和传播行为
Spring声明式事务@Transactional详解,事务隔离级别和传播⾏为@Transactional注解⽀持9个属性的设置,这⾥只讲解其中使⽤较多的三个属性:readOnly、propagation、isolation。
其中propagation属性⽤来枚举事务的传播⾏为,isolation⽤来设置事务隔离级别,readOnly进⾏读写事务控制。
@Service@Transactional(readOnly = true)public class AppTradeRec2Service extends BaseService {@Autowiredprivate AppTradeRecDao appTradeRecDao;@Autowiredprivate ConsInfoDao consInfoDao;@Transactional(readOnly = false)public void payCharge(TradeRec tradeRec) {User usr = UserUtils.getUser();ConsInfo cons = consInfoDao.getByUser(usr.getId());//修改交易记录tradeRec.setPayBefore(cons.getAccountAmt());tradeRec.setPayAfter(cons.getAccountAmt() - tradeRec.getRcvAmt());tradeRec.setIsPay("99");appTradeRecDao.save(tradeRec);//修改账户余额cons.setAccountAmt(cons.getAccountAmt() - tradeRec.getRcvAmt());consInfoDao.save(cons);}}⼀、readOnly读写事务控制readOnly=true表明所注解的⽅法或类只是读取数据。
Spring事务之七(事务自动提交)
Spring事务之七(事务自动提交)一、MySQL数据库事务自动提交对于mysql数据库,默认情况下,数据库处于自动提交模式。
每一条语句处于一个单独的事务中,在这条语句执行完毕时,如果执行成功则隐式的提交事务,如果执行失败则隐式的回滚事务。
对于正常的事务管理,是一组相关的操作处于一个事务之中,因此必须关闭数据库的自动提交模式,下面是查看方式:查看是否自动提交命令(ON表示开启自动提交,值为1,OFF表示关闭自动提交,值为0):关闭自动提交后,则用户将一直处于某个事务中,直到执行一条commit提交或rollback语句才会结束当前事务重新开始一个新的事务。
二、Spring连接如果结合Spring来获取数据库连接,就不用担心,spring会将底层连接的自动提交特性设置为false,先看下Spring事务配置:1.<!-- 配置事务 -->2.<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransac tionManager">3.<property name="dataSource" ref="dataSource" />4.</bean>5.<tx:annotation-driven transaction-manager="transactionManager"/>上面配置中,重要的是类:org.springframework.jdbc.datasource.DataSourceTransactionMa nager,看其对自动提交的处理:1.protected void doBegin(Object transaction, TransactionDefinition definition) {2.3.//......4.5.if(con.getAutoCommit()) {6.txObject.setMustRestoreAutoCommit(true);7.if(this.logger.isDebugEnabled()) {8.this.logger.debug("Switching JDBC Connection [" + con + "] to manual commit");9.}10.con.setAutoCommit(false);11.}12.13.//......14.15.}三、建议:当一个连接关闭时,如果有未提交的事务则回滚任何未提交的事务(C3P0默认的策略,C3P0的autoCommitOnClose属性默认是false)。
Spring中解决事务以及异步注解失效
Spring中解决事务以及异步注解失效一、重现@Transaction失效的场景有如下业务场景,新增订单后,自动发送短信,下面的代码在同一个类中:@Transactionpublic void addOrder(OrderInfo orderInfo){orderMapper.insert(orderInfo);try{sendMesg(orderInfo );}cach(Exception e){e.printStrace();}}@Transactionpublic void sendMesg( OrderInfo orderInfo ){mesgMapper.insert(orderInfo );throws new RuntimeException("发送短信出现异常!");}上面的伪代码模拟新增订单后,自动发送短信的业务场景。
两个操作被标识为事务,为了不影响发送短信出现异常影响订单的插入,在调用发送短信的方法时,通过 try....cach...捕获其异常并处理,不影响订单表的插入。
因为sendMesg标识为事务,其抛出异常后,事务按正常逻辑来说,事务会进行回滚,即短信表中不会插入记录。
然而事与愿违,出现的结果是短信表也插入了记录。
二、重现异步注解失效的场景1、异步注解@Async介绍基于@Async标注的方法,称之为异步方法,这些方法在执行的时候,spring将会为其开辟独立的线程执行,调用者无需等待它的完成,即可继续其他的操作。
2、如何使用@Async增加 aspectj 相关的依赖;修改 spring配置文件,在配置文件中增加如下配置:<task:annotation-driven executor = "annotationExecutor" /><task:executor id="annotationExecutor" pool-size="20" />在方法上加上@Async注解。
spring事务(Transaction)报markedasrollback-only异常。。。
spring事务(Transaction)报markedasrollback-only异常。
很多朋友在使⽤spring+hibernate或mybatis等框架时经常遇到报Transaction rolled back because it has been marked as rollback-only的异常,这个异常是怎么造成的呢,下⾯将给⼤家进⾏详细的分析。
这是专门写的⼀个造成该异常的代码:@Transactionalpublic void add(OperateLog entity)throws Exception {// TODO Auto-generated method stuboperateLogDao.add(entity);}@Transactionalpublic void save(Member member) throws Exception {memberDao.add(member);}@Transactionalpublic void add(Member member) throws Exception {try {this.save(member);/** ⽇志的title长度为10 我把值设置为add111111111111111111是为了造成异常*/OperateLog entity = new OperateLog("add111111111111111111", "1111");operateLogService.add(entity);} catch (Exception e) {e.printStackTrace();// throw e;}}执⾏以上代码就会报改异常,当我把//throw e;的注释//去掉,当我执⾏后就不会有改异常,只会报标题title太长的异常。
如下⾯提⽰:Caused by: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long for column '_title' at row 1at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4072)at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4006)at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2468)at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2629)at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2719)at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2155)at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2450)at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2371)at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2355)at mons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)at mons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)at org.hibernate.id.IdentityGenerator$GetGeneratedKeysDelegate.executeAndExtract(IdentityGenerator.java:94)at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:57)通过以上的问题我们可以发现。
spring事务失效的12种场景
spring事务失效的12种场景⼀事务不⽣效1.访问权限问题众所周知,java的访问权限主要有四种:private、default、protected、public,它们的权限从左到右,依次变⼤。
但如果我们在开发过程中,把有某些事务⽅法,定义了错误的访问权限,就会导致事务功能出问题,例如:@Servicepublic class UserService {@Transactionalprivate void add(UserModel userModel) {saveData(userModel);updateData(userModel);}}我们可以看到add⽅法的访问权限被定义成了private,这样会导致事务失效,spring要求被代理⽅法必须是public的。
说⽩了,在AbstractFallbackTransactionAttributeSource类的computeTransactionAttribute⽅法中有个判断,如果⽬标⽅法不是public,则TransactionAttribute返回null,即不⽀持事务。
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {// Don't allow no-public methods as required.if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;}// The method may be on an interface, but we need attributes from the target class.// If the target class is null, the method will be unchanged.Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);// First try is the method in the target class.TransactionAttribute txAttr = findTransactionAttribute(specificMethod);if (txAttr != null) {return txAttr;}// Second try is the transaction attribute on the target class.txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {return txAttr;}if (specificMethod != method) {// Fallback is to look at the original method.txAttr = findTransactionAttribute(method);if (txAttr != null) {return txAttr;}// Last fallback is the class of the original method.txAttr = findTransactionAttribute(method.getDeclaringClass());if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {return txAttr;}}return null;}也就是说,如果我们⾃定义的事务⽅法(即⽬标⽅法),它的访问权限不是public,⽽是private、default或protected的话,spring则不会提供事务功能。
spring事务注解@Transactional的实现原理(转)
spring事务注解@Transactional的实现原理(转)出处:Transactional是spring中定义的事务注解,在⽅法或类上加该注解开启事务。
主要是通过反射获取bean的注解信息,利⽤AOP对编程式事务进⾏封装实现。
AOP对事务的封装可以看。
我们先写个demo,感受它的加载过程。
spring事务注解:1. ⾃定义⼀个注解/*** @Target 作⽤域(作⽤在⽅法上,类上,或是属性上)* @Retention 运⾏周期* @interface 定义注解*/@Target(value = ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface MyAnnotation {//⾃定义注解的属性int id() default 0;String name() default "默认名称";String[]arrays();String title() default "默认标题";}2. 测试import ng.reflect.Method;public class User {@MyAnnotation(name="吴磊",arrays = {"2","3"})public void aMethod () {}public void bMethod () {}public static void main(String[] args) throws ClassNotFoundException {//1. 反射获取到类信息Class<?> forName = Class.forName("er");//2. 获取到当前类(不包含继承)所有的⽅法Method[] declaredMethods = forName.getDeclaredMethods();//3. 遍历每个⽅法的信息for (Method method : declaredMethods) {System.out.println("⽅法名称:" + method.getName());//4. 获取⽅法上⾯的注解MyAnnotation annotation = method.getDeclaredAnnotation(MyAnnotation.class);if(annotation == null) {System.out.println("该⽅法上没有加注解....");}else {System.out.println("Id:" + annotation.id());System.out.println("Name:" + ());System.out.println("Arrays:" + annotation.arrays());System.out.println("Title:" + annotation.title());}System.out.println("--------------------------");}}}=============================【控制台输出】⽅法名称:main该⽅法上没有加注解....--------------------------⽅法名称:aMethodId:0Name:吴磊Arrays:[ng.String;@74a14482Title:默认标题--------------------------⽅法名称:bMethod该⽅法上没有加注解....--------------------------总结:通过上⾯这么⼀个⼩demo我们就能发现,反射获取到每⼀个⽅法的注解信息然后进⾏判断,如果这是@Transactional注解,spring就会开启事务。
Spring事务管理及与mybatis整合的事务管理
数据访问事务处理in Spring+Mybatis3.0事务---保证了用户的每一次操作都是可靠的,即使出现了异常的访问,也不至于破坏后台数据的完整性;Java事务简介事务必须符合ISO/IEC所定制的ACID原则1)A(atomicity):原子性在事务执行的过程中,任何的失败就将导致事务的任何修改失效,2)C(consistency):一致性事务回滚时,事务所执行的内容必须恢复到初始状态,即事务执行前的状态3)I(isolation):隔离性事务执行过程中对数据的修改,在提交之前的数据对其他事务不可见4)D(durability):持久性已经提交的数据在事务执行失败时,数据的状态都是正确的.事务分类:全局事务(分布式事务):由应用服务器来管理(如JTA),同时可以用于多个事务性的资源;本地事务本地事务和资源相关,主要通过JDBC来实现在实际应用中,存在一种容器管理事务,容器事务主要是由javaEE应用服务器提供,容器事务大多给予JTA完成,事实上这是在容器中覆盖了JDBC和JTA事务.事务特性分析(use spring)TransactionDefinition 接口来定义事务属性。
Code:public interface TransactionDefinition{int getIsolationLevel();int getPropagationBehavior();int getTimeout();boolean isReadOnly();}事务机制a)事务隔离级别隔离级别是指若干个并发的事务之间的隔离程度。
TransactionDefinition 接口中定义了五个表示隔离级别的常量:TransactionDefinition.ISOLATION_DEFAULT(默认值):表示使用底层数据库的默认隔离级别。
对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
Spring七种事务传播行为与五种事务隔离级别
Spring七种事务传播⾏为与五种事务隔离级别⼀、事务的传播⾏为:通过Propagation定义:<!-- 配置事务通知 --><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><!-- 以⽅法为单位,指定⽅法应⽤什么事务属性:isolation:隔离级别 propagation:传播⾏为 read-only:是否只读--><tx:method name="transfer" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/><!--上⾯的⽅式只适⽤单个⽅法,当我们业务有很多个⽅法都要操作事务时,则要配置很多个,可以使⽤下⾯的通配符配置⽅式--><tx:method name="save*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/>省略update.. detele.....</tx:attributes></tx:advice>readOnly:事务属性中的readOnly标志表⽰对应的事务应该被最优化为只读事务。
这是⼀个最优化提⽰。
在⼀些情况下,⼀些事务策略能够起到显著的最优化效果,例如在使⽤Object/Relational映射⼯具(如:或TopLink)时避免dirty checking(试图“刷新”)。
spring事务注解
spring事务注解@Transactional只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能.Spring使用声明式事务处理,默认情况下,如果被注解的数据库操作方法中发生了unchecked异常,所有的数据库操作将rollback;如果发生的异常是checked 异常,默认情况下数据库操作还是会提交的。
这种默认的行为是可以改变的。
使用@Transactional注解的noRollbackFor和rollbackFor属性如:@Transactional(rollbackFor=Exception.class)可以使checked异常发生时,数据库操作也rollback、@Transactional(noRollbackFor=RuntimeException.class)可以使unchecked异常发生时也提交数据库操作。
也可以使用noRollbackForClassName、rollbackForClassName属性来指定一个异常类名的String数组来改变默认的行为。
读取数据库中的数据时是不需要事务管理的,这种情况下可以使用事务的传播行为来告诉Spring不需要开启事务,如:@Transactional(propagation = Propagation.NOT_SUPPORTED)。
事务的传播行为有:1. REQUIRED:表示业务方法需要在一个事务中处理,如果业务方法执行时已经在一个事务中,则加入该事务,否则重新开启一个事务。
这也是默认的事务传播行为;2. NOT_SUPPORTED:声明业务方法不需要事务,如果业务方法执行时已经在一个事务中,则事务被挂起,等方法执行完毕后,事务恢复进行;3. REQUIRES_NEW:表明业务方法需要在一个单独的事务中进行,如果业务方法进行时已经在一个事务中,则这个事务被挂起,并重新开启一个事务来执行这个业务方法,业务方法执行完毕后,原来的事务恢复进行;4. MANDATORY:该属性指定业务方法只能在一个已经存在的事务中进行,业务方法不能发起自己的事务;如果业务方法没有在一个既有的事务中进行,容器将抛出异常;5. SUPPORTS:该属性指定,如果业务方法在一个既有的事务中进行,则加入该事务;否则,业务方法将在一个没有事务的环境下进行;6. NEVER:指定业务方法不可以在事务中进行,如果业务方法执行时已经在一个事务中,容器将抛出异常;7. NESTED:该属性指定,如果业务方法在一个既有的事务中执行,则该业务方法将在一个嵌套的事务中进行;否则,按照REQUEIRED来对待。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1.6 Spring事务:
1. 编程式事务管理——编程式的事务管理可以清楚滴控制事务的边界,也就是让您自行实现事务开始时间、撤消操作的时机、结束时间等,可以实现细粒度的事务控制。
2. 声明式事务管理——然而多数的情况下,事务并不需要细粒度的控制,而是采用声明式的事务管理,好处是Spring事务管理的相关API可以不用介入程序之中,从对象的角度来看,它并不知道自己正被纳入事务管理之中,在不需要事务管理的时候,只要在设置文件上修改一下设置,即可移去事务管理服务。
Spring声明式的事务管理依赖它的AOP框架来完成。
使用声明式事务管理的好处是,事务管理不能侵入您所开发的组件,具体来说,DAO对象不会意识到正在事务管理之中,事实上也应该如此,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策略的话,也只需要在定义文件中重新配置。
事务的属性介绍:
Spring使用AOP来完成声明式的事务管理,因而声明式事务是以方法为边界的,Spring的事务属性自然就在于描述事务应用至方法上的策略,在Spring 中事务属性分作以下的几个参数:传播行为(Propagation behavior)、隔离层级(Isolation Level)、只读提示(Read-only hints)、事务超时期间(The transaction timeout period)
传播行为(Propagation behavior):传播行为定义了事务应用于方法的边界(Boundaries),它告知何时该开始一个新的事务,或何时事务被暂停,或方法是否要在事务中进行。
如,若传播行为设置为PROPAGATION_REQUIRED,则事务的边界在开始第一个事务的方法呼叫及结束时,如果先前没有事务被开始,则事务边界即为目前方法的执行前后。
又如果传播行为被声明为PROPAGATION_REQUIRES_NEW,则事务的边界即为该方法执行的前后。
隔离层级(Isolation Level):在一个应用程序中,可能有多个事务同时在进行,这些事务应当彼此之间另一个事务的存在,好比现在整个应用程序就只有一个事务存在,由于事务彼此之间独立,若读取的是同一个数据的话,就容易发生问题。
如:
脏读:(Dirty Read)脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另一个事务也访问了这个数据,然后使用了这个数据,由于这个数据是还没有提交的数据,(有可能回滚)那么另外这个事务读到的数据就是脏数据,依据脏数据所做的操作可能是不正确的。
不可重复读:(Non-repeatable read)指在一个事物中,多次读取同一个数据。
当这个事务还没结束时,另外一个事务也访问该同一数据。
那么,在第一个事务中
的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的数据可能是不一样的。
这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
幻读:(Phantom read):如当第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。
同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。
那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就像发生了幻觉一样。
为了避免以上问题的方法之一,需要在某个事务进行过程中锁定正在更新或查询的数据字段,直到目前的事务完成,然而完全锁定字段时,若另一个事务来查询同一份数据就必须等待,直到前一个事务完成并解除锁定为止,因而会造成应用程序在查询或更新数据时效率上的问题(hibernate悲观锁机制,oracle 中的for update),而事实上根据需求的不同,并不用在事务进行时完全地锁定数据,隔离层级可以让您根据实际的需求,对数据的锁定进行设置。
如:ISOLATION_DEFAULT——使用底层数据库预设的隔离层级
ISOLATION_READ_COMMITTED——允许事务读取其他并行的事务已经送出的数据字段,可以防止Dirty Read问题。
只读提示(Read-only hints):只读
事务超时期间(The transaction timeout period):
对于<tx:method>中的属性设置,若不设,则”propagation”默认为”REQUIRE”,”isolation”默认为“DEFAULT”、“timeout”默认属性为“-1”,“read-only”默认为“false”。
基于XML Schmea方式配置声明式事务:
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut expression="execution(* com.ying.sys.*.service.impl.*Impl.*(..))"
id="myPointcut" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut" /> </aop:config>。