Part01:事务管理
1、事务概述:
- 事务:指的是逻辑上的一组操作(多条sql语句),组成这组操作的各个单元要么全部成功,要么全部失败。
- 事务作用:保证在一个事务中多次操作要么全部成功,要么全部失败;
- 例如转账的转账方和收款方;
2、MySQL事务操作:
- start transaction:开启事务(Java中用setAutoCommit(false)方法)
- commit:提交事务
- rollback:回滚事务
3、使用JDBC自写工具类模拟转账功能:
public class JDBCAccountDemo {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
try {
//获取连接
connection = JDBCUtils.getConnection();
//在所有操作之前执行事务
connection.setAutoCommit(false);//设置自动提交事务为false(就是开启事务)
statement = connection.createStatement();
String sql1 = "update account set money = money - 100 where name = '李四'";
String sql2 = "update account set money = money + 100 where name = '张三'";
int rows1 = statement.executeUpdate(sql1);
//模拟出现异常,转出成功,转入被异常阻断
//System.out.println(1/0);
int rows2 = statement.executeUpdate(sql2);
//在所有操作之后,提交事务
connection.commit();
if(rows1 > 0 && rows2 > 0){
System.out.println("转账成功");
}
} catch (SQLException e) {
//出现异常,回滚事务
try {
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally {
//关闭资源
JDBCUtils.closeAll(connection,statement,null);
}
}
}
4、使用DBUtils工具类实现转账案例:
- QueryRunner的两个构造:
- public QueryRunner(DataSource);//不支持事务操作,因为不能确定从连接池获取的是同一个连接
- 方法:
- update(String sql, Object … params)
- query(String sql,ResultSetHandler handler,Object … params)
- 方法:
- public QueryRunner( );//支持事务操作
- 方法:
-
update(Connection con,String sql, Object … params)
-
query(Connection con,String sql,ResultSetHandler handler,Object … params)
//1.创建QueryRunner对象 QueryRunner qr = new QueryRunner(); Connection con = null; try { con = C3P0Utils.getConnection();//获取连接 //开启事务 con.setAutoCommit(false); //2.执行sql语句 int rows1 = qr.update(con, "update account set money=money-? where name=?", 100, "张三"); System.out.println(1 / 0);//模拟出现异常 int rows2 = qr.update(con, "update account set money=money+? where name=?", 100, "李四"); //提交事务 con.commit(); if (rows1 > 0 && rows2 > 0) { System.out.println("转账成功"); } } catch (Exception e) { try { //回滚事务 con.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } System.out.println("转账出现问题,程序回滚"); } finally { try { con.close();//关闭资源 } catch (SQLException e) { e.printStackTrace(); } } }
-
- 方法:
- public QueryRunner(DataSource);//不支持事务操作,因为不能确定从连接池获取的是同一个连接
5、三层思想:高内聚、低耦合
- DAO层:(date access object)数据访问层(处理数据库),一般抛出异常
- Service层:业务层(处理业务逻辑),一般捕获异常
- Web层/view层:给用户看的
6、分层的目的:
- 解耦:减少层与层之间的关系
- 增强可维护性
- 增强可扩展性
- 增强可重用性
7、不同层次包名命名规范:
- com.megu 公司域名倒写
- com.megu.dao dao层
- com.megu.service service层
- com.megu.domain javabean
- Com.megu.utils 工具
- com.megu.web/view web层
8、事务的特性:
- 原子性:强调事务的不可分割,多条语句要么都成功,要么都失败
- 一致性:强调的是事务执行的前后,数据要保持一致
- 隔离性:一个事务的执行不应该受到其他事务的干扰
- 持久性:事务一旦结束(提交/回滚),数据就持久保持到了数据库
9、如果不考虑事务的隔离性,可能引发以下安全性问题:
- 脏读:一个事务读到另一个事务还没有提交的数据
- 不可重复读:一个事务读到另一个事务以及提交的update的数据,导致在当前的事务中多次查询结果不一致;
- 虚读/幻读:一个事务读到另一个已经提交的insert的数据,导致当前的事务中多次的查询结果不一致;
10 、设置事务的隔离级别:
- 1 read uncommitted :未提交读,以上三种安全问题都有可能发生
- 2 read committed:已提交读,可以避免脏读,但是其他两种问题都有可能发生(Oracle默认)
- 4 repeatable read:可重复读,可以避免脏读和不可重复读的问题,但是虚读有可能发生(MySQL默认);
- 8 serializable:串行化的,可以避免以上三种问题的发生
注意⚠️:级别越高,越安全,效率越低
11、MySQL中查看当前事务级别:SELECT @@TX_ISOLATION;
更改当前事务隔离级别:SET TRANSACTION ISOLATION LEVEL ….
Part02:ThreadLocal
1、ThreadLocal:线程局部变量
- 作用:为每一个线程,提供一个独立的存储数据空间,只能由该线程访问
- 底层:ThreadLocal类实际上在内部封装了一个Map集合Map<Thread,Object>
- 方法:
- get( );//以当前线程对象为键,从类内部的Map中获取值
- set(Object);//给当前线程对象设置值,保存到map集合中
- 结论:ThreadLocal能保证哪个线程的数据只能由那个线程取出,别的线程无法取出;