Spring事务管理

1.事务的概念

数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。
事务管理用来确保数据的完整性和一致性。

2.事务的特点

(1)原子性(Atomicity):指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生;
(2)一致性(Consistency):指事务前后数据的完整性必须保持一致;
(3)隔离性(Isolation):指多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间数据要相互隔离。即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行;
(4)持久性(Durability):指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,即时数据库发生故障也不应该对其有任何影响。

3.Spring事务

编程式事务

即以代码的方式来管理事物,换句话说,事物将由开发者通过自己写的代码来实现,Spring提供了两个API供我们选择:TransactionTemplate 和 PlatformTransactionManager
对于编程式事务管理,spring推荐使用TransactionTemplate。

声明式事务

声明式事务管理有两种常用的方式:
1、基于tx和aop命名空间的xml配置文件
2、基于@Transactional注解

(1)配置方式

<!--    一、配置方式-->
    <!--配置事务管理器-->
    <bean id="transactionManager"  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />

    </bean>
<!--配置事务处理-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="testTransaction"/>
        </tx:attributes>
    </tx:advice>
<!--Spring事务处理,基于AOP-->
    <aop:config>
        <aop:pointcut id="testCut" expression="execution(* cn.goktech.dao.UserService.testTransaction(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="testCut"/>

    </aop:config>

UserDao

@Repository   //dao注解
public class UserDao {
    @Autowired
    private JdbcTemplate jt;

    public int updatePhone(User user){

        String sql = "update user set phone=? where id=?";
        return  jt.update(sql,new Object[]{user.getPhone(),user.getId()});

    }
}

UserService

@Component
public class UserService {
    @Autowired
    private UserDao userDao;

