什么是事务
事务是数据库的核心概念之一,它表示数据库一系列操作的集合。这些操作必须在一个事务当中,要么全部执行成功,要么全部不执行。
数据库中的事务语句
使用 BEGIN
开启一个事务
使用 COMMIT
提交一个事务
使用 ROLLBACK
回滚事务
ACID特性
原子性(Atomicity)
原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
一致性(Consistency)
一个事务执行之前和执行之后都必须处于一致性状态。例如转账,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
隔离性(Isolation)
隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
持久性(Durability)
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
Java事务类型
JDBC事务(本地事务)
JDBC事务也称之为本地事务,主要是利用Connection对象控制,可以将多个SQL语句操作放在一个事务中执行。Connection接口提供了两种事务管理的方式:自动提交和手动提交。但是JDBC事务的一个缺点就是事务范围局限于一个数据库连接中,并不能跨多个数据库。
JTA事务(分布式事务)(BMT)(原理:多个本地事务时,当其中一个失败时所有事务回滚)
JTA(Java Transaction API)是Java中的事务标准API,为J2EE 平台提供了分布式事务的支持。它与实现和协议无关,应用程序和应用服务器之间可以使用JTA来操作事务。JTA事务允许应用程序同时操作多个不同的数据库,这些数据库可以分布在不同的网络中,这就是所谓的分布式事务处理。市面上也出现一些主流开源的JTA事务框架,如:JOTM、Atomikos等。分布式事务处理也需要数据库驱动的支持。而大部分数据库的JDBC驱动都会支持这种分布式事务处理(通常都会实现XADataSource与XAConnection)。
容器事务(CMT)
容器事务主要由J2EE应用服务器提供(通常指的是EJB容器),这些容器事务大多基于JTA事务标准来实现。相对于编程式的JTA事务管理,我们可以通过容器提供的事务管理机制来完成相同的功能。这样我们不必以硬编码的方式来控制事务,完全由容器托管,我们只需简单的指定哪些方法需要加入事务,一旦指定,容器将负责事务的管理。
Spring事务管理
Spring支持本地事务以及JTA事务(如果需要使用JTA事务,可以集成第三方的JTA事务实现框架,如:JTOM)。并且Spring提供了两种方式来管理事务,一种是编程式事务,另一种是声明式事务。
编程式事务
所谓编程式指的是在程序中以编码的方式使用TransactionTemplate或者直接使用PlatformTransactionManager。对于编程式事务管理,spring推荐使用 TransactionTemplate。
声明式事务
声明式事务是建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,只需要在配置文件中做相应的配置或者使用事务注解进行声明即可。
Spring的事务传播性
事务的传播性一般在事务嵌套时候使用,比如在事务A里面调用了另外一个使用事务的方法,那么这俩个事务是各自作为独立的事务执行提交,还是内层的事务合并到外层的事务一块提交,这就是事务传播性要确定的问题。
REQUIRED(常用,默认)
业务方法需要在一个事务中运行。如果方法运行时,已经处在一个事务中,那么这个时候就会加入到该事务中,如果当前没有事务环境的话,就会为自己创建一个新的事务。
(如果存在一个事务,则加入当前事务。如果没有事务则开启)
SUPPORTS
如果业务方法A在某个事务范围内被调用,则方法成为事务的一部分。如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行。即当标注了事务传播属性——SUPPORTS的业务方法在另一个bean的业务方法中执行时,如果另一个bean的业务方法开启了事务,它就会处在事务中执行,如果另一个bean的业务方法也没开启事务,那么它也在没有事务的环境中进行。
(如果存在一个事务,则支持当前事务。如果没有事务,则非事务的执行)
MANDATORY
该属性指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果业务方法在没有事务的环境下调用,容器就会抛出异常。一种比较强硬的方式。
(如果存在一个事务,则支持当前事务。如果没有一个活动的事务,则抛出异常)
REQUIRES_NEW
该属性表明不管当前是否存在事务,业务方法总会为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务会被挂起,新的事务会被创建,直到方法执行结束,新事务才算结束,原先的事务才会恢复执行。
( 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起)
NOT_SUPPORTED
声明方法不需要事务。如果方法没有关联到一个事务,容器不会为它开启事务。如果方法在一个事务中被调用(在其他业务bean的方法中被调用了,而其他业务bean的方法是开启了事务的),该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行。
(总是非事务地执行,并挂起任何存在的事务)
NEVER
指定业务方法绝对不能在事务范围内执行。如果业务方法在某个事务中执行,容器会抛出异常,只有业务方法没有关联到任何事务,才能正常执行。比较强硬的方式,就是不支持事务。
(总是非事务地执行,如果存在一个活动事务,则抛出异常)
NESTED
(嵌套事务)如果一个活动的事务存在,则当前方法运行在一个嵌套的事务中。 如果没有活动事务,就创建一个新的事务。它使用了一个单独的事务,这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。外部事务回滚会导致内部事务的回滚。如果被调用的内部方法没有捕获异常,跑出异常也会导致外部事务的回滚。
(如果一个活动的事务存在,则运行在一个嵌套的事务中。 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行
Spring事务的隔离级别)
(如果一个活动的事务存在,就在里面创建一个子事务,先执行子事务提交再执行父事务提交)