事务(transaction)的基本认识
事务的简介
什么是事务?事务其实就是将一件事情中的n
个组成单元组合到一起,这n
个组成单元要么一起成功,要么一起失败。
事务的特性
事务有四个特性:原子性、隔离性、持久性、一致性。
1、原子性:事务是个不可分割的单位,事务中的操作要么都成功,要么都失败
2、一致性:在一个事务中,事务前后数据的完整性必须保持一致
3、隔离性:多个事务之间互不干扰,相互隔离
4、持久性:事务一旦提交,对数据库中数据的改变是永久性的
mysql中的事务
默认的事务:一条SQL语句就是一个事务,默认情况下开启并提交事务
手动事务:
1、显式开启事务start transaction (connnection.setAutoCommit(false));
2、事务提交(commit),从开启事务到提交事务之间所有的SQL语句都是有效的
3、事务的回滚(rollback):从开始事务到回滚事务之间所有的SQL语句都是无效的
使用原生的JDBC进行事务操作
1 | package com.my.jdbc; |
使用DBUtilis进行事务处理
以银行转账为例,一般银行转账需要转入账户的名称、转出的账户名称以及转账的金额,下面我将通过一个简单的例子模拟转账的场景,首先,我创建了一个jsp文件,里面写了一个简单的表单:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/tranfer" method="post">
转入账户:<input type="text" name="in"><br />
转出账户:<input type="text" name="out"><br />
转账金额:<input type="text" name="money"><br />
<input type="submit" value="确认转账"/>
</form>
</body>
</html>
然后新建一个Servlet
1 | package com.my.tranfer.web; |
新建TranferService类处理业务逻辑
1 | package com.my.tranfer.service; |
创建TranferDao类
1 | package com.my.tranfer.dao; |
其中用到的工具类以及xml文件代码如下:
1 | package com.my.utils; |
1 | <?xml version="1.0" encoding="UTF-8"?> |
可以看到,上面的代码关于事务的处理是在service层中,但是有一点不好的地方是在Servlet层中创建了Connection对象,由于事务的处理需要先取得对应的Connection对象,所以为了保证获取到正确的连接对象并且避免在service层中创建Connection对象,可以新建一个工具类,使用TreadLocal
1 | package com.my.utils; |
重新修改service层的代码:
1 | package com.my.tranfer.service; |
修改DAO层的代码:
1 | package com.my.tranfer.dao; |
在进行转账操作的时候会涉及到减去金额(转出账户),增加金额(转入账户),而事务的目的就是要确保转账的整个过程要么成功要么失败,避免出现转出账户金额减少了但转入账户金额却没有增加等一些异常情况的出现。
隔离性在并发访问下引发的问题
- 脏读:A读取到B尚未提交的数据
- 不可重复读:在同一个事务中,两次读取的数据内容不一致
- 虚读/幻读:在一个事务中,两次读取的数据条数不一致
事务的隔离级别
- read uncommitted:读取尚未提交的数据—-无法解决脏读、不可重复读、虚读/幻读等问题
- read committed:读取已经提交的数据—-Oracle默认的隔离级别,可以解决脏读问题
- repeatable read:重复读取—-mysql默认的隔离级别,可以解决脏读以及不可重复读的问题
- serializable:串行化,安全级别最高—可以解决脏读、不可重复读、虚读/幻读问题
事务的隔离级别并不是越高越好,事务的安全级别越高其性能就越低,查看mysql中的事务隔离安全级别的命令:select @@tx_isolation
,设置事务隔离安全级别的命令:set session transaction isolation level 安全级别
。