sql

掌握JDBC完成数据表CURD
掌握DAO模式程序

今天学习重点:JDBC事务管理、数据库连接池技术

事务:一组全部成功、全部失败操作。(这组操作不可分割)
案例:转账案例

MySQL 数据库 默认情况下 一条SQL就是一个单独事务,事务是自动提交的
Oracle 数据库 默认情况下 事务不是自动提交 ,所有SQL都将处于一个事务中,你需要手动进行commit提交/rollback回滚

设计账户table
create table account(
id int primary key not null,
name varchar(40),
money double
);

insert into account values(1,’aaa’,1000);
insert into account values(2,’bbb’,1000);
insert into account values(3,’ccc’,1000);

在mysql管理事务
start transaction 开启事务 (所有对数据表增加、修改、删除操作 临时表进行)
rollback 回滚事务 (取消刚刚操作)
commit 提交事务 (确认刚才操作)
* 在事务管理中执行sql,使用数据库内临时表保存,在没有进行事务提交或者回滚,其它用户无法看到事务操作结果的
* SQL语言中只有 DML才能被事务管理 insert update delete

Oracle实验
create table account(
id int primary key not null,
name varchar(40),
money number
);

使用JDBC程序如何控制事务
Connection.setAutoCommit(false); // 相当于start transaction
Connection.rollback(); rollback — 回滚到事务开启时状态
Connection.commit(); commit

  • 将mysql的jar 复制 WEB-INF/lib
  • 复制之前编写的JDBCUtils工具类
  • 将数据库配置文件 dbconfig.properties 复制 src目录下 —– 修改数据库配置

事务回滚点 SavePoint
* 当时事务特别复杂,有些情况不会回滚到事务最开始状态,需要将事务回滚到指定位置
Savepoint sp = conn.setSavepoint(); 设置回滚点
Conn.rollback(sp); 事务回滚到指定位置

create table person(
id int primary key,
name varchar(40)
);


事务的四大特性: ACID 原子性、一致性、隔离性、持久性
原子性:事务的一组操作不可分割,要么都成功,要么都失败
一致性:事务前后数据完整性 转账前 A 和 B 账户总和2000元,转账后 总和还是2000 元
隔离性:并发访问存在时,事务之间是隔离的,一个事务不应该影响其它事务运行效果
持久性:当事务一旦提交,事务数据永久存在,无法改变

企业开发中一定要保证事务原子性,事务最复杂问题都是由事务隔离性引起的

不考虑事务隔离将引发哪些问题:脏读、不可重复读、虚读
脏读:一个事务读取另一个事务 未提交数据 —- 是数据库隔离中最重要问题
不可重复读:一个事务读取另一个事务 已提交数据,在一个事务中两次读取结果不同 —– 在某些情况下出现问题
虚读:一个事务读取另一个事务 插入数据,造成在一个事务中两次读取记录条数不同
* 虚读 不可重复读 区别? 不可重复读读取 update数据 ,虚读读取insert 数据

数据库为了解决三类隔离引发问题:提供四个数据库隔离级别(所有数据库通用)
Serializable : 串行处理 —- 解决三类问题
Repeatable read :可以解决 不可重复读、脏读,会发生虚读 ——- MySQL 默认级别
read committed : 可以 解决脏读 ,会发生 不可重复读、虚读 ——– Oracle默认级别
read uncommitted : 会导致三类问题发生
Serializable > Repeatable read > read committed > read uncommitted
数据库隔离问题危害 脏读> 不可重复读 > 虚读

安全级别越高,处理效率越低;安全级别越低,效率高

在数据库中通过
set transaction isolation level 设置事务隔离级别
select @@tx_isolation 查询当前事务隔离级别

小实验:将演示各个级别导致隔离问题 — 默认并发访问
打开两个mysql 窗口

1、脏读问题 read uncommitted —-
将B窗口隔离级别设置 read uncommitted
set transaction isolation level read uncommitted;
在A、B窗口分别开启一个事务 start transaction
在A窗口完成转账操作
update account set money= money - 200 where name=’aaa’;
update account set money= money +200 where name=’bbb’;

在B窗口进行查询 —- 读取到未提交转账结果
A创建回滚 rollback B窗口结果 恢复之前

2、不可重复读 read committed
将B窗口隔离级别设置 read committed
set transaction isolation level read committed;
重复刚才操作

在A创建没有提交前,B窗口查询数据不会改变 (避免脏读)
A窗口提交 commit
B窗口读取A窗口提交结果 (在同一个事务中 发生不可重复读)

3、虚读 Repeatable read
将B窗户 隔离级别设置 Repeatable read
set transaction isolation level Repeatable read;
在A、B创建分别开启事务
在A窗口转账 提交
B 连续查询,会发现不会读取到 A提交 数据结果 (避免不可重复读)

  • 虚读发生概率很低
    A窗口插入一条数据 B窗口能够在同一个事务查询到 — 虚读

4、演示 Serializable 串行处理效果
将B窗口级别设置 Serializable
set transaction isolation level Serializable;
在A、B窗口同时开启事务

在B窗口查询,在A窗口插入 — 发现A窗口阻塞
* 当B窗口操作表数据时,别窗口无法操作


