JDBC事务

1.获取连接 Connection con=DriverManager.getConnection();
2.开启事务 con.setAutoCommit(true/fase);
3.执行CRUD
4.提交事务或回滚事务 con.commit()/con.rollback()
5.关闭连接 con.close();

数据库隔离级别

隔离级别             隔离级别的值      导致的问题
Read-uncommitted       0            导致脏读;
Read-committed         1            避免脏读,允许不可重复读和幻读
Repeatable-Read        2            避免脏读、不可重复读,允许幻读
Serializable           3            全部避免

脏读:一事务对数据进行了修改,但未提交,另一事务可以读取到未提交的数据,如果第一个事务发生了回滚,那么第二事务就读到了。不可重复读:一个事务中发生了两次读操作,第一次读操作和第二次操作之间,另外一个事务对数据进行了修改,导致两次读的数据不一致。幻读:第一个事务对一定范围的数据进行批量修改,第二个事务在这个范围内增加一条数据,这时候第一个事务就会丢失对新数据的修改。

spring事务的传播性

事务传播性就定义在多个事务同时存在的时候,spring应该如何处理这些事务的行为,以事务嵌套为例,来深入理解spring事务传播的机制;
假设:外层事务servicea的methoda()调用内层service的methodb()若spring的级别为:propagation_required(spring的默认值)。如果serviceb.methodb()的事务级别定位为propagation_required,那么执行servicea.methoda()的时候,spring已经发起了事务,这时调用serviceb.methodb(),serviceb.methodb()看到自己已经运行在servicea.methoda()的事务内部,就不再起新的事务。假设serviceb.methodb()运行的时候发现自己没有在事务中,他就会为自己分配一个事务。这样,在servicea.methoda()或者serviceb.methodb()内的任何地方出现异常,事务都会被回滚。

全局事务和本地事务

所谓的全局事务也可以理解为分布式事务,也就是说程序需要处理来自不同的数据库连接;

所谓的本地事务可以理解为应用程序不需要处理分布式的数据库集群,只处理单一的数据源。

spring的关键抽象

spring事务抽象中的关键是,事务策略的概念,这个概念由org.springframework.transcation.PlatformTransactionManager接口定义。使用spring时,无论选择编程式事务管理,还是声明式事务管理,都必须定义一个正确的PlatformTransactionManager实现。举例如下所示:

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransationManager">
    <property name="dataSource" ref="dataSource">
</bean>

通过创建事务管理器,就可将应用连接到不同的事务源上。

spring使用资源同步的事务

通过配置事务管理器,我们将应用连接到了不同的事务源上,接下来我们需要直接或间接地获取一种持久化api(jdbc等)的应用代码,来获取或操作资源以实现事务的同步。

spring提供了两种解决方案,一种是高层抽象的解决方案即对所有持久化API都采用这种 模板 方法,包括 JdbcTemplate、HibernateTemplate和JdoTemplate类,另外一种是低层的解决方案,有以下这些类:DataSourceUtils(针对JDBC),SessionFactoryUtils(针对Hibernate),PersistenceManagerFactoryUtils(针对JDO)等等。

当对应用代码来说,直接同原始持久化API特有的资源类型打交道是更好的选择时,这些类确保应用代码获取到正确的Spring框架所管理的bean,事务被正确同步,处理过程中的异常被映射到一致的API。

spring声明式事务管理

Spring的事务管理是通过AOP代理实现的。 其中的事务通知由元数据(目前基于XML或注解)驱动。 代理对象与事务元数据结合产生了一个AOP代理,它使用一个PlatformTransactionManager 实现品配合TransactionInterceptor,在方法调用前后实施事务。

举例说明:

    // 我们想做成事务性的服务接口

    package x.y.service;
    
    public interface FooService {
    
      Foo getFoo(String fooName);
    
      Foo getFoo(String fooName, String barName);
    
      void insertFoo(Foo foo);
    
      void updateFoo(Foo foo);
    
    }
    // 上述接口的一个实现

    package x.y.service;
    
    public class DefaultFooService implements FooService {
    
      public Foo getFoo(String fooName) {
        throw new UnsupportedOperationException();
      }
    
      public Foo getFoo(String fooName, String barName) {
        throw new UnsupportedOperationException();
      }
    
      public void insertFoo(Foo foo) {
        throw new UnsupportedOperationException();
      }
    
      public void updateFoo(Foo foo) {
        throw new UnsupportedOperationException();
      }
    
    }

