JDBC-Transaction(事务处理)

数据库事务



在数据库中,所谓事务是指作为单个逻辑工作单元执行的一系列操作
为确保数据库中数据的一致性,所有的操作要么都成功,要么都失败。当这个单元中的一部分操作失败,整个事务应全部视为错误,所有从起始点以后的操作应全部回退到开始状态。
事务的操作:先定义开始一个事务,然后对数据作修改操作,这时如果提交(COMMIT),这些修改就永久地保存下来,如果回退(ROLLBACK),数据库管理系统将放弃您所作的所有修改而回到开始事务时的状态。

l事务的ACID属性

1. 原子性(Atomicity
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 
2. 一致性(Consistency
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。(数据不被破坏)
3. 隔离性(Isolation
(事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
4. 持久性(Durability
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响


事务处理:保证所有事务都作为一个工作单元来执行,当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),要么整个事务回滚(rollback)到最初状态
在JDBC中,事务默认是自动提交的,每次执行一个SQL 语句时,如果执行成功,就会向数据库自动提交,而不能回滚
实现事务的步子步骤:
调用Connection 对象的setAutoCommit(false); 以取消自动提交事务
在所有的SQL 语句都成功执行后,调用commit(); 方法提交事务
在出现异常时,调用rollback(); 方法回滚事务
可以通过Connection的getAutoCommit()方法来获得当前事务的提交方式
******************************************
l   1  查看提交模式

         select @@autocommit;

   0:表示手动提交
    1   自动提交

l 1 begin,rollback,commit 来实现
  begin              //开始一个事务

          rollback          //事务回滚

          commit          //提交事务

          例如:

          mysql> begin;

          mysql> insert into testvalues(9,'dd','dd');

          mysql> commit;

l 2 直接用 set 来改变 mysql 的自动提交模式

        MYSQL默认是自动提交的,也就是你提交一个QUERY,它就直接执行
  set autocommit =0    禁止自动提交
  set autocommit =1   开启自动提交,   来实现事务的处理。
  * 当你用 set autocommit =0 的时候,你以后所有的SQL都将做为事务处理,直

            到你用commit确认或rollback结束。

       *  注意:当你结束这个事务的同时也开启了个新的事务

     


package cn.itcast.mysql.transaction;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import org.junit.Test;

import cn.itcast.mysql.util.JDBCUtils;

/**
 * #账户基本信息表 create table account ( accountid varchar(18) , #账号 balance
 * double(10,2) #余额 ) #存款表 create table inaccount ( accountid varchar(18) , #账号
 * inbalance double(10,2) #存入余额 ) #办卡完成,初始化数据 INSERT INTO
 * account(accountid,balance) VALUES('123456789',0)
 * 
 * #向账户中存入100元:
 * INSERT INTO inaccount(accountid,inbalance) VALUES('123456789',100) UPDATE
 * account SET balance=balance+100 WHERE accountid='123456789'
 * 
 * @author Administrator
 * 
 */
public class TestTransaction {

	@Test
	public void testSaveMoney() {
		Connection conn = null;
		PreparedStatement pstmt = null;

		// 准备数据
		String accountId = "123456789";
		double money = 100;

		try {
			conn = JDBCUtils.getConnection();
			
/*****************************************************/			
			//设置事务为手动提交
			System.out.println("默认事务为"+conn.getAutoCommit());//true代表自动提交,false手动提交
			conn.setAutoCommit(false);
			System.out.println("事务为"+conn.getAutoCommit());
/*****************************************************/		
			
			// 向inaccount表中插入一条记录
			// sql: INSERT INTO inaccount(accountid,inbalance)
			// VALUES('123456789',100)
			String sql = "INSERT INTO inaccount(accountid,inbalance) VALUES(?,?)";
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, accountId);
			pstmt.setDouble(2, money);
			pstmt.executeUpdate();//数据马上同步到数据库

			// 更新account表,将指定账号的余额加上100
			// UPDATE account SET balance=balance+100 WHERE
			// accountid='123456789'
			sql = "UPDATE account SET balance=balance+? WHERE accountid=?";
			pstmt = conn.prepareStatement(sql);
			pstmt.setDouble(1, money);
			pstmt.setString(2, accountId);
			
			//模拟因网络或不明原因出异常
			boolean flag = true;
			if(flag) {
				throw new SQLException("因网络或不明原因出异常!");
			}
			pstmt.executeUpdate();
			
/*****************************************************/			
			//提交事务
			conn.commit();
/*****************************************************/	
			
		} catch (SQLException e) {
			
/*****************************************************/			
			//回滚事务
			try {
				if(conn!=null) {
					conn.rollback();
				}
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
/*****************************************************/
			
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(conn, pstmt, null);
		}
	}
}

多个事务并发运行时的并发问题

l事务隔离级别(transactionisolation levels):
隔离级别就是对事务并发控制的四个等级。分为

 串行化(SERIALIZABLE)

 2  可重复读(REPEATABLE READ)

 3  读已提交(READ COMMITED)

 4  读未提交(READ UNCOMMITED)    


常用数据库默认的隔离级别


1、mysql默认的隔离级别为Repeatable_Read
2、sqlserver 默认的隔离级别为Read Commited
3、oracle数据库支持READ COMMITTED和SERIALIZABLE

两种事务隔离性级别,不支持READ UNCOMMITTED和

REPEATABLE READ这两种隔离性级别,

Oracle数据库默认使用的事务隔离性级别却是READ COMMITTED.

隔离级别 分为四种 :
 READ UNCOMMITTED

                       READ COMMITTED

   REPEATABLE READ 
                            SERIALIZABLE

查询当前事务的隔离级别

          select@@tx_isolation

设置当前当前连接的隔离级别
SET TRANSACTION ISOLATION LEVEL   隔离级别
设置数据库系统的全局的隔离级别
 SET  global  TRANSACTION ISOLATION LEVEL   隔离级别

对于同时运行的多个事务,当这些事务访问数据库相同的数据时,如果没有采取必要的隔离机制,就会产生并发问题.
脏读:一个事务读取了另一个事务改写但还未提交的数据,如果这些数据被回滚,则读到的数据是无效的。
不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已提交的更新数据。
可重复读:在同一事务中多次读取数据时,能够保证所读数据一样,也就是,后续读取不能读到另一事务已提交的更新数据。
幻读:一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。再后来的查询中,第一个事务就会发现有些原来没有的记录。


数据库并发操作存在的异常情况

脏读

设置数据库的隔离级别:
set TRANSACTION ISOLATION LEVEL  READ UNCOMMITTED

业务情景:
张三准备用ATM机向其北京招行的帐户汇1000块钱,在汇之前先用其智能手机的手机银行客户端查询到其北京账户还剩100,接着他用ATM机向其北京帐户打过去1000,在提示确认提交时,他又用手机查看了一下其帐户余额竟然为1100。

不可重复读

设置数据库的隔离级别: 
set TRANSACTION ISOLATION LEVEL  READ COMMITTED ;

业务情景:
张三用其智能手机的手机银行客户端查询其招行帐户余额为100元,他没有马上退出,去做了一会别的事,在此过程中他老婆在一个ATM机上向其帐户上汇入了1000元,他回来后又刷新了一下余额,竟然为1100元。

幻读(Magic read)

设置数据库的隔离级别:
set TRANSACTION ISOLATION LEVEL  REPEATABLE READ;

业务情景:
张三用其智能手机的手机银行客户端查询其招行帐户余额为300元,他准备用其中的200去抢购一个商品,在他没付款之前他的老婆取走100元,他再次查看了一下余额,还是300元,他花了200支付了商品,在支付完后,他再次查了一下余额,竟然发现账户里一毛钱都没有啦!

不能并发

设置数据库的隔离级别:
	set TRANSACTION ISOLATION LEVEL  SERIALIZABLE;
业务情景:
张三用其智能手机的手机银行客户端查询其招行帐户余额为99.9元,但他需要100元才能支付抢购,他QQ告知老婆在线等待,他老婆立马找到一个ATM机准备向其帐户上汇入了100元,但钱就是汇不进去。

















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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值