当前位置: 首页 > news >正文

云南省建设厅网站地址白银网站建设公司

云南省建设厅网站地址,白银网站建设公司,绿叶网站怎么做,wordpress首页read more⚠️ 再提醒一次#xff1a;Spring 本身并不实现事务#xff0c;Spring事务 的本质还是底层数据库对事务的支持。你的程序是否支持事务首先取决于数据库 #xff0c;比如使用 MySQL 的话#xff0c;如果你选择的是 innodb 引擎#xff0c;那么恭喜你#xff0c;是可以支持…⚠️ 再提醒一次Spring 本身并不实现事务Spring事务 的本质还是底层数据库对事务的支持。你的程序是否支持事务首先取决于数据库 比如使用 MySQL 的话如果你选择的是 innodb 引擎那么恭喜你是可以支持事务的。但是如果你的 MySQL 数据库使用的是 myisam 引擎的话那不好意思Spring从根上就是不支持事务的。 这里再多提一下一个非常重要的知识点MySQL 怎么保证原子性的 我们知道如果想要保证事务的原子性就需要在异常发生时对已经执行的操作进行回滚在 MySQL 中恢复机制是通过 回滚日志undo log 实现的所有事务进行的修改都会先记录到这个回滚日志中然后再执行相关的操作。如果执行过程中遇到异常的话我们直接利用 回滚日志 中的信息将数据回滚到修改之前的样子即可并且回滚日志会先于数据持久化到磁盘上。这样就保证了即使遇到数据库突然宕机等情况当用户再次启动数据库的时候数据库还能够通过查询回滚日志来回滚之前未完成的事务。 〇、Spring 事务简介 Spring 事务 提供一套抽象的事务管理并且结合 Spring IoC 和 Spring AOP简化了应用程序使用数据库事务通过声明式事务可以做到对应用程序无侵入的实现事务功能。例如 使用JDBC 操作数据库想要使用事务的步骤为 获取连接 Connection con DriverManager.getConnection()开启事务con.setAutoCommit(true/false);执行CRUD提交事务/回滚事务 con.commit() / con.rollback();关闭连接 conn.close(); 采用Spring 事务后只需要 关注第3步的实现其他的步骤 都是Spring 完成。 Spring事务的本质 其实就是 AOP 和 数据库事务Spring 将数据库的事务操作提取为切面通过AOP的方式增强事务方法。 一、Spring 支持两种方式的事务管理 Spring提供了编程式事务和声明式事务两种实现方式。 1.1 编程式事务管理 Spring编程式事务有两种实现方法1、TransactionTemplate   2、PlatformTransactionManager  对于编程式事务管理Spring推荐使用TransactionTemplate。 通过 TransactionTemplate或者PlatformTransactionManager   手动管理事务实际应用中很少使用现在大多都是用声明式事务通过Spring自动帮我们管理事务但是编程式事务对于你理解 Spring 事务管理原理有帮助。 1.1.1 TransactionTemplate 采用TransactionTemplate和采用其他Spring模板如JdbcTempalte和HibernateTemplate来实现事务是一样的都属于编程式事务。它使用回调方法把应用程序从处理取得和释放资源中解脱出来。如同其他模板TransactionTemplate是线程安全的。实例代码如下 1、无返回值 // 可以自己手动新建也可以将TransactionTemplate交给Spring容器管理直接通过Autowired自动注入得到transactionTemplate对象 TransactionTemplate transactionTemplate new TransactionTemplate(); // 如果无返回值就new TransactionCallbackWithoutResult() transactionTemplate.execute(new TransactionCallbackWithoutResult() { Override protected void doInTransactionWithoutResult(TransactionStatus status) { try { // ....  业务代码 jdbcTemplate.update(update t set v? where k?, n, k2); //不需要手动提交 }catch (Exception e){ e.printStackTrace(); //但是需要手动回滚 status.setRollbackOnly(); } } }); 2、有返回值 // 新建一个TransactionTemplate TransactionTemplate tt new TransactionTemplate(); // 如果有返回值就new TransactionCallback() Object result tt.execute( new TransactionCallback(){ public Object doTransaction(TransactionStatus status){ // 业务逻辑... updateOperation(); // 返回值 return resultOfUpdateOperation(); } } ); // 执行execute方法进行事务管理 注 使用TransactionCallback()可以返回一个值。如果使用TransactionCallbackWithoutResult则没有返回值。 使用TransactionTemplate来实现编程式事务需要自己创建事务但是不需要自己手动提交它如果正常执行完会自动提交事务。不过需要自己手动设置回滚如果不设置回滚事务出现问题是不会回滚的。 1.1.2 PlatformTransactionManager   Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager通过这个接口Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器但是具体的实现就是各个平台自己的事情了。所以Spring事务管理的一个优点就是为不同的事务API提供一致的编程模型如JTA、JDBC、Hibernate、JPA它们自己去按照接口模板实现自己的事务管理器。示例代码如下 1、可以自己手动创建TransactionManager对象 // 定义一个某个框架平台的TransactionManager如JDBC、Hibernate DataSourceTransactionManager dataSourceTransactionManager new DataSourceTransactionManager(); // 设置数据源 dataSourceTransactionManager.setDataSource(this.getJdbcTemplate().getDataSource()); // 定义事务属性 DefaultTransactionDefinition transDef new DefaultTransactionDefinition(); // 设置传播行为属性 transDef.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED); // 获得事务状态开始事务 TransactionStatus status dataSourceTransactionManager.getTransaction(transDef); try { // 业务逻辑数据库操作 doSomething(); // 提交事务 dataSourceTransactionManager.commit(status); } catch (Exception e) { // 回滚回滚 dataSourceTransactionManager.rollback(status); } 2、也可以将TransactionManager对象交给Spring去管理通过Autowired自动注入获得事务管理器对象 Autowired private PlatformTransactionManager transactionManager; public void testTransaction() { TransactionStatus status transactionManager.getTransaction(new DefaultTransactionDefinition()); try { // ....  业务代码 transactionManager.commit(status); } catch (Exception e) { transactionManager.rollback(status); } } 使用TransactionManager来实现编程式事务与TransactionTemplate有些许不同它依旧需要自己创建事务需要自己手动设置回滚。并且它还需要自己需手动提交事务如果不手动提交事务是不会自动提交的。 1.2 声明式事务管理 声明式事务管理建立在AOP之上其本质是对方法前后进行拦截然后在目标方法开始之前创建或者加入一个事务执行完目标方法之后根据执行的情况提交或者回滚。 推荐使用声明式事务因为代码侵入性最小底层实际是通过 AOP 实现基于Transactional 的全注解方式使用最多。声明式事务并不需要自己创建事务提交事务回滚等操作Spring会自动替你完成但是声明式事务的粒度就是方法层面的而编程式事务的粒度可以更小。 既然是基于AOP实现的那么使用声明式事务必然需要对事务进行配置根据不同的配置方法可以总结出三种实现方法1、XML实现    2、注解实现     3、混合实现XML和注解同时使用 其实我们能发现这三种实现方法和AOP的配置是一样的Spring事务的底层原理就是通过AOP代理增强代码实现的在原有代码上增加了事务管理的代码。 1.2.1 xml配置声明式事务 XML配置文件 ?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:aophttp://www.springframework.org/schema/aop xmlns:txhttp://www.springframework.org/schema/tx xmlns:contexthttp://www.springframework.org/schema/context xsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd Index of /schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd Index of /schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd Index of /schema/context http://www.springframework.org/schema/context/spring-context.xsd !-- 纯xml 我们的业务类也配成xml -- !--将业务类交给spring管理-- bean idxmlUserService classcom.test.transaction.service.XMLUserService property namejdbcTemplate refjdbcTemplate/property /bean !--配置jdbcTemplate,来设置数据源并将jdbcTemplate装配进Spring容器-- bean idjdbcTemplate classorg.springframework.jdbc.core.JdbcTemplate property namedataSource refdatasource/property /bean !--配置数据源,将数据源绑定到jdbcTemplate-- bean iddatasource classorg.springframework.jdbc.datasource.DriverManagerDataSource property nameusername valueroot/property property namepassword value123456/property property nameurl valuejdbc:mysql://localhost:3306/shadow?useSSLfalseamp;allowPublicKeyRetrievaltrueamp;serverTimezoneUTC /property property namedriverClassName valuecom.mysql.cj.jdbc.Driver /property /bean !--配置事务管理器,将事务管理器绑定上数据源-- bean idtransactionManager classorg.springframework.jdbc.datasource.DataSourceTransactionManager property namedataSource refdatasource/property /bean !--事务通知(设置在什么时候干什么事),将事务管理器作用到指定的方法中-- tx:advice transaction-managertransactionManager idtxAdvice tx:attributes tx:method nameupdate/ /tx:attributes /tx:advice !-- aop的配置,设置切入点,表示对包路径com.test.transaction.service下的所有名为update的方法进行事务管理-- aop:config aop:pointcut idp expressionexecution(*com.test.transaction.service..*.*(..))/ aop:advisor advice-reftxAdvice pointcut-refp/aop:advisor /aop:config /beans Java代码 public class XMLUserService { JdbcTemplate jdbcTemplate; // 通过xml配置注入jdbcTemplate public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate jdbcTemplate; } // Spring会自动对update方法进行事务管理 public void update() { jdbcTemplate.update(update t set v? where k?,n,k1); // 检测到抛出异常就会自动回滚事务 throw new NullPointerException(); } } 1.2.2 注解配置声明式事务 JavaConfig配置类 // 将App这个类作为JavaConfig配置类.这个注解如果不加会有问题 Configuration // 开启注解支撑 EnableTransactionManagement // 扫描这个包下面的注解 ComponentScan(com.test.transaction) public class App { // 将jdbcTemplate对象放到容器当中,用于连接数据库,执行sql语句 Bean public JdbcTemplate jdbcTemplate() { return new JdbcTemplate(dataSource()); } // 将数据源对象配置到Spring容器中 Bean public DataSource dataSource() { DriverManagerDataSource dataSource new DriverManagerDataSource(); dataSource.setDriverClassName(com.mysql.cj.jdbc.Driver); dataSource.setPassword(123456); dataSource.setUrl(jdbc:mysql://localhost:3306/shadow? useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneUTC); dataSource.setUsername(root); return dataSource; } /** * 声明命事务管理器,将事务管理器装配到Spring容器中 * return */ Bean public PlatformTransactionManager transactionManager() { // 创建事务管理器对象 DataSourceTransactionManager dataSourceTransactionManager new DataSourceTransactionManager(); // 设置数据源 dataSourceTransactionManager.setDataSource(dataSource()); return dataSourceTransactionManager; } } 使用 Transactional注解进行事务管理的示例代码如下 Transactional(propagation Propagation.REQUIRED) public void aMethod { //do something B b new B(); C c new C(); b.bMethod(); c.cMethod(); } 注解Transactional的属性 具体位置在org.springframework.transaction.annotation.Transactional 1.2.3 混合方式 这个就没有标准看自己的喜好哪些写到xml哪些写成Java代码。 以下配置代码参考自Spring事务配置的五种方式。根据代理机制的不同总结了五种Spring事务的配置方式。他们都是可以实现XML和注解混用的。 1、每个Bean都有一个代理 ?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:contexthttp://www.springframework.org/schema/context xmlns:aophttp://www.springframework.org/schema/aop xsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd Index of /schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd Index of /schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd !-- 配置注解扫描 -- bean idsessionFactory classorg.springframework.orm.hibernate3.LocalSessionFactoryBean property nameconfigLocation valueclasspath:hibernate.cfg.xml / property nameconfigurationClass valueorg.hibernate.cfg.AnnotationConfiguration / /bean !-- 定义事务管理器声明式的事务 -- bean idtransactionManager classorg.springframework.orm.hibernate3.HibernateTransactionManager property namesessionFactory refsessionFactory / /bean !-- 配置DAO -- bean iduserDaoTarget classxxx.dao.UserDaoImpl property namesessionFactory refsessionFactory / /bean !-- 配置事务代理 -- bean iduserDao classorg.springframework.transaction.interceptor.TransactionProxyFactoryBean !-- 配置事务管理器 -- !-- 将事务管理添加到DAO层代码上 -- property nametransactionManager reftransactionManager / property nametarget refuserDaoTarget / property nameproxyInterfaces valuexxx.dao.GeneratorDao / !-- 配置事务属性 -- property nametransactionAttributes props prop key*PROPAGATION_REQUIRED/prop /props /property /bean /beans 2、所有Bean共享一个代理基类 ?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:contexthttp://www.springframework.org/schema/context xmlns:aophttp://www.springframework.org/schema/aop xsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd Index of /schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd Index of /schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd !-- 配置数据源 -- bean idsessionFactory classorg.springframework.orm.hibernate3.LocalSessionFactoryBean property nameconfigLocation valueclasspath:hibernate.cfg.xml / property nameconfigurationClass valueorg.hibernate.cfg.AnnotationConfiguration / /bean !-- 定义事务管理器声明式的事务 -- bean idtransactionManager classorg.springframework.orm.hibernate3.HibernateTransactionManager property namesessionFactory refsessionFactory / /bean !-- 配置事务基类 -- bean idtransactionBase classorg.springframework.transaction.interceptor.TransactionProxyFactoryBean lazy-inittrue abstracttrue !-- 配置事务管理器 -- property nametransactionManager reftransactionManager / !-- 配置事务属性 -- property nametransactionAttributes props prop key*PROPAGATION_REQUIRED/prop /props /property /bean !-- 配置DAO -- bean iduserDaoTarget classxxx.dao.UserDaoImpl property namesessionFactory refsessionFactory / /bean bean iduserDao parenttransactionBase property nametarget refuserDaoTarget / /bean /beans 3、使用拦截器 ?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:contexthttp://www.springframework.org/schema/context xmlns:aophttp://www.springframework.org/schema/aop xsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd Index of /schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd Index of /schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd !-- 配置数据源 -- bean idsessionFactory classorg.springframework.orm.hibernate3.LocalSessionFactoryBean property nameconfigLocation valueclasspath:hibernate.cfg.xml / property nameconfigurationClass valueorg.hibernate.cfg.AnnotationConfiguration / /bean !-- 定义事务管理器声明式的事务 -- bean idtransactionManager classorg.springframework.orm.hibernate3.HibernateTransactionManager property namesessionFactory refsessionFactory / /bean !-- 配置事务拦截器 -- bean idtransactionInterceptor classorg.springframework.transaction.interceptor.TransactionInterceptor property nametransactionManager reftransactionManager / !-- 配置事务属性 -- property nametransactionAttributes props prop key*PROPAGATION_REQUIRED/prop /props /property /bean bean classorg.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator property namebeanNames list value*Dao/value /list /property property nameinterceptorNames list valuetransactionInterceptor/value /list /property /bean !-- 配置DAO -- bean iduserDao classxxx.dao.UserDaoImpl property namesessionFactory refsessionFactory / /bean /beans 4、使用tx标签配置的拦截器 ?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:contexthttp://www.springframework.org/schema/context xmlns:aophttp://www.springframework.org/schema/aop xsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd Index of /schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd Index of /schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd !-- 开启注解支持 -- context:annotation-config / !-- 扫描注解 现在可以不写上面那句,直接写下面的扫描注解标签就可以开启注解支持了-- context:component-scan base-packagexxx / !-- 定义数据源 -- bean idsessionFactory classorg.springframework.orm.hibernate3.LocalSessionFactoryBean property nameconfigLocation valueclasspath:hibernate.cfg.xml / property nameconfigurationClass valueorg.hibernate.cfg.AnnotationConfiguration / /bean !-- 定义事务管理器声明式的事务 -- bean idtransactionManager classorg.springframework.orm.hibernate3.HibernateTransactionManager property namesessionFactory refsessionFactory / /bean !-- 定义事务通知 -- tx:advice idtxAdvice transaction-managertransactionManager tx:attributes tx:method name* propagationREQUIRED / /tx:attributes /tx:advice !-- 定义切面 -- aop:config aop:pointcut idinterceptorPointCuts expressionexecution(* xxx.dao.*.*(..)) / aop:advisor advice-reftxAdvice pointcut-refinterceptorPointCuts / /aop:config /beans 5、全注解 ?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:contexthttp://www.springframework.org/schema/context xmlns:aophttp://www.springframework.org/schema/aop xsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd Index of /schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd Index of /schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd !-- 1.配置数据源 -- context:annotation-config / !-- 2.配置事务管理器 -- context:component-scan base-packagexxx / !-- 3.配置事务管理器 -- bean idsessionFactory classorg.springframework.orm.hibernate3.LocalSessionFactoryBean property nameconfigLocation valueclasspath:hibernate.cfg.xml / property nameconfigurationClass valueorg.hibernate.cfg.AnnotationConfiguration / /bean !-- 定义事务管理器声明式的事务 -- bean idtransactionManager classorg.springframework.orm.hibernate3.HibernateTransactionManager property namesessionFactory refsessionFactory / /bean !-- 启用注解方式的声明式事务如果将这个去掉将不会创建事务 -- tx:annotation-driven transaction-managertransactionManager/ /beans 然后在需要使用事务的类或方法上加上注解Transactional即可。 1.3 编程式事务和声明式事务的区别 编程式事务每次实现都要单独实现业务量大功能复杂时使用编程式事务无疑是痛苦的而声明式事务不同声明式事务属于无侵入式不会影响业务逻辑的实现只需要在配置文件中做相关的事务规则声明或者通过注解的方式便可以将事务规则应用到业务逻辑中。 显然声明式事务管理要优于编程式事务管理这正是Spring倡导的非侵入式的编程方式。唯一不足的地方就是声明式事务管理的粒度是方法级别而编程式事务管理是可以到代码块的但是可以通过提取方法的方式完成声明式事务管理的配置。 二、Spring 事务管理接口介绍 Spring 框架中事务管理相关最重要的 3 个接口如下 PlatformTransactionManager平台事务管理器Spring 事务策略的核心。TransactionDefinition事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)。TransactionStatus事务运行状态。 我们可以把 PlatformTransactionManager 接口可以被看作是事务上层的管理者而 TransactionDefinition 和 TransactionStatus 这两个接口可以看作是事务的描述。 PlatformTransactionManager 会根据 TransactionDefinition 的定义比如事务超时时间、隔离级别、传播行为等来进行事务管理 而 TransactionStatus 接口则提供了一些方法来获取事务相应的状态比如是否新事务、是否可以回滚等等。 2.1 PlatformTransactionManager事务管理接口 Spring 并不直接管理事务而是提供了多种事务管理器 。Spring 事务管理器的接口是PlatformTransactionManager它是Spring 事务结构中的核心接口。 通过这个接口Spring 为各个平台如JDBC(DataSourceTransactionManager)、Hibernate(HibernateTransactionManager)、JPA(JpaTransactionManager)等都提供了对应的事务管理器但是具体的实现就是各个平台自己的事情了。 PlatformTransactionManager 接口的具体实现如下 PlatformTransactionManager接口中定义了三个方法 package org.springframework.transaction; import org.springframework.lang.Nullable; public interface PlatformTransactionManager { // 获得事务 TransactionStatus getTransaction(Nullable TransactionDefinition var1) throws TransactionException; // 提交事务 void commit(TransactionStatus var1) throws TransactionException; // 回滚事务 void rollback(TransactionStatus var1) throws TransactionException; } 通过PlatformTransactionManager的接口 可以看出Spring 事务的 的三个核心方法事务开启 提交事务回滚事务Spring 事务功能的实现 都是围绕这三个方法来实现。 这里多插一嘴。为什么要定义或者说抽象出来PlatformTransactionManager这个接口呢 主要是因为要将事务管理行为抽象出来然后不同的平台去实现它这样我们可以保证提供给外部的行为不变方便我们扩展。 “为什么我们要用接口”  《设计模式》GOF 那本这本书在很多年前都提到过说要基于接口而非实现编程你真的知道为什么要基于接口编程么 纵观开源框架和项目的源码接口是它们不可或缺的重要组成部分。要理解为什么要用接口首先要搞懂接口提供了什么功能。我们可以把接口理解为提供了一系列功能列表的约定接口本身不提供功能它只定义行为。但是谁要用就要先实现我遵守我的约定然后再自己去实现我定义的要实现的功能。 举个例子我上个项目有发送短信的需求为此我们定了一个接口接口只有两个方法: 1.发送短信 2.处理发送结果的方法。 刚开始我们用的是阿里云短信服务然后我们实现这个接口完成了一个阿里云短信的服务。后来我们突然又换到了别的短信服务平台我们这个时候只需要再实现这个接口即可。这样保证了我们提供给外部的行为不变。几乎不需要改变什么代码我们就轻松完成了需求的转变提高了代码的灵活性和可扩展性。 什么时候用接口当你要实现的功能模块设计抽象行为的时候比如发送短信的服务图床的存储服务等等。 2.1.1 JDBC事务 如果应用程序中直接使用JDBC来进行持久化DataSourceTransactionManager会为你处理事务边界。为了使用DataSourceTransactionManager你需要使用如下的XML将其装配到应用程序的上下文定义中 bean idtransactionManager classorg.springframework.jdbc.datasource.DataSourceTransactionManager property namedataSource refdataSource / /bean 实际上DataSourceTransactionManager是通过调用java.sql.Connection来管理事务而后者是通过DataSource获取到的。通过调用连接的commit()方法来提交事务同样事务失败则通过调用rollback()方法进行回滚。 2.1.2 Hibernate事务 如果应用程序的持久化是通过Hibernate实习的那么你需要使用HibernateTransactionManager。对于Hibernate3需要在Spring上下文定义中添加如下的bean声明 bean idtransactionManager classorg.springframework.orm.hibernate3.HibernateTransactionManager property namesessionFactory refsessionFactory / /bean sessionFactory属性需要装配一个Hibernate的session工厂HibernateTransactionManager的实现细节是它将事务管理的职责委托给org.hibernate.Transaction对象而后者是从Hibernate Session中获取到的。当事务成功完成时HibernateTransactionManager将会调用Transaction对象的commit()方法反之将会调用rollback()方法。 2.1.3 Java持久化API事务JPA Hibernate多年来一直是事实上的Java持久化标准但是现在Java持久化API作为真正的Java持久化标准进入大家的视野。如果你计划使用JPA的话那你需要使用Spring的JpaTransactionManager来处理事务。你需要在Spring中这样配置JpaTransactionManager bean idtransactionManager classorg.springframework.orm.jpa.JpaTransactionManager property namesessionFactory refsessionFactory / /bean JpaTransactionManager只需要装配一个JPA实体管理工厂javax.persistence.EntityManagerFactory接口的任意实现。JpaTransactionManager将与由工厂所产生的JPA EntityManager合作来构建事务。 2.1.4 Java原生API事务 如果你没有使用以上所述的事务管理或者是跨越了多个事务管理源比如两个或者是多个不同的数据源你就需要使用JtaTransactionManager bean idtransactionManager classorg.springframework.transaction.jta.JtaTransactionManager property nametransactionManagerName valuejava:/TransactionManager / /bean JtaTransactionManager将事务管理的责任委托给javax.transaction.UserTransaction和javax.transaction.TransactionManager对象其中事务成功完成通过UserTransaction.commit()方法提交事务失败通过UserTransaction.rollback()方法回滚。 2.2 TransactionInfo事务信息 事务信息对象包括一个事务所有的信息持有事务管理器、事务定义对象、目标方法唯一标识、事务状态对象、外层的TransactionInfo外层的TransactionInfo 用于在应用程序中获取 当前的TransactionInfo 对象。 2.3 TransactionDefinition事务定义 事务管理器接口 PlatformTransactionManager 通过 getTransaction(TransactionDefinition definition) 方法来得到一个事务这个方法里面的参数是 TransactionDefinition 类 这个类就定义了一些基本的事务属性。 什么是事务属性呢事务属性可以理解成事务的一些基本配置描述了事务策略如何应用到方法上。 事务属性包含了 5 个方面 隔离级别传播行为回滚规则是否只读事务超时 TransactionDefinition 接口中定义了 5 个方法以及一些表示事务属性的常量比如隔离级别、传播行为等等。 package org.springframework.transaction; import org.springframework.lang.Nullable; public interface TransactionDefinition { int PROPAGATION_REQUIRED 0; int PROPAGATION_SUPPORTS 1; int PROPAGATION_MANDATORY 2; int PROPAGATION_REQUIRES_NEW 3; int PROPAGATION_NOT_SUPPORTED 4; int PROPAGATION_NEVER 5; int PROPAGATION_NESTED 6; int ISOLATION_DEFAULT -1; int ISOLATION_READ_UNCOMMITTED 1; int ISOLATION_READ_COMMITTED 2; int ISOLATION_REPEATABLE_READ 4; int ISOLATION_SERIALIZABLE 8; int TIMEOUT_DEFAULT -1; // 返回事务的传播行为默认值为 REQUIRED。 int getPropagationBehavior(); // 返回事务的隔离级别默认值是 DEFAULT int getIsolationLevel(); // 返回事务的超时时间默认值为-1。如果超过该时间限制但事务还没有完成则自动回滚事务。 int getTimeout(); // 返回是否为只读事务默认值为 false boolean isReadOnly(); Nullable String getName(); } 2.4 TransactionStatus事务状态 TransactionStatus接口用来记录事务的状态。该接口定义了一组方法用来获取或判断事务的相应状态信息。 PlatformTransactionManager.getTransaction(…)方法返回一个 TransactionStatus 对象。 TransactionStatus 接口内容如下 public interface TransactionStatus{ boolean isNewTransaction(); // 是否是新的事务 boolean hasSavepoint(); // 是否有恢复点 void setRollbackOnly();  // 设置为只回滚 boolean isRollbackOnly(); // 是否为只回滚 boolean isCompleted; // 是否已完成 } 2.5 TransationSynchronization事务同步回调接口 事务同步回调接口在事务 周期的各个点 执行回调 方法。比如 挂起 继续提交前后 完成前后 。用于 管理 应用程序在事务周期中绑定的资源。在Spring - Mybatis 整合时正式Mybatis 正式利用了TransationSynchronization同步器才让Mybatis 的事务管理交给了 Spring 事务来管理。 2.6 TransactionSynchronizationManager事务同步回调的管理器 在事务运行过程中需要保存一些状态比如 数据库连接 ThreadLocalMapObject, Object resources - 应用代码随事务的声明周期绑定的对象   ThreadLocalSetTransactionSynchronization synchronizations-使用的同步器用于应用扩展   ThreadLocalString currentTransactionName-事务的名称   ThreadLocalBoolean currentTransactionReadOnly-事务是否是只读   ThreadLocalInteger currentTransactionIsolationLevel-事务的隔离级别   ThreadLocalBoolean actualTransactionActive-是否实际的开启了事务如果加入 到 别的事务就不是实际开启事务。 2.7 SuspendedResourceHolder挂起的资源持有对象 在挂起一个事务时用于记录被挂起事务的运行时信息这些信息就是TransactionSynchronizationManager中记录的事务信息。然后将这些信息 保存在 新的DefaultTransactionStatus对象中便于内部事务运行结束后恢复外层事务。 三、事务属性详解 实际业务开发中大家一般都是使用 Transactional 注解来开启事务很多人并不清楚这个参数里面的参数是什么意思有什么用。为了更好的在项目中使用事务管理强烈推荐好好阅读一下下面的内容。 3.1 事务传播行为事务传播机制 事务传播行为是为了解决业务层方法之间互相调用的事务问题。事务的传播性一般用在事务嵌套 的场景比如一个事务方法里面调用了另外一个事务方法那么两个方法是各自作为独立的方法提交还是内层的事务合并到外层的事务一起提交这就是需要事务传播机制的配置来确定怎么样执行。 当事务方法被另一个事务方法调用时必须指定事务应该如何传播。例如方法可能继续在现有事务中运行也可能开启一个新事务并在自己的事务中运行。 举个例子我们在 A 类的aMethod()方法中调用了 B 类的 bMethod() 方法。这个时候就涉及到业务层方法之间互相调用的事务问题。如果我们的 bMethod()发生异常需要回滚如何配置事务传播行为才能让 aMethod()也跟着回滚呢这个时候就需要事务传播行为的知识了如果你不知道的话一定要好好看一下。 Service Class A { Autowired B b; Transactional(propagation Propagation.xxx) public void aMethod { //do something b.bMethod(); } } Service Class B { Transactional(propagation Propagation.xxx) public void bMethod { //do something } } 在TransactionDefinition定义中包括了如下几个表示传播行为的常量 public interface TransactionDefinition { int PROPAGATION_REQUIRED 0; int PROPAGATION_SUPPORTS 1; int PROPAGATION_MANDATORY 2; int PROPAGATION_REQUIRES_NEW 3; int PROPAGATION_NOT_SUPPORTED 4; int PROPAGATION_NEVER 5; int PROPAGATION_NESTED 6; ...... } 不过为了方便使用Spring 相应地定义了一个枚举类Propagation package org.springframework.transaction.annotation; import org.springframework.transaction.TransactionDefinition; public enum Propagation { //如果当前存在事务则加入该事务 如果当前没有事务则创建一个新的事务 REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED) //如果当前存在事务则加入该事务 如果当前没有事务 则以非事务的方式继续运行 SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS) //如果当前存在事务则加入该事务 如果当前没有事务 则抛出异常 MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY) //创建一个新的事务 如果当前存在事务 则把当前事务挂起 REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW) //以非事务方式运行 如果当前存在事务 则把当前事务挂起 NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED) //以非事务方式运行如果当前存在事务则抛出异常 NEVER(TransactionDefinition.PROPAGATION_NEVER) //如果当前存在事务 则创建一个事务作为当前事务的嵌套事务来运行 如果不存在则创建一个新的事务 NESTED(TransactionDefinition.PROPAGATION_NESTED); private final int value; Propagation(int value) { this.value value; } public int value() { return this.value; } } 正确的事务传播行为可能的值如下 1.TransactionDefinition.PROPAGATION_REQUIRED REQUIRED 是 Spring 默认的事务传播类型该传播类型的特点是当前方法存在事务时子方法加入该事务。此时父子方法共用一个事务无论父子方法哪个发生异常回滚整个事务都回滚。即使父方法捕捉了异常也是会回滚。而当前方法不存在事务时子方法新建一个事务。 它是使用的最多的一个事务传播行为我们平时经常使用的Transactional注解默认使用就是这个事务传播行为。如果当前存在事务则加入该事务如果当前没有事务则创建一个新的事务。也就是说 如果外部方法没有开启事务的话Propagation.REQUIRED修饰的内部方法会新开启自己的事务且开启的事务相互独立互不干扰。如果外部方法开启事务并且被Propagation.REQUIRED修饰的话所有Propagation.REQUIRED修饰的内部方法和外部方法均属于同一事务 只要一个方法回滚整个事务均回滚。 举个例子如果我们上面的aMethod()和bMethod()使用的都是PROPAGATION_REQUIRED传播行为的话两者使用的就是同一个事务只要其中一个方法回滚整个事务均回滚。 Service Class A { Autowired B b; Transactional(propagation Propagation.REQUIRED) public void aMethod { //do something b.bMethod(); } } Service Class B { Transactional(propagation Propagation.REQUIRED) public void bMethod { //do something } } 为了验证 REQUIRED 事务传播类型的特点我们再来做几个测试。 还是上面 methodA 和 methodB 的例子。当 methodA 不开启事务methodB 开启事务这时候 methodB 就是独立的事务而 methodA 并不在事务之中。因此当 methodB 发生异常回滚时methodA 中的内容就不会被回滚。用如下的代码就可以验证我们所说的。 public void methodA(){ System.out.println(methodA); tableService.insertTableA(new TableEntity()); transactionServiceB.methodB(); } Transactional public void methodB(){ System.out.println(methodB); tableService.insertTableB(new TableEntity()); throw new RuntimeException(); } 最终的结果是tablea 插入了数据tableb 没有插入数据符合了我们的猜想。 当 methodA 开启事务methodB 也开启事务。按照我们的结论此时 methodB 会加入 methodA 的事务。此时我们验证当父子事务分别回滚时另外一个事务是否会回滚。 我们先验证第一个当父方法事务回滚时子方法事务是否会回滚 Transactional public void methodA(){ tableService.insertTableA(new TableEntity()); transactionServiceB.methodB(); throw new RuntimeException(); } Transactional public void methodB(){ tableService.insertTableB(new TableEntity()); } 结果是talbea 和 tableb 都没有插入数据即父事务回滚时子事务也回滚了。 我们继续验证第二个当子方法事务回滚时父方法事务是否会回滚 Transactional public void methodA(){ tableService.insertTableA(new TableEntity()); transactionServiceB.methodB(); } Transactional public void methodB(){ tableService.insertTableB(new TableEntity()); throw new RuntimeException(); } 结果是talbea 和 tableb 都没有插入数据即子事务回滚时父事务也回滚了。 我们继续验证第三个当字方法事务回滚时父方法捕捉了异常父方法事务是否会回滚 Transactional public void methodA() { tableService.insertTableA(new TableEntity()); try { transactionServiceB.methodB(); } catch (Exception e) { System.out.println(methodb occur exp.); } } Transactional public void methodB() { tableService.insertTableB(new TableEntity()); throw new RuntimeException(); } 结果是talbea 和 tableb 都没有插入数据即子事务回滚时父事务也回滚了。所以说这也进一步验证了我们之前所说的REQUIRED 传播类型它是父子方法共用同一个事务的。 2.TransactionDefinition.PROPAGATION_REQUIRES_NEW REQUIRES_NEW 也是常用的一个传播类型该传播类型的特点是无论当前方法是否存在事务子方法都新建一个事务。此时父子方法的事务时独立的它们都不会相互影响。但父方法需要注意子方法抛出的异常避免因子方法抛出异常而导致父方法回滚。 创建一个新的事务如果当前存在事务则把当前事务挂起。也就是说不管外部方法是否开启事务Propagation.REQUIRES_NEW修饰的内部方法会新开启自己的事务且开启的事务相互独立互不干扰。 举个例子如果我们上面的bMethod()使用PROPAGATION_REQUIRES_NEW事务传播行为修饰aMethod还是用PROPAGATION_REQUIRED修饰的话。如果aMethod()发生异常回滚bMethod()不会跟着回滚因为 bMethod()开启了独立的事务。但是如果 bMethod()抛出了未被捕获的异常如果在bMethod方法内try...catch处理了这个异常那么aMethod就不会检测到这个异常也就不会回滚了并且这个异常满足事务回滚规则的话aMethod()同样也会回滚因为这个异常被 aMethod()的事务管理机制检测到了。 Service Class A { Autowired B b; Transactional(propagation Propagation.REQUIRED) public void aMethod { //do something b.bMethod(); } } Service Class B { Transactional(propagation Propagation.REQUIRES_NEW) public void bMethod { //do something } } 为了验证 REQUIRES_NEW 事务传播类型的特点我们再来做几个测试。 首先我们来验证一下当父方法事务发生异常时子方法事务是否会回滚 Transactional public void methodA(){ tableService.insertTableA(new TableEntity()); transactionServiceB.methodB(); throw new RuntimeException(); } Transactional(propagation Propagation.REQUIRES_NEW) public void methodB(){ tableService.insertTableB(new TableEntity()); } 结果是tablea 没有插入数据tableb 插入了数据即父方法事务回滚了但子方法事务没回滚。这可以证明父子方法的事务是独立的不相互影响。 下面我们来看看当子方法事务发生异常时父方法事务是否会回滚 Transactional public void methodA(){ tableService.insertTableA(new TableEntity()); transactionServiceB.methodB(); } Transactional(propagation Propagation.REQUIRES_NEW) public void methodB(){ tableService.insertTableB(new TableEntity()); throw new RuntimeException(); } 结果是tablea 没有插入了数据tableb 没有插入数据。 从这个结果来看貌似是子方法事务回滚导致父方法事务也回滚了。但我们不是说父子事务都是独立的不会相互影响么怎么结果与此相反呢 其实是因为子方法抛出了异常而父方法并没有做异常捕捉此时父方法同时也抛出异常了于是 Spring 就会将父方法事务也回滚了。如果我们在父方法中捕捉异常那么父方法的事务就不会回滚了修改之后的代码如下所示。 Transactional public void methodA(){ tableService.insertTableA(new TableEntity()); // 捕捉异常 try { transactionServiceB.methodB(); } catch (Exception e) { e.printStackTrace(); } } Transactional(propagation Propagation.REQUIRES_NEW) public void methodB(){ tableService.insertTableB(new TableEntity()); throw new RuntimeException(); } 结果是tablea 插入了数据tableb 没有插入数据。这正符合我们刚刚所说的父子事务是独立的并不会相互影响。 这其实就是我们上面所说的父方法需要注意子方法抛出的异常避免因子方法抛出异常而导致父方法回滚。因为如果执行过程中发生 RuntimeException 异常和 Error 的话那么 Spring 事务是会自动回滚的。 3.TransactionDefinition.PROPAGATION_NESTED NESTED 也是常用的一个传播类型该方法的特性与 REQUIRED 非常相似其特性是当前方法存在事务时子方法加入在嵌套事务执行。当父方法事务回滚时子方法事务也跟着回滚。当子方法事务发送回滚时父事务是否回滚取决于是否捕捉了异常。如果捕捉了异常那么就不回滚否则回滚。 可以看到 NESTED 与 REQUIRED 的区别在于父方法与子方法对于共用事务的描述是不一样的REQUIRED 说的是共用同一个事务而 NESTED 说的是在嵌套事务执行。这一个区别的具体体现是在子方法事务发生异常回滚时父方法有着不同的反应动作。 对于 REQUIRED 来说无论父子方法哪个发生异常全都会回滚。而 REQUIRED 则是父方法发生异常回滚时子方法事务会回滚。而子方法事务发送回滚时父事务是否回滚取决于是否捕捉了异常。 如果当前存在事务就在嵌套事务内执行如果当前没有事务就执行与TransactionDefinition.PROPAGATION_REQUIRED类似的操作。也就是说 在外部方法开启事务的情况下在内部开启一个新的事务作为嵌套事务存在。如果外部方法无事务则单独开启一个事务与 PROPAGATION_REQUIRED 类似。 这里还是简单举个例子如果 bMethod() 回滚的话aMethod()不会回滚内部事务回滚外部事务不回滚。如果 aMethod() 回滚的话bMethod()会回滚外部事务回滚内部事务也回滚。 Service Class A { Autowired B b; Transactional(propagation Propagation.REQUIRED) public void aMethod { //do something b.bMethod(); } } Service Class B { Transactional(propagation Propagation.NESTED) public void bMethod { //do something } } 为了验证 NESTED 事务传播类型的特点我们再来做几个测试。 首先我们来验证一下当父方法事务发生异常时子方法事务是否会回滚 TransactionalpublicvoidmethodA() { tableService.insertTableA(newTableEntity()); transactionServiceB.methodB(); thrownewRuntimeException(); } Transactional(propagation Propagation.NESTED) publicvoidmethodB() { tableService.insertTableB(newTableEntity()); } 结果是tablea 和 tableb 都没有插入数据即父子方法事务都回滚了。这说明父方法发送异常时子方法事务会回滚。 接着我们继续验证一下当子方法事务发生异常时如果父方法没有捕捉异常父方法事务是否会回滚 TransactionalpublicvoidmethodA() { tableService.insertTableA(newTableEntity()); transactionServiceB.methodB(); } Transactional(propagation Propagation.NESTED) publicvoidmethodB() { tableService.insertTableB(newTableEntity()); thrownewRuntimeException(); } 结果是tablea 和 tableb 都没有插入数据即父子方法事务都回滚了。这说明子方法发送异常回滚时如果父方法没有捕捉异常try...catch异常那么父方法事务也会回滚。 最后我们验证一下当子方法事务发生异常时如果父方法捕捉了异常父方法事务是否会回滚 TransactionalpublicvoidmethodA() { tableService.insertTableA(newTableEntity()); try{ transactionServiceB.methodB(); } catch(Exceptione) { } } Transactional(propagation Propagation.NESTED) publicvoidmethodB() { tableService.insertTableB(newTableEntity()); thrownewRuntimeException(); } 结果是tablea 插入了数据tableb 没有插入数据即父方法事务没有回滚子方法事务回滚了。这说明子方法发送异常回滚时如果父方法捕捉了异常那么父方法事务就不会回滚。 看到这里相信大家已经对 REQUIRED、REQUIRES_NEW 和 NESTED 这三个最常用的事务传播类型有了深入的理解了。最后让我们来总结一下 事务传播类型 特性 REQUIRED 当前方法存在事务时子方法加入该事务。此时父子方法共用一个事务无论父子方法哪个发生异常回滚整个事务都回滚。即使父方法捕捉了异常也是会回滚。而当前方法不存在事务时子方法新建一个事务。 REQUIRES_NEW 无论当前方法是否存在事务子方法都新建一个事务。此时父子方法的事务时独立的它们都不会相互影响。但父方法需要注意子方法抛出的异常避免因子方法抛出异常而导致父方法回滚。 NESTED 当前方法存在事务时子方法加入在嵌套事务执行。当父方法事务回滚时子方法事务也跟着回滚。当子方法事务发送回滚时父事务是否回滚取决于是否捕捉了异常。如果捕捉了异常那么就不回滚否则回滚。 4.TransactionDefinition.PROPAGATION_MANDATORY 如果当前存在事务则加入该事务如果当前没有事务则抛出异常。mandatory强制性 这个使用的很少就不举例子来说了。 若是错误的配置以下 3 种事务传播行为事务将不会发生回滚这里不对照案例讲解了使用的很少。 TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务则加入该事务如果当前没有事务则以非事务的方式继续运行。TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行如果当前存在事务则把当前事务挂起。TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行如果当前存在事务则抛出异常。 3.2 事务隔离级别 事务的隔离级别定义一个事务可能受其他并发务活动活动影响的程度可以把事务的隔离级别想象为这个事务对于事物处理数据的自私程度。 在一个典型的应用程序中多个事务同时运行经常会为了完成他们的工作而操作同一个数据。并发虽然是必需的但是会导致以下问题 脏读Dirty read 脏读发生在一个事务读取了被另一个事务改写但尚未提交的数据时。如果这些改变在稍后被回滚了那么第一个事务读取的数据就会是无效的。 不可重复读Nonrepeatable read 不可重复读发生在一个事务执行相同的查询两次或两次以上但每次查询结果都不相同时。这通常是由于另一个并发事务在两次查询之间更新了数据。不可重复读重点在修改。 幻读Phantom reads 幻读和不可重复读相似。当一个事务T1读取几行记录后另一个并发事务T2插入了一些记录时幻读就发生了。在后来的查询中第一个事务T1就会发现一些原来没有的额外记录。幻读重点在新增或删除。 在理想状态下事务之间将完全隔离从而可以防止这些问题发生。然而完全隔离会影响性能因为隔离经常涉及到锁定在数据库中的记录甚至有时是锁表。完全隔离要求事务相互等待来完成工作会阻碍并发。因此可以根据业务场景选择不同的隔离级别。 TransactionDefinition 接口中定义了五个表示隔离级别的常量 public interface TransactionDefinition { ...... int ISOLATION_DEFAULT -1; int ISOLATION_READ_UNCOMMITTED 1; int ISOLATION_READ_COMMITTED 2; int ISOLATION_REPEATABLE_READ 4; int ISOLATION_SERIALIZABLE 8; ...... } 和事务传播行为那块一样为了方便使用Spring 也相应地定义了一个枚举类Isolation public enum Isolation { DEFAULT(TransactionDefinition.ISOLATION_DEFAULT), // 最低级别只能保证不读取 物理上损害的数据允许脏读 READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED), // 只能读到已经提交的数据 READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED), // 可重复读 REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ), // 串行化读读写相互阻塞 SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE); private final int value; Isolation(int value) { this.value value; } public int value() { return this.value; } } 下面我依次对每一种事务隔离级别进行介绍 TransactionDefinition.ISOLATION_DEFAULT :使用后端数据库默认的隔离级别MySQL 默认采用的 REPEATABLE_READ 隔离级别 Oracle 默认采用的 READ_COMMITTED 隔离级别.TransactionDefinition.ISOLATION_READ_UNCOMMITTED :最低的隔离级别使用这个隔离级别很少因为它允许读取尚未提交的数据变更可能会导致脏读、幻读或不可重复读TransactionDefinition.ISOLATION_READ_COMMITTED : 允许读取并发事务已经提交的数据可以阻止脏读但是幻读或不可重复读仍有可能发生TransactionDefinition.ISOLATION_REPEATABLE_READ : 对同一字段的多次读取结果都是一致的除非数据是被本身事务自己所修改可以阻止脏读和不可重复读但幻读仍有可能发生。TransactionDefinition.ISOLATION_SERIALIZABLE : 最高的隔离级别完全服从 ACID 的隔离级别。所有的事务依次逐个执行这样事务之间就完全不可能产生干扰也就是说该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。 事务的隔离级别示例 Transactional(isolation Isolation.READ_UNCOMMITTED) 读取未提交数据(会出现脏读, 不可重复读) 基本不使用 3.3 事务超时属性 为了使一个应用程序很好地执行它的事务不能运行太长时间。因此声明式事务的一个特性就是它的超时。 所谓事务超时就是指一个事务所允许执行的最长时间如果超过该时间限制但事务还没有完成则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间其单位是秒默认值为-1这表示事务的超时时间取决于底层事务系统或者没有超时时间。 假设事务的运行时间变得格外的长由于事务可能涉及对数据库的锁定所以长时间运行的事务会不必要地占用数据库资源。这时就可以声明一个事务在特定秒数后自动回滚不必等它自己结束。 由于超时时钟在一个事务启动的时候开始的因此只有对于那些具有可能启动一个新事务的传播行为PROPAGATION_REQUIRES_NEW、PROPAGATION_REQUIRED、ROPAGATION_NESTED的方法来说声明事务超时才有意义。 事务的超时性示例 Transactional(timeout30) 3.4 事务只读属性 如果一个事务只对数据库执行读操作那么该数据库就可能利用那个事务的只读特性采取某些优化措施。通过把一个事务声明为只读可以给后端数据库一个机会来应用那些它认为合适的优化措施。由于只读的优化措施是在一个事务启动时由后端数据库实施的 因此只有对于那些具有可能启动一个新事务的传播行为PROPAGATION_REQUIRES_NEW、PROPAGATION_REQUIRED、 ROPAGATION_NESTED的方法来说将事务声明为只读才有意义。 package org.springframework.transaction; import org.springframework.lang.Nullable; public interface TransactionDefinition { ...... // 返回是否为只读事务默认值为 false boolean isReadOnly(); } 对于只有读取数据查询的事务可以指定事务类型为 readonly即只读事务。只读事务不涉及数据的修改数据库会提供一些优化手段适合用在有多条数据库查询操作的方法中。 很多人就会疑问了为什么我一个数据查询操作还要启用事务支持呢 拿 MySQL 的 innodb 举例子根据官网 MySQL :: MySQL 5.7 Reference Manual :: 14.7.2.2 autocommit, Commit, and Rollbackopen in new window描述MySQL 默认对每一个新建立的连接都启用了autocommit模式。在该模式下每一个发送到 MySQL 服务器的sql语句都会在一个单独的事务中进行处理执行结束后会自动提交事务并开启一个新的事务。 但是如果你给方法加上了Transactional注解的话这个方法执行的所有sql会被放在一个事务中。如果声明了只读事务的话数据库就会去优化它的执行并不会带来其他的什么收益。 如果不加Transactional每条sql会开启一个单独的事务中间被其它事务改了数据都会实时读取到最新值。 分享一下关于事务只读属性其他人的解答 如果你一次执行单条查询语句则没有必要启用事务支持数据库默认支持 SQL 执行期间的读一致性如果你一次执行多条查询语句例如统计查询报表查询在这种场景下多条查询 SQL 必须保证整体的读一致性否则在前条 SQL 查询之后后条 SQL 查询之前数据被其他用户改变则该次整体的统计查询将会出现读数据不一致的状态此时应该启用事务支持 只读示例 Transactional(readOnlytrue) 该属性用于设置当前事务是否为只读事务设置为true表示只读false则表示可读写默认值为false。 3.5 事务回滚规则 这些规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下事务只有遇到运行时异常RuntimeException 的子类时才会回滚Error 也会导致事务回滚但是在遇到检查型Checked异常何IO异常IOException时不会回滚。 例如下面的代码执行后tablea 和 tableb 两个表格都会插入一条数据它们是不会回滚的 Transactional public void methodA() throws Exception { tableService.insertTableA(new TableEntity()); transactionServiceB.methodB(); } Transactional public void methodB() throws Exception { tableService.insertTableB(new TableEntity()); // 非 RuntimeException throw new Exception(); } 回滚示例 指定单一异常类Transactional(rollbackForRuntimeException.class) 指定多个异常类Transactional(rollbackFor{RuntimeException.class, Exception.class}) 该属性用于设置需要进行回滚的异常类数组当方法中抛出指定异常数组中的异常时则进行事务回滚。 如果你想要回滚你定义的特定的异常类型的话可以通过注解设置 Transactional(rollbackFor MyException.class) 也可以通过XML设置 tx:method nameupdate propagationREQUIRED/ 但是比如设置了IO异常就回滚如果在代码中try...catch处理了这个异常之后就不会回滚了。 四、Spring事务使用方法论 看完了事务的传播类型我们对 Spring 事务又有了深刻的理解。 看到这里你应该也明白使用事务不再是简单地使用 Transaction 注解就可以还需要根据业务场景选择合适的传播类型。那么我们再升华一下使用 Spring 事务的方法论。一般来说使用 Spring 事务的步骤为 根据业务场景分析要达成的事务效果确定使用的事务传播类型。在 Service 层使用 Transaction 注解配置对应的 propogation 属性。事务管理一般是对Service层使用的所以以后编写Service层时一定不要忘了加事务管理事务管理是系统开发的最基本素质如果一个程序员在开发的时候不去对Service层做事务管理那么这个程序员就该被开除了 下次遇到要使用事务的情况记得按照这样的步骤去做哦~ Transactional 介绍 Transactional 注解 可以作用于接口、接口方法、类以及类方法上。当作用于类上时该类的所有 public 方法将都具有该类型的事务属性同时我们也可以在方法级别使用该注解来覆盖类级别的定义。 虽然Transactional 注解可以作用于接口、接口方法、类以及类方法上但是 Spring 建议不要在接口或者接口方法上使用该注解因为这只有在使用基于接口的代理时它才会生效。另外 Transactional 注解应该只被应用到 public 方法上这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 Transactional 注解这将被忽略也不会抛出任何异常。
http://www.w-s-a.com/news/28430/