我们假定,FooService的前两个方法(getFoo(String) 和getFoo(String, String))必须执行在只读事务上下文中,其他的方法(insertFoo(Foo)和 updateFoo(Foo))必须执行在可读写事务上下文中。定义配置文件如下:

   <!-- from the file 'context.xml' -->
   <?xml version="1.0" encoding="UTF-8"?>
   <beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:aop="http://www.springframework.org/schema/aop"
     xmlns:tx="http://www.springframework.org/schema/tx"
     xsi:schemaLocation="
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
  
  <!-- this is the service object that we want to make transactional -->
  <bean id="fooService" class="x.y.service.DefaultFooService"/>

  <!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) -->
  <tx:advice id="txAdvice" transaction-manager="txManager">
  <!-- the transactional semantics... -->
  <tx:attributes>
    <!-- all methods starting with 'get' are read-only -->
    <tx:method name="get*" read-only="true"/>
    <!-- other methods use the default transaction settings (see below) -->
    <tx:method name="*"/>
  </tx:attributes>
  </tx:advice>
  
  <!-- ensure that the above transactional advice runs for any execution
    of an operation defined by the FooService interface -->
  <aop:config>
  <aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
  <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
  </aop:config>
  
  <!-- don't forget the DataSource -->
  <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
  <property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
  <property name="username" value="scott"/>
  <property name="password" value="tiger"/>
  </bean>

  <!-- similarly, don't forget the PlatformTransactionManager -->
  <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"/>
  </bean> 
  <!-- other <bean/> definitions here -->
  </beans>
  

分析一下上面的配置。我们要把一个服务对象('fooService' bean)做成事务性的。 施加的事务语义封装在<tx:advice/>定义中。<tx:advice/> “把所有以 'get' 开头的方法看做执行在只读事务上下文中, 其余的方法执行在默认语义的事务上下文中”。 其中的 'transaction-manager' 属性被设置为一个指向 PlatformTransactionManager bean的名字(这里指 'txManager'),该bean将会真正管理事务。

配置中最后一段是 <aop:config/> 的定义, 它确保由 'txAdvice' bean定义的事务通知在应用中合适的点被执行。 首先我们定义了 一个切面,它匹配 FooService 接口定义的所有操作, 我们把该切面叫做 'fooServiceOperation'。然后我们用一个通知器(advisor)把这个切面与 'txAdvice' 绑定在一起, 表示当 'fooServiceOperation' 执行时,'txAdvice' 定义的通知逻辑将被执行。
<aop:pointcut/> 元素定义是AspectJ的切面表示法.

上面的配置将为'fooService' bean创建一个代理对象,这个代理对象被装配了事务通知,所以当它的相应方法被调用时,一个事务将被启动、挂起、被标记为只读,或者其它(根据该方法所配置的事务语义)。

spring回滚

在Spring框架的事务架构里,当context的事务里的代码抛出 Exception 时事务进行回滚。Spring框架的事务基础架构代码将从调用的堆栈里捕获到任何未处理的 Exception,并将标识事务将回滚。然而,请注意Spring框架的事务基础架构代码将默认地只在抛出运行时和unchecked exceptions时才标识事务回滚。也就是说,当抛出一个 RuntimeException 或其子类例的实例时。(Errors 也一样 - 默认地 - 标识事务回滚。)从事务方法中抛出的Checked exceptions将不被标识进行事务回滚。

下面的XML配置片断里示范了如何配置一个用于回滚的checked、应用程序特定的 Exception 类型。

<tx:advice id="txAdvice" transaction-manager="txManager">
  <tx:attributes>
  <tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/>
  <tx:method name="*"/>
  </tx:attributes>
</tx:advice>

有时候你不想在异常抛出的时候回滚事务,就可以使用“不回滚规则”。 在下面的例子中,我们告诉Spring 框架即使遇到没有经过处理的InstrumentNotFoundException异常,也不要回滚事务。

