大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺
文章目录
一、java事务概述
1.1、java事务简述
1、简介
事务(TRANSACTION)是作为单个逻辑工作单元执行的一系列SQL操作,这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行。如果任何一个SQL操作失败,那么整个操作就都失败,所有操作都会回滚到操作前状态,或者是上一个节点。
2、java事务和数据库事务的关联
实际上,一个Java应用系统,如果要操作数据库,则通过JDBC来实现的。增加、修改、删除都是通过相应方法间接来实现的,事务的控制也相应转移到Java程序代码中。因此,数据库操作的事务习惯上就称为Java事务。
1.2、Java事务的类型
1、Java事务的类型有三种:JDBC事务、JTA(Java Transaction API)事务、容器事务。
(1)JDBC事务
JDBC 事务是用 Connection 对象控制的。JDBC Connection 接口( java.sql.Connection )提供了两种事务模式:自动提交和手动提交。
使用 JDBC 事务界定时,可以将多个 SQL 语句结合到一个事务中。
特点:事务的范围局限于一个数据库连接。一个 JDBC 事务不能跨越多个数据库。
(2)JTA(Java Transaction API)事务允许程序执行分布式事务处理,使用此事务,需要一个实现javax.sql.XADataSource、javax.sql.XAConnection和javax.sql.XAResource接口的JDBC驱动程序,一个XADataSource对象就是一个XAConnection对象的工厂,XAConnections是参与JTA事务的JDBC连接,对于XA的连接不能调用java.sql.Connection.commit()或者java.sql.Connection.rollback()
而是应该调用UserTransaction.begin()、UserTransaction.commit()和
UserTransaction.rollback()。
特点:可以跨越多个数据库,功能强大,使用复杂
(3)容器事务
容器事务主要是J2EE应用服务器提供的,容器事务大多是基于JTA完成,这是一个基于JNDI的,相当复杂的API实现。
2、三种Java事务差异
(1)JDBC事务控制的局限性是在一个数据库连接内,但其使用简单。
(2)JTA事务的功能强大,事务可以跨越多个数据库或多个DAO,使用也比较复杂。
(3)容器事务,主要指的是J2EE应用服务器提供的事务管理,局限于EJB应用使用。
3、总结
Java事务控制是构建应用不可缺少的一部分,合理选择使用何种事务对整个应用系统来说至关重要。一般说来,在单个JDBC连接的情况下可以选择JDBC事务,在跨多个连接或者数据库情况下,需要选择使用JTA事务,如果用到了EJB,则可以考虑使用EJB容器事务
1.3、java事务的特性
事务必须服从ISO/IEC所制定的ACID原则。ACID是原子性(atomicity)、一致性(consistency)、隔离性 (isolation)和持久性(durability)的缩写。
1、原子性(Atomicity):
事务是一个完整的操作。事务的各步操作是不可分的(原子的);要么都执行,要么都不执行。
2、一致性(Consistency):
当事务执行失败时,所有被该事务影响的数据都应该恢复到事务执行前的状态。
3、隔离性(Isolation):
对数据进行修改的所有并发事务是彼此隔离的,这表明事务必须是独立的,它不应以任何方式依赖 于或影响其他事务。
4、持久性(Durability):
事务完成后,它对数据库的修改被永久保持,事务日志能够保持事务的持久性。
通俗的理解,事务是一组原子操作单元,从数据库角度说,就是一组SQL指令,要么全部执行成功,若因为某个原因其中一条指令执行有错误,则撤销先前执行过的所有指令。更简答的说就是:要么全部执行成功,要么撤销不执行。
1.4、java事务的隔离级别
1、MySQL数据库为我们提供的四种隔离级别:
(1)Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
(2)Repeatable read (可重复读):可避免脏读、不可重复读的发生。
(3)Read committed (读已提交):可避免脏读的发生。
(4)Read uncommitted (读未提交):最低级别,任何情况都无法保证。
以上四种隔离级别最高的是Serializable级别,最低的是Read uncommitted级别,级别越高,执行效率就越低。像Serializable这样的级别,就是以锁表的方式(类似于Java多线程中的锁)使得其他的线程只能在锁外等待,所以平时选用何种隔离级别应该根据实际情况。在MySQL数据库中默认的隔离级别为Repeatable read (可重复读)。
在MySQL数据库中,支持上面四种隔离级别,默认的为Repeatable read (可重复读);而在Oracle数据库中,只支持Serializable (串行化)级别和Read committed (读已提交)这两种级别,其中默认的为Read committed级别。
2、脏读、幻读和不可重复读——简述
(1)脏读:一个事务读取到了另外一个事务没有提交的数据;
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
(2)不可重复读:在同一事务中,两次读取同一数据,得到内容不同;
是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据,并且进行了修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。(即不能读到相同的数据内容)
(3)幻读:同一事务中,用同样的操作读取两次,得到的记录数不相同;
是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
3、脏读、幻读和不可重复读——解决方案
(1)脏读:修改时加排他锁,直到事务提交后才释放,读取时加共享锁,读取完释放事务1读取数据时加上共享锁后(这 样在事务1读取数据的过程中,其他事务就不会修改该数据),不允许任何事物操作该数据,只能读取,之后1如果有更新操作,那么会转换为排他锁,其他事务更 无权参与进来读写,这样就防止了脏读问题。
但是当事务1读取数据过程中,有可能其他事务也读取了该数据,读取完毕后共享锁释放,此时事务1修改数据,修改 完毕提交事务,其他事务再次读取数据时候发现数据不一致,就会出现不可重复读问题,所以这样不能够避免不可重复读问题。
(2)不可重复读:读取数据时加共享锁,写数据时加排他锁,都是事务提交才释放锁。读取时候不允许其他事物修改该数据,不管数据在事务过程中读取多少次,数据都是一致的,避免了不可重复读问题
(3)幻读问题:采用的是范围锁RangeS RangeS_S模式,锁定检索范围为只读,这样就避免了幻读问题。
4、在MySQL数据库中查看当前事务的隔离级别:
select @@tx_isolation;
在MySQL数据库中设置事务的隔离 级别:
set [glogal | session] transaction isolation level 隔离级别名称;
set tx_isolation=’隔离级别名称;’
例1:查看当前事务的隔离级别:
例2:将事务的隔离级别设置为Read uncommitted级别:
或:
1.5、spring事务的传播特性
1、spring事务的传播特性——简述
spring事务传播特性共分为7种,默认的是REQUIRED
REQUIRED (propagation_required)--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
SUPPORTS(propagation_supports)--支持当前事务,如果当前没有事务,就以非事务方式执行。
REQUIRES_NEW(propagation_requires_new)--新建事务,如果当前存在事务,把当前事务挂起。
MANDATORY(propagation_mandatory)--支持当前事务,如果当前没有事务,就抛出异常。
NOT_SUPPORTED(propagation_not_support)--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
NEVER(propagation_never)--以非事务方式执行,如果当前存在事务,则抛出异常。
NESTED(propagation_nested)--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与REQUIRED类似的操作。
2、spring事务的传播特性——详解
(1)【REQUIRED】
service1:事务传播机制:REQUIRED
service2:事务传播机制:REQUIRED
REQUIRED:支持事务,没有事务时创建新的事务,如果存在事务则直接加入该事务执行
method1执行时没有事务,则会新建事务Tx1,在method1中调用method2时,发现已经存在事务,则会加入事务执行,最后执行commit
(2)【SUPPORTS】
如果其他bean调用这个方法时,其他bean声明了事务,则就用这个事务,如果没有声明事务,那就不用事务
service1:事务传播机制:REQUIRED
service2:事务传播机制:SUPPORTS
method1执行时没有事务,则会新建事务Tx1,method2执行时检查到存在事务,则在事务中进行执行,然后进行commit
service1:事务传播机制:NOT_SUPPORTED
service2:事务传播机制:SUPPORTS
method1执行不会创建事务,method2也不会创建事务
SUPPORTS类型的事务传播机制,是否使用事务取决于调用方法是否有事务,如果有则直接用,如果没有则不使用事务
(3)【MANDATORY】
必须在一个已有的事务中执行,否则报错
service1:事务传播机制:NOT_SUPPORTED
service2:事务传播机制:MANDATORY
method1不会创建事务执行,method2执行时检查当前没有事务,则会抛出异常不会执行,method1已经在数据库层面执行成功并完成commit,所以抛出的异常不影响method1的执行结果
MANDATORY必须在已有事务下被调用,否则报错
(4)【REQUIRES_NEW】
REQUIRES_NEW:不管是否存在事务,都创建一个新的事务,原来的方法挂起,新的方法执行完毕后,继续执行老的事务
service1:事务传播机制:REQUIRED,
service2:事务传播机制:REQUIRES_NEW
执行method1时会创建新的事务Tx1,执行method2时会先将Tx1挂起,然后创建新的事务Tx2,执行完后Tx2进行commit,然后将Tx1解除挂起,Tx1进行commit
REQUIRES_NEW为当前方法创建一个新的事务,并且当前事务先提交,然后再提交老的事务
新的事务提交成功之后,老的事务提交失败时新的事务的sql不会回滚
新的事务提交失败回滚,老的事务是否会进行回滚?应该会的
(5)【NOT_SUPPORTED】
service1:事务传播机制:NOT_SUPPORTED
service2:事务传播机制:REQUIRED
NOT_SUPPORTED:以非事务方式执行,如果当前存在事务则将当前事务挂起
method1执行不会创建事务,执行method2时会创建新的事务,执行sql之后会进行commit
service1:事务传播机制:NOT_SUPPORTED
service2:事务传播机制:NOT_SUPPORTED
两个方法都不会创建事务执行
NOT_SUPPORTED相当于没有Spring事务,每条执行语句单独执行,单独提交
(6)【NEVER】
须在一个没有事务中执行,否则报错
service1:事务传播机制:REQUIRED
service2:事务传播机制:NEVER
method1执行时没有事务,则会新建事务Tx1,method2执行时检查到存在事务,则会进行报错抛出异常,method1由于还没有提交事务,则会进行回滚
(7)【NESTED】
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与REQUIRED类似的操作
service1:事务传播机制:REQUIRED
service2:事务传播机制:NESTED
method1执行时没有事务,则会新建事务Tx1,执行sql后不会提交,method2创建一个NESTED嵌套事务执行sql,然后一起提交
method1方法创建一个事务,则再调用method2方法时,直接在该事务的基础上创建一个嵌套事务,本质上还是同一个事务,做一次提交;
service1:事务传播机制:NOT_SUPPORTED
service2:事务传播机制:NESTED
method1执行不会创建事务,sql执行在数据库commit,method2会创建一个事务执行sql后commit
method1方法不创建事务,则调用method2方法时,直接创建一个新的事务,单独提交
用法:
@Transactional(propagation = Propagation.REQUIRED)
public void transcationTest(){
.....
}
用注解的方式添加事务时,类内部调用带有注解的方法,事务不生效。注解的事务使用aop切入上的。
可以注入当前类,调用类下的方法。
1.6、spring支持的事务管理类型
1、事务管理类型
编程式事务管理:可以通过编程的方式控制事务的开启、提交和回滚。(灵活性高,很难维护)——jdbc事务
声明式事务管理:可以将业务代码和事务管理分离,只需用注扇子和XML配置来管理事务。——JTA事务
2、声明式事务管理
(1)基于接口
a、基于TransactionInterceptor的声明式事:
Spring 声明式事务的基础,通常也不建议使用这种方式,但是与 aop 一样,了解这种方式对理解 Spring 声明式事务有很大作用。
b、基于 TransactionProxyFactoryBean 的声明式事务:
第一种方式的改进版本,简化的配置文件的书写,这是 Spring 早期推荐的声明式事务管理方式。
(2)、基于< tx >和< aop >命名空间的声明式事务管理:
目前推荐的方式,其最大特点是与 Spring AOP 结合紧密,可以充分利用切点表达式的强大支持,使得管理事务更加灵活
(3)、基于@ Transactional 的全注解方式:
将声明式事务管理简化到了极致。开发人员只需在配置文件中加上一行启用相关后处理 Bean 的配置,然后在需要实施事务管理的方法或者类上使用@ Transactional 指定事务规则即可实现事务管理,而且功能也不必其他方式逊色。
二、java事物使用
2.1、XML配置
1、配置事务管理器
<!--事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
2、配置事务开启方式
事务有三种开启方式:
(1) 代理工厂
<!--第一种事务开启方式代理工厂-->
<bean id="txService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager"></property>
<property name="target" ref="stockpPayService"></property>
<property name="transactionAttributes">
<props>
<prop key="buyStock">ISOLATION_DEFAULT,PROPAGATION_REQUIRED,-StockException</prop>
</props>
</property>
</bean>
(2)注解
<!--第二种事务开启方式,注解版-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
//后台方法代码上加此注解
@Transactional(rollbackFor = StockException.class)
(3)aspectj xml
<!--第三种方式 aspectj xml版-->
<tx:advice id="txadvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="buyStock" isolation="DEFAULT" propagation="REQUIRED" rollback-for="StockException"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="mypointccut" expression="execution(* *..day22tx.service.*.*(..))"></aop:pointcut>
<aop:advisor advice-ref="txadvice" pointcut-ref="mypointccut"></aop:advisor>
</aop:config>
2.2、事务使用方式
1、注解方式
股票交易的实现类StockPayServiceImpl
package cn.dawn.day22tx.service;
import cn.dawn.day22tx.dao.IAccountDAO;
import cn.dawn.day22tx.dao.IStockDAO;
import cn.dawn.day22tx.entity.StockException;
import org.springframework.transaction.annotation.Transactional;
/** * Created by Dawn on 2018/3/15. */
public class StockPayServiceImpl implements IStockPayService {
IStockDAO iStockDAO;
IAccountDAO iAccountDAO;
//注解使用事物
@Transactional(rollbackFor = StockException.class)
public boolean buyStock(int aid, int ablance, int sid, int scount) throws StockException {
boolean isBuy=true;
boolean sflag = iStockDAO.updateStock(sid, scount, isBuy);
if(true){
throw new StockException("网络被挂掉异常");
}
boolean aflag = iAccountDAO.updateAccount(aid, ablance, isBuy);
if(sflag&&aflag)
return true;
else
return false;
}
public IStockDAO getiStockDAO() {
return iStockDAO;
}
public void setiStockDAO(IStockDAO iStockDAO) {
this.iStockDAO = iStockDAO;
}
public IAccountDAO getiAccountDAO() {
return iAccountDAO;
}
public void setiAccountDAO(IAccountDAO iAccountDAO) {
this.iAccountDAO = iAccountDAO;
}
}
@Transactional里面只写了遇到什么异常会回滚,它的传播行为和隔离级别有默认值,就省略了
StockException类
package cn.dawn.day22tx.entity;
/** * Created by Dawn on 2018/3/15. */
public class StockException extends ClassNotFoundException {
public StockException() {
}
public StockException(String s) {
super(s);
}
}
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/183815.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...