相关文章:

  • 镜像的网站怎么做排名无极网站建设质量
  • 奉贤集团公司网站建设小工具文本wordpress
  • 不用代码做网站网站建设和运行费用
  • 阜阳网站开发招聘网站建设合作协议申请
  • 电子配件 技术支持 东莞网站建设wordpress 生成html代码
  • 网站用免费空间好不好网站建设的视频
  • 网站开发项目职责门户资源分享网站模板
  • 建网站需要什么语言如何做二维码跳转到网站
  • 天津建设交培训中心网站做网站起名字
  • 黑河北京网站建设湛江市住房和城乡建设局网站
  • 网站建设拾金手指下拉十九企业查询官网
  • 邢台提供网站建设公司哪家好五合一建站
  • 京东网站设计代码驻马店做网站的公司
  • 织梦网站模板使用教程福州网站建设工作
  • 做网站要准备的需求asp 网站后台
  • 滨州网站开发公司中立建设集团有限公司网站
  • 泰安建设厅网站做网站为什么要建站点
  • 有什么好的手机推荐网站创建网站需要哪些工作
  • 网站能给企业带来什么上饶市网站建设公司
  • 学做网站卖东西去哪学南宁网站建设gxjzdrj
  • 欧美网站建设案例网站开发 男生
  • 网站正在开发中做电子元器件的网站
  • 做网站搭建的公司中国建设银行官网站u盾证书
  • 大连哪里有手机自适应网站建设公司网站介绍模板 html
  • 佛山模板建站宣传片制作公司电话
  • 文字网站居中能自己做网站接业务吗
  • 免备案自助建站网站广州珈瑶公司是哪一年注册的
  • ps做网站界面wordpress为图片添加圆角
  • seo优化推广业务员招聘seo顾问服务福建
  • 成都私人网站建设seo网站推广方案策划书