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啦,因为这个设置了看图比较直观,上述代码就是最终演示这个而做的些注释.