事务
事务是:在数据库指业务处理的”一个业务“对应数据库中的多个步骤的操作。例如银行转账。
面对的问题:程序接受请求后,会至少发送两条SQL语句,两条语句之间会有时间的间隔,如果间隔时间期间Mysql服务器发生意外,导致第二条SQL无法执行,导致整个业务的最终数据错误。所以,所有的数据库软件,都必须具有一种能力,可接收一系列的“SQL”语句,将这些SQL语句看做是一个“整体”执行后,要么全部成功,要么全部失败这种能力就是:事务处理
数据库中的事务操作
- JDBC操作数据库的事务操作
- DBUtils中的数据库的事务操作
数据库中的事务处理:1、手动提交、2、自动提交情况下开启一个临时事务处理
- mysql中默认的事务处理方式,自动提交–将每条SQL语句看做是一个独立的事务,执行后会立即更改数据库,与其他的SQL语句没有关联。不能满足我们对事务的处理需求。
- 怎么查看当前mysql的事务处理方式:show variables like ‘autocommit’;
mysql中的事务处理的两种
- 关闭自动提交:set autocommit =off;(开启是 ON)
- 执行SQL语句
- 提交/回滚:commit;//提交 | rollback;回滚
- 【注意】当提交或者回滚后,之前的SQL语句被全部处理。要么全部更改打数据库,要么全部取消。之后新的SQL语句,就是新的事务。
- 【注意】 事务提交后不能被回滚,回滚之后也不能被提交了。
在“自动提交”状态下,开启临时一个“手动事务”
- 开启临时事务:start tansaction;
- 执行SQL语句
- 提交或回滚临时事务:commit; | rollback;
- 【注意】只要提交事务,这个事务就结束了,立即恢复到之前的自动事务状态
- 【注意】事务对于mysql是“回话级别的”每个回话都可以单独设置事务的处理,会不影响,互相隔离;
- 【注意】在一个“会话中”的查询结果是可以查询到之前修改的结果不论是否提交/回顾;但在另一个回话中,查询时查询不出来的。
代码
JDBC中的事务处理
public class Demo {
public static void main(String[] args)throws Exception {
String url="jdbc:mysql://localhost:3306/db_demo";
String user="root";
String pwd="123456";
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接
Connection con = DriverManager.getConnection(url,user,pwd);
//关闭数据库自动提交
con.setAutoCommit(false);
//获取SQL执行器
Statement stmt = con.createStatement();
try{
String sql1="update account set balance=balance-1000 where userName='zhangsan'";
int row1 = stmt.executeUpdate(sql1);
String sql2="update account set balance=balance+1000 where userName='lisi'";
int row2 = stmt.executeUpdate(sql2);
System.out.println(row1+" ==="+row2);
if(row1!=1||row2!=1){
con.rollback();
System.out.println("回滚了");
}else{
con.commit();
}
}catch(Exception e){
con.rollback();
System.out.println("异常了 回滚了");
e.printStackTrace();
}
con.close();
}
}
DBUtils中的事务处理
public class Demo {
public static void main(String[] args) throws Exception{
//创建QueryRuanner对象是Dbutils工具类里的类 查询、增删改等操作
QueryRunner qr=new QueryRunner();
//创建连接池状态
ComboPooledDataSource ds= new ComboPooledDataSource();
Connection con = ds.getConnection();
con.setAutoCommit(false);
try{
String sql1="update account set balance=balance-1000 where userName='zhangsan'";
String sql2="update account set balance=balance+1000 where userName='lisi'";
int row1 = qr.update(con, sql1);
int row2 = qr.update(con, sql2);
if(row1!=1||row2!=1){
con.rollback();
System.out.println("出错了");
}else{
con.commit();
System.out.println("成功了");
}
}catch(Exception e){
con.rollback();
System.out.println("异常了,回滚了");
}
con.setAutoCommit(true);
con.close();
}
}
软件分层
1).视图层(view):负责接收数据、命令、展示数据;
2).控制层(controller):负责接收视图层的数据、命令,寻找相应的”业务层”进行处理(业务分发)
3).业务层:负责具体的业务逻辑实现
4).持久层(Dao):负责持久化数据的(访问数据库)
5).模型层(model):负责封装数据,在各层之间传递数据。
分层的好处:
1).将代码解耦,使各功能部分的代码之间的耦合度降到最低。不同功能的代码分到不同的类中存储,后期需要修改时,修改哪部分代码,就找哪层的类即可,其它类不用改。
有一句话:高内聚、低耦合
高内聚:功能相同、相近的代码放到同一个类中;
低耦合:各功能的代码之间降低耦合;
事务特性 ACID
- 原子性
- 一致性
- 隔离性
- 持久性
不考虑两个事务的隔离性会产生的问题
- 脏读:一个事务读到了另一个事务未提交的数据.
- 不可重复读:一个事务读到了另一个事务已经提交(update)的数据。引发另一个事务,在事务中的多次查询结果不一致。
- 虚读/幻读:一个事务读到了另一个事务已经提交(insert)的数据。导致另一个事务,在事务中多次查询的结果不一致。
事务的隔离级别
1).read uncommitted:读未提交;最低级;基本上不隔离。可以读到脏数据。
2).read committed:读已提交;可以解决脏读,但不能解决”不可重复读”和”幻读”;
3).repeatable read: 可重复读。在MYSQL下可以解决:脏读、不可重复的、幻读;(MySQL的默认级别)
4).serializable :将两个事务完全隔离,一个事务未结束时,另一个事务必须等待;
查看当前数据库的隔离级别:
select @@tx_isolation;
设置会话的事务隔离级别:
set session transaction isolation level 上面的四个隔离级别之一;
安全和性能对比
安全性:serializable > repeatable read > read committed > read uncommitted
性能 : serializable < repeatable read < read committed < read uncommitted
常见数据库的默认隔离级别:
MySql:repeatable read(第三级)
Oracle:read committed(第二级)
ThreadLocal 类的工作原理
可以保存一个Connection对象,当Dao层需要的时候可以直接获取数据库连接对象。不需要每层在传递Connection对象,到Dao层。