在JDBC程序中如何控制数据库隔离级别 Connection setTransactionIsolation(int level)
* 如果不设置隔离级别—- 采用数据库默认

  • 64位mysql5.5 无法修改隔离级别 set session transaction isolation level read committed;

事务丢失更新问题
两个事务同时读取同一条记录,A先修改记录,B也修改记录(B是不知道A修改过),B提交数据后B的修改结果覆盖了A的修改结果。

解决丢失更新:通过悲观锁 和 乐观锁

1、悲观锁原理,使用数据库内部锁机制,进行table的锁定,在A修改数据时,A就将数据锁定,B此时无法进行修改 —– 无法发生两个事务同时修改
* 假设丢失更新会发生

在mysql中默认情况下,当你修改数据,自动为数据加锁(在事务中) —- 防止两个事务同时修改数据 —- 读锁
* 事务和锁和不可分开的,锁一定是在事务中使用 ,当事务关闭锁自动释放

在mysql内部有两种常用锁 读锁和写锁
读锁(共享锁) 一张表可以添加多个读锁,如果表添加读锁(不是当前事务添加的),该表不可以修改
* select * from account lock in share mode;
* 共享锁非常容易发生死锁
写锁(排它锁) 一张表只能加一个排它锁,排他锁和其它共享锁、排它锁都具有互斥效果 。
* 如果一张表想添加排它锁,前提之前表一定没有加过共享锁和排他锁
* select * from account for update ;

悲观锁可以使用 排它锁实现 —– 解决丢失更新问题

2、乐观锁原理: 使用不是数据库锁机制,而是一个特殊标记字段,如果控制字段状态和内容,得知数据是否发生并发访问!
* 假设丢失更新不会发生
* 数据库timestamp 时间戳字段
create table blog (
id int primary key,
title varchar(40),
updatetime timestamp
);

insert into blog values(1,’java学习’,null); —- timestamp在数据插入时,字段生成当前时间
update blog set title = ‘传智播客考试’ where id =1 ; —- timestamp 在数据修改时,自动更新为当前时间


数据库开发中存在问题,每次客户请求,在服务器端都单独创建一个连接操作数据库,当并发访问量非常大,很容易造成内存溢出,而且创建连接、释放连接资源非常消耗服务器性能。

连接池原理: 在服务器端一次性创建多个连接,将多个连接保存在一个连接池对象中,当请求需要操作数据库时,不会为请求创建新的连接,而是直接从连接池中获得一个连接,操作数据库结束,并不需要真正关闭连接,而是将连接放回到连接池中。
* 节省创建连接、释放连接 资源

自定义一个连接池
1、编写class 实现DataSource 接口
2、在class构造器 一次性创建10个连接,将连接保存LinkedList中
3、实现getConnection 从 LinkedList中 返回一个连接
4、提供将连接放回连接池中方法
* 当用户使用连接后,不能调用Connection的close,而要使用连接池提供关闭方法,将连接放回连接池

用户调用Connection的 close能否将连接放回连接池呢?
—————— 修改close方法原来逻辑

Java中常用三种方法可以增强 原有方法
1、类继承 、方法覆盖
* 必须控制对象创建,才能使用该方式
2、装饰者模式方法加强
* 必须和目标对象实现相同接口或继续相同父类,特殊构造器(传入被包装对象)
3、动态代理


在实际开发中 不会自己实现连接池 ,使用开源免费数据库连接池
Apache commons-dbcp 连接池
c3p0 数据库连接池
Tomcat内部提供数据库连接池

1、当使用Apache DBCP 需要下载 commons-dbcp.jar commons-pool.jar
* apache commons 子项目 zip包没有快速入门文档 只有API
手动设置四个参数
编写properties配置文件 —— Properties对象加载文件

2、下载c3p0 解压 doc目录 存在c3p0 使用入门
ComboPooledDataSource 手动设置参数
配置文件 在src目录新建 c3p0-config.xml

自定义配置可以有很多个
* 在实际软件系统中,测试环境、开发环境、线上数据库 是不同数据库

Basic Pool Configuration 基本属性
acquireIncrement 当连接池连接用完了,根据该属性决定一次性新建多少连接
initialPoolSize 初始化一次性创建多少个连接
maxPoolSize 最大连接数
maxIdleTime 最大空闲时间,当连接池中连接经过一段时间没有使用,根据该数据进行释放
minPoolSize 最小连接池尺寸

当创建连接池时,一次性创建initialPoolSize 个连接,当连接使用完一次性创建 acquireIncrement 个连接,连接最大数量 maxPoolSize ,当连接池连接数量大于 minPoolSize ,经过maxIdleTime 连接没有使用, 该连接将被释放

ComboPooledDataSource dataSource = new ComboPooledDataSource(“自定义配置名称”);
* 如果不存在配置,将使用默认配置

3、Tomcat内置连接池
因为tomcat和 dbcp 都是Apache公司项目,tomcat内部连接池就是dbcp
* Tomcat 支持Servlet/JSP 容器 ,并不支持所有JavaEE 规范 ——- JNDI
开发者通过JNDI方式 访问Tomcat内置 连接池

将web工程部署到tomcat 三种方式: 配置server.xml 元素、配置独立xml文件 元素 、直接将网站目录复制Tomcat/webapps
虚拟目录 —- 元素


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值