jdbc管理事务以及事务隔离级别的概念

一.什么叫事务
1.将一组更新数据库内容的sql语句放在一起执行
         --在mysql当中,默认是自动提交的,所以必须手动开启事务,通过Start transaction开启事务,然后必须执行commit才能提交.
         在jdbc当中默认连接是自动提交事务的.贴上我们jdbc操作数据一些代码先.主要的代码,以及下面是工具类,其他的资源就不贴了.

package com.imooc.test;

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

import com.imooc.utils.JdbcUtils;
/**
 * 事务的演示
 * @author pic
 */
public class TestTransaction {

	public static void main(String[] args) throws SQLException {
		Connection conn=null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		//回滚点
		Savepoint sp =null;
		try {
			conn=JdbcUtils.getConnection();
			//在开启事务前设置连接的隔离级别,假设我们设置的是SERIALIZABLE(串行化),我们可以在提交前
			//让线程休眠那么一段时间,然后去数据库插入数据
			conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
			//进行转账的模拟,aaa--100->bbb
			//1.开启事务
			conn.setAutoCommit(false);//相当于在数据库里面执行 Start transaction
			String sql1 = "update account set money=money-100 where name='aaa'";
			String sql2 = "update account set money=money+100 where name='bbb'";
			ps=conn.prepareStatement(sql1);
			ps.executeUpdate();
			//设置回滚点的位置
			//sp = conn.setSavepoint();
			ps=conn.prepareStatement(sql2);
			ps.executeUpdate();
			//模拟转中中途出现异常的情况
			//int n =5/0;
			Thread.sleep(1000*40);
			conn.commit();
		} catch (Exception e) {
			e.printStackTrace();
			//如果进行回滚必须也要在下面跟上commit,不然数据库还是不会发生改变
			/*conn.rollback(sp);
			conn.commit();*/
		} finally{
			JdbcUtils.release(conn, ps, rs);
		}
	}

}


=================================================================================

package com.imooc.utils;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class JdbcUtils {
	private static Properties prop = new Properties();
	private static String DRIVER;
	private static String URL;
	private static String USERNAME;
	private static String PASSWORD;
	static{
		InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
		try {
			prop.load(in);
			DRIVER = prop.getProperty("driver");
			URL = prop.getProperty("url");
			USERNAME = prop.getProperty("username");
			PASSWORD = prop.getProperty("password");
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	//1.获取连接
	public static Connection getConnection() throws ClassNotFoundException, SQLException{
		Class.forName(DRIVER);
		Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
		return conn;
	}
	//2.关闭连接等一些资源
	public static void release(Connection conn,Statement st,ResultSet rs) {
		if(rs!=null){
			try {
				rs.close();
			} catch (SQLException e) {
				if(rs!=null){
					rs=null;
				}
				e.printStackTrace();
			}
		}
		if(st!=null){
			try {
				st.close();
			} catch (SQLException e) {
				if(st!=null){
					st=null;
				}
				e.printStackTrace();
			}
		}
		if(conn!=null){
			try {
				conn.close();
			} catch (SQLException e) {
				if(conn!=null){
					conn=null;
				}
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) throws ClassNotFoundException, SQLException {
		Connection conn = JdbcUtils.getConnection();
		System.out.println(conn);
	}
	
}


2.事务的四大特性:
1).原子性:原子性是指事务是一组不可分割的工作单位,事务中的操作要么全都提交,要么全都不提交.
2).一致性:事务提交前后数据的应该是一致完整的,例:转账时两人总金额为2000,转完账后两人的总金额也是2000.
3).隔离性:多个用户并发访问数据库时,一个用户的事务不能被其他的用户的事务干扰,多个并发事务之间要相互隔离.
4).持久性:事务提交成功后,对数据库的改变是永久的,无论接下来数据库有没有发生故障.
注:隔离性的级别可能会出现的问题:
(1).脏读:指一个事务读取到了其他事务未提交的事务 例:商家开启事务正在查询了一下账户发现账户余额为1000,此时一个买家开启一个事务向商家转账100,但是并没有提交
这时候商家可能再次查询余额发现变为1100了,这就是脏读,发货后买家回滚事务,商家就只有1000了.
(2).不可重复读:第一次读取某一行数据时得到了一条记录,但是这时候另一个事务将这条数据进行修改或者删除并别提交了,然后前面那个事务再次读取的时候记录就发生了改变,
这种不可重复读的现象在有些情况下不是错误,但是在一些特定场合下就会是错误了.例如:央行需要做报表对我国的存款情况进行统计,而且这个报表的话不是只读一次就行了,而是不断的进行读取,但是此时还是会有有人在进行存款,所以每次读取都可能会不一样,这种情况下做出的报表然结果也就不一样啦--这就是问题.
(3).虚读(幻读):一个事务插入前后,另一个事务进行读取操作,这时候就会出现前后读取的数据不一致.当然这在一般情况下不算是问题,但是在特定场合下就会存在问题啦.
例如:人口普查需要统计我国人口数,假设我们就是select count(1) from XXX;你说每天都有人在出生,那每一天人口不一样,不止每一天每一分钟都不一样,这时候该怎么统计个
准确的数字呢,出生一个人相当于在我们人口表中插入一条数据,插入数据前后读取的条数也就不相同,此时就是问题啦.

数据库定义的隔离级别:
(1).Serilizable:可避免脏读、不可重复读、虚读(幻读)情况的发生。(但是其效率是最差的,也就是在读这个表的时候事务之间是同步的,称之为串行化).
(2).Repeatable read:可避免脏读和不可重复读的情况.但是不能避免虚读(幻读,虽然在有这个隔离级别下大部分情况下看不到虚读的情况,但是其还是存在的,所以不容忽视,称之为可重复读).
(3).Read committed:可以避免脏读,当然以上级别的问题都无法避免了.(称之为读已提交).
(4).Read Uncommitted:以上级别的问题全部无法避免.(称之为读未提交).

--mysql数据是严格按照数据库的隔离级别的规范设计的,有以上四种隔离级别,且默认的隔离级别是:REPEATABLE-READ
--而oracle只支持Serilizable和Read committed两种事务级别,且oracle默认的隔离级别为Read committed;

set transaction isolation level 设置事务隔离级别
select @@tx_isolation 查询当前事务隔离级别
最后还是在前面的那段主代码中测试了下我们jdbc设置隔离级别的操作,由于lazy所以这里我只测试了Serilizable啦,因为这个设置了看图比较直观,上述代码就是最终演示这个而做的些注释. 

猜你喜欢

转载自644259206.iteye.com/blog/2282999