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