事务解释

1、事务是什么?用处
事务就是一个事情,组成这个事情可能有多个单元,要求这些单元,要么全部成功,要么全部不成功。在开发中,有事务的存在,可以保证数据完整性。
2、事务怎么操作?
(1)MySQL下怎么操作
方式一:
start transaction 开启事务
rollback 事务回滚
commit 事务提交
方式二:
通过show variables like '%commit%';可以查看当前autocommit值
在MySQL数据库中它的默认值是"on"代表自动事务。
自动事务的意义就是:执行任意一条SQL语句都会自动提交事务。

测试:将autocommit的值设置为off
1、set autocommit=off 关闭自动事务
2、必须手动commit才可以将事务提交
注意:MySQL默认autocommit=on oracle默认的autocommit=off

(2)jdbc下的操作:
java.sql.Connection接口中有几个方法是用于可以操作事务
1、setAutoCommit(boolean flag);
如果flag=false;它就相当于start transaction;
2、rollback()—事务回滚
3、commit()----事务提交

3、事务特性 ACID
原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性(Consistency)
事务前后数据的完整性必须保持一致。
隔离性(Isolation)
事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间数据要相互隔离。
持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障都不应该对其有任务影响。

、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
如果不考虑事务的隔离性,会出现什么问题?

1、脏读 一个事务读取到另一个事务的未提交数据
2、不可重复读
两次读取的数据不一致(update)
3、虚读(幻读)
两次读取的数据不一致(insert)
4、丢失更新
两个事务对同一条记录进行操作,后提交的事务,将先提交的事务的修改覆盖了。

对于以上的问题,我们可以通过设置事务的隔离级别来解决。

1、事务的隔离级别有哪些?
(1)Serializable:可避免脏读、不可重复读、虚读情况的发生。(串行化)
(2)Repeatable read:可避免脏读、不可重复读情况的发生。(可重复读)不可以避免虚读
(3)Read commited:可避免脏读情况发生(读已提交)
(4)Read uncommitted:最低级别,以上情况均无法保证。(读未提交)

2、怎么设置事务的隔离级别?
(1)MySQL中设置
a:查看事务隔离级别
select @@tx_isolation 查询当前事务隔离级别
MySQL中默认的事务隔离级别是 Repeatable read
扩展:oracle中默认的事务隔离级别是 read committed
b:MySQL中怎么设置事务隔离级别
set session transaction isolation level 设置事务隔离级别
(2)jdbc中设置
在jdbc中设置事务隔离级别
使用java.sql.Connection 接口中提供的方法
void setTransactionIsolation(int level) throws SQLException
参数level可以取以下值:
level -以下Connection常量之一:

Connection.TRANSACTION_READ_UNCOMMITTED、
						Connection.TRANSACTION_READ_COMMITTED、
						Connection.TRANSACTION_REPEATABLE_READ 
						Connection.TRANSACTION_SERIALIZABLE。

(注意,不能使用Connection.TRANSACTION_NONE,因为它指定了不受支持的事务。)

4、演示
1、脏读
一个事务读取到另一个事务的未提交数据
设置A,B事务隔离级别为 Read uncommitted

set session transaction isolation level  read uncommitted;

(1)在A事务中
start transaction;

update account set money=money-500 where name='aaa';
update account set money=money+500 where name='bbb';

(2)在B事务中

start transaction;
select * from account;

这时,B事务读取时,会发现,钱已经汇完。那么就出现了脏读。

当A事务提交前,执行rollback,在commit,B事务在查询就会发现,钱恢复成原样,也出现了两次查询结果不一致问题,出现了不可重复读。
2、解决脏读问题
将事务的隔离级别设置为 read committed来解决脏读。
设置A,B事务隔离级别为 Read committed
set session transaction isolation level read committed;
(1)在A事务中

start transaction;
update account set money=money-500 where name='aaa';
update account set money=money+500 where name='bbb';

(2)在B事务中

start transaction;
select * from account;

这是B事务中,读取信息时,是不能读取到A事务未提交的数据的,也就解决了脏读。
让A事务,提交数据commit;
这时,在查询,这次结果与上一次查询结果又不一样了,还存在不可重复读。

