2019.8.27 NIIT第四十二天 /*问题:? Properties ps = new Properties();
ps.load(this.getClass().getClassLoader().getResourceAsStream("db.properties")); */
事务管理
事务概述
- 事务指的是逻辑上的一组操作,组成这组操作的各个单元要么全都成功,要么全都失败.
- 事务作用:保证在一个事务中多次操作要么全都成功,要么全都失败. 关键字:start transactions
commit判断sql是否都执行成功,如果不成功,把数据刷到数据表中,如果执行失败,不去更新表 rollback将数据回滚到修改前
sql语句在内存中运行,然后通过commit将内存中的数据与数据库中的数据同步,发生sql错误则不同步 测试的时候可以使用cmd进行演示
JDBC事务操作
Connection对象的方法名 描述 conn.setAutoCommit(false) 开启事务 conn.commit() 提交事务 conn.rollback() 回滚事务
事务代码: 如果要进行事务操作,需要把事务操作的代码,放在try-catch 之内
package com.igeek1;
import org.junit.Test;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
public class TesTrancation {
@Test
public void m1(){
Connection conn=null;
Statement st=null;
try {
//获取连接
conn=JDBCTools.getConn();
//开启事务,设置自动提交
conn.setAutoCommit(false);
st=conn.createStatement();
//执行sql语句
String sql="";
sql="update account set money=money-1000 where name='冠希'";
st.executeUpdate(sql);
sql="update account set money=money+1000 where name='美美'";
st.executeUpdate(sql);
//提交数据
conn.commit();
} catch (SQLException e) {
//5、一旦出错,回滚事务
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally {
//关闭连接
JDBCTools.closeCon(conn,st);
}
}
}
DBUtils事务操作
Connection对象的方法名 描述 conn.setAutoCommit(false) 开启事务 new QueryRunner() 创建核心类,不设置数据源(手动管理连接) query(conn , sql , handler, params ) 或 update(conn, sql , params) 手动传递连接 DbUtils.commitAndClose(conn) 提交并关闭连接 DbUtils.rollbackAndClose(conn) 回顾并关闭连接 使用DbUtils操作事务
package com.igeek1;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.junit.Test;
import java.sql.Connection;
import java.sql.SQLException;
public class TestDBUtils {
@Test
public void m1(){
//通过连接池获取连接
Connection conn=JDBCTools.getConn();
QueryRunner qr=new QueryRunner();
String sql="";
try {
conn.setAutoCommit(false);
sql="update account set money=money-1000 where name='冠希'";
qr.update(conn,sql);
sql="update account set money=money+1000 where name='美美'";
qr.update(conn,sql);
DbUtils.commitAndClose(conn);
} catch (SQLException e) {
e.printStackTrace();
try {
DbUtils.rollbackAndClose(conn);
} catch (SQLException e1) {
e1.printStackTrace();
}
}
}
}
tips:使用DbUtils.commitAndCloseQuietly();不会产生异常
分层思想
- 开发中,常使用分层思想
- 不同的层次结构分配不同的解决过程,各个层次间组成严密的封闭系统
- 不同层级结构彼此平等
- 分层的目的是:
- 解耦 降低模块间的关联度
- 可维护性
- 可扩展性
- 可重用性
- 不同层次,使用不同的包表示
- com.igeek 公司域名倒写
- com.igeek.dao dao层//data access object 把数据库操作代码都写在dao中(各种操作数据库的java类,放在同一个包下)
- com.igeek.service service层//业务层 把各种业务代码都写在server层(各种业务类,放在同一个包下)
- com.igeek.domain javabean
- com.igeek.utils 工具 分层思想实现登录操作:
转账案例
事务操作:dao层必须抛出异常,在service层去处理异常
ThreadLocal:可以在层共享数据的一个容器(Map<k,v>) 测试层
@Test
public void testTransfer(){
accountService as=new accountService();
as.transfer();
}
service层
public void transfer(){
accountDao adao=new accountDao();
Connection conn=JDBCTools.getConnection();
JDBCTools.local.set(conn);
try {
conn.setAutoCommit(false);
double money=1000;
adao.out("冠希",money);
adao.in("美美",money);
DbUtils.commitAndClose(conn);
} catch (SQLException e) {
e.printStackTrace();
try {
DbUtils.rollbackAndClose(conn);
} catch (SQLException e1) {
e1.printStackTrace();
}
}
}
dao层
public void out(String outName, double v) throws SQLException {
QueryRunner qr=new QueryRunner();
String sql="update account set money=money-? where name=?";
System.out.println("out"+JDBCTools.local.get());
qr.update(JDBCTools.local.get(),sql,v,outName);
}
public void in(String inName, double v) throws SQLException {
QueryRunner qr=new QueryRunner();
String sql="update account set money=money+? where name=?";
System.out.println("in"+JDBCTools.local.get());
qr.update(JDBCTools.local.get(),sql,v,inName);
}
事务特性:ACID
面试重点记忆
- 原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
- 一致性(Consistency)事务前后数据的完整性必须保持一致。
- 隔离性(Isolation)事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。
- 持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
并发访问问题
- 如果不考虑隔离性,事务存在3种并发访问问题。
-
- 脏读:一个事务读到了另一个事务未提交的数据.
-
- 不可重复读:一个事务读到了另一个事务已经提交(update)的数据。引发另一个事务,在事务中的多次查询结果不一致。
-
- 虚读 /幻读:一个事务读到了另一个事务已经提交(insert)的数据。导致另一个事务,在事务中多次查询的结果不一致。
隔离级别:解决问题
- 数据库规范规定了4种隔离级别,分别用于描述两个事务并发的所有情况。
-
- read uncommitted 读未提交,一个事务读到另一个事务没有提交的数据。
- a) 存放:3个问题(脏读、不可重复读、虚读)。
- b) 解决:0个问题
-
- read committed 读已提交,一个事务读到另一个事务已经提交的数据。
- a) 存放:2个问题(不可重复读、虚读)。
- b) 解决:1个问题(脏读)
-
- repeatable read :可重复读,在一个事务中读到的数据始终保持一致,无论另一个事务是否提交。
- a) 存放:1个问题(虚读)。
- b) 解决:2个问题(脏读、不可重复读)
-
- serializable 串行化,同时只能执行一个事务,相当于事务中的单线程。
- a) 存放:0个问题。
- b) 解决:3个问题(脏读、不可重复读、虚读)
- 安全和性能对比
- 安全性:serializable > repeatable read > read committed > read uncommitted
- 性能 : serializable < repeatable read < read committed < read uncommitted
- 常见数据库的默认隔离级别:
- MySql:repeatable read
- Oracle:read committed
查询数据库的隔离级别
show variables like '%isolation%'; 或 select @@tx_isolation;
设置数据库的隔离级别
set session transaction isolation level 级别字符串