<tx:advice id="txAdvice">
  <tx:attributes>
  <tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/>
  <tx:method name="*"/>
  </tx:attributes>
</tx:advice>

当Spring框架捕获到一个异常后会检查配置回滚规则来决定是不是要回滚事务,这时候会遵循最匹配的规则。 所以在下面这种配置中,除了InstrumentNotFoundException这种类型的异常不会导致事务回滚以外,其他任何类型的异常都会。

<tx:advice id="txAdvice">
  <tx:attributes>
  <tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/>
  </tx:attributes>
</tx:advice>

第二种方法是通过 编程式 方式来指定回滚事务。 虽然写法非常的简单,但是这个方法是高侵入性的,并且使你的代码与Spring框架的事务架构高度耦合。 下面的代码片断里示范了Spring框架管理事务的编程式回滚:

public void resolvePosition() {
  try {
    // some business logic...
  } catch (NoProductInStockException ex) {
    // trigger rollback programmatically
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
  }
}

编程式方法的回滚对你来说是可见,如果你需要它你就可以使用,但是使用它就直接违反了在你的应用中使用一个纯基于POJO的模型。

@Transactional注解

除了基于XML文件的声明式事务配置外,你也可以采用基于注解式的事务配置方法。直接在Java源代码中声明事务语义的做法让事务声明和将受其影响的代码距离更近了,而且一般来说不会有不恰当的耦合的风险,因为,使用事务性的代码几乎总是被部署在事务环境中举例如下:

    @Transactional
    public class DefaultFooService implements FooService {
    
      Foo getFoo(String fooName);
    
      Foo getFoo(String fooName, String barName);
    
      void insertFoo(Foo foo);
    
      void updateFoo(Foo foo);
    }

当上述的POJO定义在Spring IoC容器里时,上述bean实例仅仅通过一 行xml配置就可以使它具有事务性的。如下:

    <!-- from the file 'context.xml' -->
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:aop="http://www.springframework.org/schema/aop"
         xmlns:tx="http://www.springframework.org/schema/tx"
         xsi:schemaLocation="
         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
         http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
         http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
      
      <!-- this is the service object that we want to make transactional -->
      <bean id="fooService" class="x.y.service.DefaultFooService"/>
    
      <!-- enable the configuration of transactional behavior based on annotations -->
      <tx:annotation-driven transaction-manager="txManager"/>
    
      <!-- a PlatformTransactionManager is still required -->
      <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <!-- (this dependency is defined somewhere else) -->
         <property name="dataSource" ref="dataSource"/>
      </bean>
      
      <!-- other <bean/> definitions here -->
    
    </beans>
    

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

Spring团队的建议是你只在具体的类上使用 @Transactional 注解, 而不要注解在接口上。你当然可以在接口(或接口方法)上使用 @Transactional 注解, 但是这只有在你使用基于接口的代理时它才会生效。因为注解是 不能继承 的, 这就意味着如果你正在使用基于类的代理时,事务的设置将不能被基于类的代理所识别,而且对象也不会被事务代理所包装 (这是很糟糕的)。

在<tx:annotation-driven/>元素上的"proxy-target-class" 属性 控制了有什么类型的事务性代理会为使用@Transactional 来注解的类创建代理。 如果"proxy-target-class" 属性被设为"true",那么基于类的代理就会被创建。 如果"proxy-target-class" 属性被设为"false" 或者没设,那么会创建基于接口的标准JDK代理。

在多数情形下,方法的事务设置将被优先执行。在下列情况下,例如: DefaultFooService 类在类的级别上被注解为只读事务,但是,这个类中的 updateFoo(Foo) 方法的 @Transactional 注解的事务设置将优先于类级别注解的事务设置。

@Transactional(readOnly = true)
public class DefaultFooService implements FooService {

  public Foo getFoo(String fooName) {
    // do something
  }

    // these settings have precedence for this method
    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    public void updateFoo(Foo foo) {
        // do something
        
    }
}

    

haofengpingjieli
97 声望5 粉丝

好风凭借力送我上青云