3、解决不可重复读
将事务的隔离级别设置为Repeatable read来解决不可重复读。
设置A,B事务隔离级别为Repeatable read;

set session transaction isolation level  Repeatable read;

a:在A事务中

start transaction;
update account set money=money-500 where name='aaa';
update account set money=money+500 where name='bbb';

b:在B事务中

start transaction;
select * from account;

当A事务提交后commit;B事务在查询,与上次查询结果一致,解决了不可重复读。
4、设置事务隔离级别 Serializable,它可以解决所有问题

set session transaction isolation level Serializable;

如果设置成这种隔离级别,那么会出现锁表。也就说,一个事务在对表进行操作时,其它事务操作不了。

总结:
事务隔离级别:
read uncommitted;什么问题也解决不了
read committed;可以解决脏读,其它解决不了
Repeatable read;可以解决脏读,可以解决不可重复读,不能解决虚读。
Serializable;他会锁表,可以解决所有问题
安全性:serializable > repeatable read > read committed > read uncommitted
性能:serializable < repeatable read < read committed < read uncommitted

结论:实际开发中,通常不会选择serializable 和read uncommitted。
MySQL默认隔离级别Repeatable read,orale默认隔离级别read committed。

5、案例
转账汇款—使用事务
问题:service调用了dao中两个方法完成了一个业务操作,如果其中一个方法执行失败怎么办?
需要事务控制
问题:怎样进行事务控制?
我们在service层进行事务的开启,回滚以及提交操作。
问题:进行事务操作需要使用connection对象,那么,怎样保证,在service中与dao中所使用的是同一个connection。
在service层创建出connection对象,将这个对象传递到dao层。
注意:connection对象使用完成后,在service层的finally中关闭
而每一个PrepareStatement它们在dao层的方法中用完就关闭。
关于程序问题:
1、对于转入与转出操作。我们需要判断是否成功,如果失败了,可以通过抛出自定义异常在servlet中判断,进行信息展示。
问题:
在设置dao层时,

		public interface AccountDao {
			public void accountOut(String accountOut, double money) throws Exception;

			public void accountIn(String accountIn, double money) throws Exception;

		}
	那么我们自己去实现这个接口时,怎样处理,同一个Connection对象问题?
		使用ThreadLocal
		
		ThreadLocal可以理解成是一个Map集合
		Map<Thread,Object>
		set方法是向ThreadLocal中存储数据,那么当前的key值就是当前线程对象.
		get方法是从ThreadLocal中获取数据,它是根据当前线程对象来获取值。
		
		如果我们是在同一个线程中,只要在任意的一个位置存储了数据,在其它位置上,就可以获取到这个数据。
		
	关于JdbcUtils中使用ThreadLocal
		1.声明一个ThreadLocal
			private static final ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
		2.在getConnection()方法中操作
			Connection con = tl.get(); 直接从ThreadLocal中获取,第一次返回的是null.
			if (con == null) {
				// 2.获取连接
				con = DriverManager.getConnection(URL, USERNAME, PASSWORD);
				tl.set(con); //将con装入到ThreadLocal中。
			}

丢失更新
多个事务对同一条记录进行了操作,后提交的事务将先提交的事务操作覆盖了。
问题:怎样解决丢失更新问题?
解决丢失更新可以采用两种方式:
1、悲观锁
悲观锁(假设丢失更新一定会发生)–使用数据库内部锁机制,管理事务
提交的锁机制
(1)、共享锁
select * from table lock in share mode(读锁、共享锁)
(2)、排它锁
select * from table for update (写锁、排它锁)
update语句默认添加排它锁
2、乐观锁
乐观锁(假设丢失更新不会发生)–采用程序中添加版本字段解决丢失更新问题

create table product (
					   id int,
					   name varchar(20),
					   updatetime timestamp
					);

					insert into product values(1,'冰箱',null);
					update product set name='洗衣机' where id = 1;

解决丢失更新:在数据表添加版本字段,每次修改过记录后,版本字段都会更新,如果读取是版本字段,与修改时版本字段不一致,说明别人进行修改过数据(重改)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值