6.jdbc事物
什么是事物:
事物就是逻辑上的一组操作,要么成功,要么失败!(比如转账 A 给 B 转账,A转出去了,B没收到,那么就不应该算成功,而是应该全部失败.)
事物的特性:
-
-
- 原子性:意思是不可以分割
-
-
-
- 一致性:意思就是事物从一个状态到另一个状态,应该是一致的,比如说转账(A有1000元,B有1000元,他们一共2000元;现在A转给B 500元,那么转账之后A 有500元,B 有1500元[中间不考虑手续费什么的..],这样他们还是 2000元,就是前后一致的。)
- 隔离性:意思一个事物一旦开启,就应该不受其他事物的影响(还是用转账来举例: A去ATM上查有1000元,在查的同时,B给A转了 500元,但是现在A 还没有退出当前事物,因此A 就不应该知道B 给他转了500元。)
- 永久性: 意思就是一旦事物提交,就应该永久的存起来
-
如何开始事物?
一般来说,事物都是默认开启的,我们要自己来控制事物,一般涉及到如下步骤
-
-
- 设置不默认开启事物
- 提交事物
- 事物错误,回滚
-
来看看代码
@Test
public
void
test_trans
(){
try
{
Class
.
forName
(
"com.mysql.jdbc.Driver"
);
Connection
connection
=
DriverManager
.
getConnection
(
url
,
user
,
password
);
connection
.
setAutoCommit
( false);
//设置不默认开启事物
PreparedStatement
ps
=
connection
.
prepareStatement
(
"update test set name='000' where id=1"
);
ps
.
executeUpdate
();
int
i
=
1
/
0
;
//错误
ps
=
connection
.
prepareStatement
(
"update test set name='222' where id=2"
);
ps
.
executeUpdate
();
connection
.
commit
();
//事物提交
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
try
{
connection
.
rollback
();
//事物回滚
}
catch
(
SQLException
e1
)
{
e1
.
printStackTrace
();
}
}finally {
//关闭资源
}
}
|
如果步骤比较多,然后不确定有一个步骤是否会有错,这时候可以设置回滚点
看下代码
@Test
public
void
test_trans1
(){
Savepoint
sp
=
null;
try
{
Class
.
forName
(
"com.mysql.jdbc.Driver"
);
Connection
connection
=
DriverManager
.
getConnection
(
url
,
user
,
password
);
connection
.
setAutoCommit
( false);
//设置不默认开启事物
PreparedStatement
ps
=
connection
.
prepareStatement
(
"update test set name='000' where id=1"
);
ps
.
executeUpdate
();
sp
=
connection
.
setSavepoint
();
//设置回滚点
int
i
=
1
/
0
;
//错误
ps
=
connection
.
prepareStatement
(
"update test set name='222' where id=2"
);
ps
.
executeUpdate
();
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
try
{
connection
.
rollback
(
sp
);
//事物回滚
}
catch
(
SQLException
e1
)
{
e1
.
printStackTrace
();
}
}finally {
try
{
connection
.
commit
();
}
catch
(
SQLException
e
)
{
e
.
printStackTrace
();
}
//事物提交
//关闭资源
}
}
|
我自己的建议是能不用回滚点就不用它。
事物的隔离级别
TRANSACTION_READ_UNCOMMITTED 指示可以发生脏读 (dirty read)、不可重复读和虚读 (phantom read) 的常量。 |
TRANSACTION_READ_COMMITTED 指示不可以发生脏读的常量;不可重复读和虚读可以发生。 |
TRANSACTION_REPEATABLE_READ 指示不可以发生脏读和不可重复读的常量;虚读可以发生。 |
TRANSACTION_SERIALIZABLE 指示不可以发生脏读、不可重复读和虚读的常量。 |
java.sql.Connection | ||
---|---|---|
public static final int | TRANSACTION_READ_COMMITTED | 2 |
public static final int | TRANSACTION_READ_UNCOMMITTED | 1 |
public static final int | TRANSACTION_REPEATABLE_READ | 4 |
public static final int | TRANSACTION_SERIALIZABLE | 8 |
这几个隔离级别的脏读,不可重复读,幻读(虚读)到底是什么意思?
在事务管理中违反ACID特性的3个问题:脏读取、不可重复读和幻影行。如果存在多个并发的事务在运行,而这些事务操作了同一个数据来完成他们的任务,就会导致3个问题的存在。要解决它们,就必须在事务之间定义合适的隔离级别。事务之间存在的3个问题是:
脏读取 (Dirty read)
当一个事务读取了另一个事务尚未提交的更新,就叫脏读取。在另一个事务回滚的情况下,当前事务所读取的另一个事务的数据就是无效的。
不可重复读取(Nonrepeatable read)
在一个事务中执行多次同样的查询操作,但每次查询的结果都不一样,就叫做不可重复读取,通常这种情况是由于数据在二次查询之间被另一个并发的事务所修改。
幻读(Phantom rows)
这是对事务危害最小的一个问候,它类似不可重复读取,也是一个事务的更新结果影响到另一个事务问题。但是它不仅影响另一个事务查询结果,而且还会使查询语句返回一些不同的行录行。
这3个问题危害程度依次为:脏读取最大、不可重复读取其次、幻读最小。
如何查询数据库的默认事物级别:
mysql>
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)
默认的是:
指示不可以发生脏读和不可重复读的常量;虚读可以发生
一般这个级别基本上能够满足我们的需求了
下面我们来试试测试下:
先修改数据库的事物级别为最低的;
修改后的结果:
mysql> select @@tx_isolation;
+------------------+
| @@tx_isolation |
+------------------+
| READ-UNCOMMITTED |
+------------------+
1 row in set (0.00 sec)
+------------------+
| @@tx_isolation |
+------------------+
| READ-UNCOMMITTED |
+------------------+
1 row in set (0.00 sec)
先看下数据库
同时两个事物再跑:
先测试脏读
这个时候一个事物(A)在查询,开始查询出来money=1000,然后另一个事物(B)修改价格,这时候事物(A)再次查询,就变成了money=1500,读取到了B还没有提交的数据叫脏读。
同时也出现了另一个错误,叫不可重复读,事物A前后读取的数据不一样。
还有一个幻读,这个不好测试,就在这里不测了