    public void testTransaction(){
        userDao.updatePhone(new User(1001,null,0,"13112102345",null));
        userDao.updatePhone(new User(1002,null,0,"13527861126",null));
        //产生异常
        userDao.updatePhone(new User(1003,null,0,"138278611260",null));
    }
引用配置文件方式2
<!--    引用配置文件-方式2-->
    <context:property-placeholder location="classpath:jdbc.properties" />
<!--    配置数据源-->
    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
        <property name="driverClassName" value="${driver}" />
        <property name="url" value="${url}" />
        <property name="username" value="${username}" />
        <property name="password" value="${password}" />
     </bean>
对比util标签的方式
<!--引用jdbc.properties配置文件 -->
<util:properties id="jdbc" location="classpath:jdbc.properties" />
<!--配置数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
    <property name="url"  value="#{jdbc.url}"/>
    <property name="driverClassName" value="#{jdbc.driver}" />
    <property name="username" value="#{jdbc.username}" />
    <property name="password" value="#{jdbc.password}" />
    <property name="maxIdle" value="#{jdbc.maxIdle}" />
</bean>

(2)注解方式

applicationContext.xml

<!--二、声明式事务:扫描注解方式-->
    <tx:annotation-driven transaction-manager="transactionManager" />

UserService

//    注解方式处理事务,只能用于public方法上,实现类上
    @Transactional
    public void testTransAnno(){
        userDao.updatePhone(new User(1001,null,0,"13111111111",null));
        userDao.updatePhone(new User(1002,null,0,"13522222222",null));
        //产生异常
        userDao.updatePhone(new User(1003,null,1000,"138333333330",null));
    }

4.@Transactional相关属性

@Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED,rollbackFor = IOException.class,noRollbackFor = NullPointerException.class)
public void testTransAnno(){

注解方式处理事务,只能用于public方法
事务并发访问问题及隔离级别(isolation)
事务传播行为(propagation)
rollbackFor,noRollbackFor:手动指定异常回滚的默认行为

在这里插入图片描述
在这里插入图片描述
使用@Transactional注解需要注意的问题:
1、如果在接口、实现类或方法上都指定了@Transactional 注解,则优先级顺序为方法>实现类>接口;
2、虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效;
3、@Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。


事务并发问题及隔离级别

事务并发可能存在的问题:

1.脏读:一个事务读到另一个事务未提交的更新数据。
2.不可重复读:一个事务两次读同一行数据,可是这两次读到的数据不一样。
3.幻读:一个事务执行两次查询,但第二次查询比第一次查询多出了一些数据行。
4.丢失更新:撤消一个事务时,把其它事务已提交的更新的数据覆盖了。

事务隔离级别:

1、DEFAULT 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。
2、未提交读(read uncommited) :是最低的事务隔离级别,它允许另外一个事务可以看到这个事务未提交的数据,脏读,不可重复读,虚读都有可能发生。
3、已提交读 (read commited):保证一个事物提交后才能被另外一个事务读取。另外一个事务不能读取该事物未提交的数据避免脏读。但是不可重复读和虚读有可能发生 。
4、可重复读 (repeatable read):避免脏读和不可重复读.但是虚读有可能发生.。
5、串行化的 (serializable):这是花费最高代价但最可靠的事务隔离级别。事务被处理为顺序执行。可以避免所有读问题。
其中,MySQL默认采用repeatable read隔离级别;Oracle默认采用read commited隔离级别,隔离级别越高,意味着数据库事务并发执行性能越差,能处理的操作就越少。

Spring实现事务隔离

在Spring中定义了5个表示隔离级别的常量,只需对@Transactional的isolation属性设置即可:
1、Isolation.DEFAULT:默认值,表示使用底层数据库的默认隔离级别;
2、Isolation.READ_UNCOMMITTED:最低级别隔离,该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别
3、Isolation.READ_COMMITTED:读取已提交数据(会出现不可重复读和幻读)
4、Isolation.REPEATABLE_READ:可重复读(会出现幻读)
5、 Isolation.SERIALIZABLE:串行化

事务传播行为

事务传播是指一个方法调用了另一个带有事务控制的方法,这种复杂情况就需要指定事务传播的 处理方案
Spring中的事务的传播类型有以下几种:

事务传播类型作用描述
REQUIRED支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择
SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行
MANDATORY支持当前事务,如果没有事务,就抛出异常
REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起来
NOT_SUPPORTED以非事务方式执行,如果当前存在事务,就把当前事务挂起
NEVER以非事务方式执行,如果当前存在事务,则抛出异常
NESTED如果当前存在事务,则在嵌套事务内执行。拥有多个可以回滚 的保存点,内部回滚不会对外部事务产生影响,只对DataSourceTransactionManager有效

利用@Transactional注解的propagation属性可以控制事务的传播行为

@Transactional(propagation=Propagation.REQUIRED)
	public void f1(int id){
	//业务处理逻辑
	}

事务回滚操作

@Transactional默认情况下RuntimeException异常将触发事务回滚,但是任何Checkedexception将不触发事务回滚
常见的RuntimeException和CheckedException如表所示

RuntimeExceptionCheckedException
ArithmeExceptionClassNotFoundException
NullPointerExceptionNotSuchMetodException
ClassCastExceptionNoSuchFieldException
NumberFormatExceptionCloneNotSupportedException
IndexOutOfBoundsExceptionIOException
NegativeArraySzieException
UnsupprotedOperationException

对于CheckedException,需要手动指定异常类型,才能实现事务回滚,
1、使用注解实现声明式事务时,按如下方式指定异常:
@Transactional(rollbackFor=ClassNotFoundException.class)
2、使用XML配置实现声明式事务时,按如下方式指定异常:
<tx:method name=“update”propagation=“REQUIRED”
rollback-for=“java.lang.ClassNotFoundException”/>

当使用自定义异常时,异常类必须从RuntimeException继承才能自动回滚事务,否则需要制定事务回滚的异常类型。

5.Service——业务逻辑层

Controller——>Service——>ServiceImpl(@Service)——>Dao

//业务具体实现类
@Service
public class HelloServiceImpl implements HelloService {
    @Autowired
    private UserDao userDao;

    public int add(int a, int b) {
        return a+b;
    }

    public int insert(User user) {
        return 0;
    }
}

@Service是加在实现类HelloServiceImpl类上面的
Service是业务逻辑层,接口

//业务逻辑层,接口
public interface HelloService {
    public int add(int a,int b);

    public int insert(User user);
}

在Controller中注入的对象是Service的对象,不是实现类的对象

@Controller
public class HelloController {

    //Controller——>Service——>ServiceImpl(@Service)——>Dao
    @Autowired
    private HelloService helloService;

    @RequestMapping("/testService")
    public String testService(){
        System.out.println(helloService.add(10,20));
        return "index";
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值