一、事务应用
1、事务定义
用于完成一件事件的独立单元 例如:我们在数据库操作的时候,对数据库的一次更新可以认为是一个事务,业务中银行转账也可以是一个事务(余额的变更)
2、事务的四个特性
a、原子性: 一个事务在执行过程中, 要么全部执行,要么全部不执行,不可能停滞了中途,及时中途失败了,也会回滚到初始状态, 事务是一个不可分割的独立单元
b、一致性: 事务体现在数据库操作中,在事务开始前和结束后,它们的完整性约束没有被破坏,保持前后一致
c、隔离性: 同一时间 ,只允许一个事务处理同一条数据 , 事务和事务相互独立并隔离, 多个事务之间处理同一条数据 必须考虑事务的隔离级别问题
d、持久性: 事务可以提交(commit),也可以回滚(rollbck) ,事务一旦提交将永久生效,事务的回滚可以返回到修改之前的状态。
3、MySql中事务演示
默认情况下,MySql的事务是自动提交 ,
查询事务的提交方式
show variables like 'autocommit';
ON:自动 OFF:关闭
更改事务的提交方式
set @@autocommit =0 ;
注意 以上只能更改当前窗口的事务提交方式,如果需要全局更改 ,需要在my.ini下 添加如下
set @@autocommit =0 ; 然后重启mysql服务
-- 手动提交事务
select * from t_user;
update t_user set money =400 where userid =1;
-- 对于没有提交的记录 ,会锁行(行级锁)
commit;
rollback;
事务的并发产生的问题
1、 脏读: 事务A 读取了 事务B未提交的数据 ,假如事务B 对数据更新后,事务A读取了一次,事务B对数据回滚了, 事务A再次读取一次,事务A发现 第一次读的数据是脏数据。
2、不可重复读: 事务A多次读取同一条记录,事务B在期间进行提交了,到时事务A读取的数据前后不一致,这是属于 不可重复读 。
3、幻读: 对于新增记录而言, 事务A第一次读取 与第二个读取的表数据总行数 不一致 ,不一致原因是 在期间事务B可能添加了记录 ,导致事务A出现 幻觉多了一条记录。
MySql的默认隔离级别: 可重复读取
select @@transaction_isolation
REPEATABLE-READ
修改MySql的隔离级别:
set session transaction isolation level read committed ;
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交数据(read uncommitted) | 是 | 是 | 是 |
读已提交数据(read committed) | 否 | 是 | 是 |
可重复读(repeatable-read ) | 否 | 否 | 是 |
序列化(serializable) | 否 | 否 | 否 |
4、JDBC中 事务的手动提交
默认情况下 JDBC的事务是自动提交的, 由于在特定的业务场景下 一个事务需要执行多个sql语句 ,例如转账业务 ,账户A给账户B转账100元, 账户A的余额需要减少100 , 而账户B的余额要增加100 (2个sql)
public static void main(String[] args) {
// 账户A 给账户B 转账
MyUser fromUser = new MyUser();
fromUser.setUserid(1);
MyUser toUser = new MyUser();
toUser.setUserid(2);
Scanner sc = new Scanner(System.in);
System.out.println("请输入转账的金额");
double money = sc.nextDouble();
// 获取连接对象
Connection conn = null;
PreparedStatement ps = null;
// 设置该连接为 手动提交事务
try {
conn = DBUtil.getConn();
conn.setAutoCommit(false);
String sql1 = "update t_user set money = money-"+money +" where userid = ?";
ps = conn.prepareStatement(sql1);
// 设置参数
ps.setInt(1,fromUser.getUserid());
// 执行
int count1= ps.executeUpdate();
System.out.println("count1----"+count1);
//模拟突然断电
System.out.println(10/0);
// 另一个账户的钱 增多
sql1 = "update t_user set money = ifnull(money,0)+"+money +" where userid = ?";
ps = conn.prepareStatement(sql1);
ps.setInt(1,toUser.getUserid());
//执行
int count2 = ps.executeUpdate();
System.out.println("count2----"+count2);
// 提交事务
conn.commit();;
System.out.println("转账成功");
} catch (Exception e) {
e.printStackTrace();
// 回滚
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
System.out.println("转账失败");
} finally {
//关闭连接
DBUtil.closeAll(conn , ps , null);
}
}
二、DAO的综合应用
创建图书表和 用户表 ,完成后台图书管理系统。
1、建立独立项目: 项目包括 dao 、entity、 util包
dao中不同的表提供不同的接口 和 实现类
entity中提供对应表的实体类
util包装 提供 DBUtil类
写一个控制台程序,调用 dao的实现类 (BookDaoImpl、UserDaoImpl)
完成功能:
1、用户登录,用户注册(注册时验证用户是否存在)
2、图书管理: 添加图书,删除图书,修改图书,查询所有图书,根据ID查询图书。
b_user : 用户id 用户名 密码 状态
book :图书id ,图书名,图书作者,图书价格,图书发布时间 ,图书所属的userid (外键)
项目分层:
项目名
-|src
-| com.j2008
-|util : DBUtil.java
-| entity: XXX.java ...
-|dao: XXXDao.java
XXXDaoImpl.java
-| client : 测试类 控制台输入