在Spring里添加事务,首先了解一下事务:

  1. 事物的概念
  2. 事物的特性
  3. Spring中的事务控制方式
  4. 事务的管理器
  5. 事务的属性
  6. 事务的传播规则
  7. 事务的隔离机制
  8. 实战(配置,服务层,持久层代码)

事务的概念

  事务是一组操作的执行单元,相当于数据库的单条操作而言,一组SQL指令,如若其中一条发生错误,则整个事务的一组都要进行回滚。

事物的特性

Atomic

Consistent,事务要保证数据库整体数据的完整性和业务的数据的一致性,事务成功提交整体数据修改,事务错误则回滚到数据回到原来的状态。

Isolate,两个事务的执行都是独立的,事务之前不会相互影响,多个事务操作一个对象时会以串行等待的方式保证事务相互之间处于隔离。

Durable,一旦事务成功提交后,数据将会保存到数据库,不能再进行回滚,以后的操作都将在当前数据库状态上继续进行。

事务的控制方式

编程式事务:通过手动编码控制事务的边界,可以细粒度的事务控制。

声明式事务:在Spring中添加配置文件,加载注解,通过Spring的AOP,代理到事务操作,降低代码的耦合。

事务管理器

  事务管理器委托给持久层,持久机制里提供特定的事务管理器实现。

org.springframework.jdbc.datasource.DataSourceTransactionManager:Datasource中管理事务
   org.springframework.orm.ojb.PersistenceBrokerTransactionManager:ojb用作持久化机制时,用它来管理事务

事务的属性介绍

@Transactional(
readOnly
timeout
noRollbackFor
isolation
propagation
  )

是否为只读事务:只读事务不做任何修改,可以优化查询操作。

事务超时(单位为秒):事务的最长持续时间,如果该时间内事务一直没有操作或回滚,则系统将自动进行回滚。-1表示不超时,但最终实现需要由底层数据库实现。

隔离级别:控制并发访问下数据库的安全性。

隔离级别

 

DEFAULT

READ_UNCOMMITED

READ_COMMITTED

REPEATABLE_READ

SERIALIZABLE

意义

默认的隔离级别

 

允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读

 

允许在并发事务已经提交后读取。可防止脏读,但幻读和 不可重复读仍可发生

 

对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,但幻读仍可能发生

 

完全服从事务ACID的隔离级别,确保不发生脏、幻、不可重复读。这在所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表来完成的

 

注:

  不同的隔离级别采用不同的方式来实现,在四种隔离级别中,Serializable的隔离级别最高,Read Uncommited的隔离级别最低。

  大多数据库默认的隔离级别为Read Commited,如SqlServer,Oracle

  当然也有少部分数据库默认的隔离级别为Repeatable_Read ,如Mysql,

  Oracle数据库支持READ COMMITTED和SERIALIZABLE两种事务隔离性级别,不支持READ UNCOMMITTED和REPEATABLE READ这两种隔离性级别。虽然SQL标准定义的默认事务隔离性级别是SERIALIZABLE,但是Oracle数据库默认使用的事务隔离性级别却是READ COMMITTED.

传播规则:定义事务方法和调用事务方法的方法之间的事务边界。

传播行为

 

REQUIRED

NOT_SUPPORTED

REQUIRES_NEW

MANDATORY

SUPPORTS

NEVER

NESTED

意义

业务方法需要在一个事务中运行。如果方法运行时,已经处在一个事务中,那么加入到该事务,否则为自己创建一个新的事务

声明方法不需要事务。如果方法没有关联到一个事务,容器不会为它开启事务。如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行

属性表明不管是否存在事务,业务方法总会为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务会被挂起,新的事务会被创建,直到方法执行结束,新事务才算结束,原先的事务才会恢复执行

该属性指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果业务方法在没有事务的环境下调用,容器就会抛出异常

这一事务属性表明,如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分。如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行

指定业务方法绝对不能在事务范围内执行。如果业务方法在某个事务中执行,容器会抛出异常,只有业务方法没有关联到任何事务,才能正常执行

如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按REQUIRED属性执行.它使用了一个单独的事务, 这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效

实战

在applicationContext_service.xml添加事务配置,注解驱动

<!--事务处理-->
    <!--1.添加事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--因为事务必须关联数据库处理,所以要配置数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
<!--2.添加事务的注解驱动-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

配置数据源

<bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource">
        <!--数据库驱动-->
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
 <!--数据库url-->
        <property name="url" value="jdbc:mysql://hostip/port/v12"></property>
 <!--数据库名-->
        <property name="username" value="root"></property>
 <!--数据库密码-->
        <property name="password" value="root"></property>
    </bean>

测试事务

1创建接口
public interface AccountsService {
    int save(Accounts accounts);
}
2创建实现类
@Service
@Transactional(propagation = Propagation.REQUIRED)   //必须加入这个注解事务才可以正常运行,不然测试类创建的是实现类本身不是动态代理
public class AccountsServiceImpl implements AccountsService {
    //切记切记
    //一定会有数据访问层的对象
 
    @Autowired
    AccountsMapper accountsMapper;
    @Override
    public int save(Accounts accounts) {
        int num=0;
        num=accountsMapper.save(accounts);
        System.out.println("增加账户成功!num="+num);
        //手工抛出异常
        System.out.println(1/0);
        return num;
    }
}
3在mapper层中增加

3.1增加接口
public interface AccountsMapper {
 
    //增加账户
    int save(Accounts accounts);
}
3.2增加实现类的sql映射.xml
<insert id="save" parameterType="accounts">
insert into  accounts values (#{aid},#{aname},#{acontent});
</insert>
4测试  
@Test
    public void test1(){
        //创建容器并启动
        ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext_service.xml");
 
        AccountsService accountsService= (AccountsService) ac.getBean("accountsServiceImpl");
        System.out.println(accountsService.getClass());
         accountsService.save(new Accounts(810, "王伟", "账户安全"));
 
 
    }