今天学习重点: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)
);
例子:
public void demo3() {
// 创建person表,向表中插入20000条数据 ------ 如果插入过程中发生错误,插入数据条数1000整数倍
// PreparedStatement 批处理
Connection conn = null;
PreparedStatement stmt = null;
Savepoint savepoint = null;
try {
conn = JDBCUtils.getConnection();
// 开启事务
conn.setAutoCommit(false);
// 保存一次
savepoint = conn.setSavepoint();
String sql = "insert into person values(?,?)";
// 预编译SQL
stmt = conn.prepareStatement(sql);
for (int i = 1; i <= 20000; i++) {
stmt.setInt(1, i);
stmt.setString(2, "name" + i);
// 添加到批处理
stmt.addBatch();
if (i == 4699) {
int d = 1 / 0;
}
// 每隔200向数据库发送一次
if (i % 200 == 0) {
stmt.executeBatch();
stmt.clearBatch();
}
if (i % 1000 == 0) {// 1000整数倍
// 保存 回滚点
savepoint = conn.setSavepoint();
}
}
stmt.executeBatch();// 为了确保缓存sql都提交了
// 没有错误
conn.commit();
} catch (Exception e) {
// 回滚事务,回滚存储点
try {
conn.rollback(savepoint);
conn.commit();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally {
JDBCUtils.release(stmt, conn);
}
}
----------------------------------------------------------------------------------------------
事务的四大特性: 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对象加载文件
public class DBCPUtils {
private static DataSource ds = null;
static {
try {
InputStream in = DBCPUtils.class.getClassLoader()
.getResourceAsStream("dbcpconfig.properties");
Properties prop = new Properties();
prop.load(in);
ds = BasicDataSourceFactory.createDataSource(prop);
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public static Connection getConnection() throws SQLException{
return ds.getConnection();
}
public static DataSource getSource(){
return ds;
}
}
2、
导入c3p0-0.9.1.2.jar
下载c3p0 解压 doc目录 存在c3p0 使用入门
ComboPooledDataSource 手动设置参数
配置文件 在src目录新建 c3p0-config.xml
<default-config>
<named-config name="intergalactoApp"> 自定义配置可以有很多个
* 在实际软件系统中,测试环境、开发环境、线上数据库 是不同数据库
Basic Pool Configuration 基本属性
acquireIncrement 当连接池连接用完了,根据该属性决定一次性新建多少连接
initialPoolSize 初始化一次性创建多少个连接
maxPoolSize 最大连接数
maxIdleTime 最大空闲时间,当连接池中连接经过一段时间没有使用,根据该数据进行释放
minPoolSize 最小连接池尺寸
当创建连接池时,一次性创建initialPoolSize 个连接,当连接使用完一次性创建 acquireIncrement 个连接,连接最大数量 maxPoolSize ,当连接池连接数量大于 minPoolSize ,经过maxIdleTime 连接没有使用, 该连接将被释放
ComboPooledDataSource dataSource = new ComboPooledDataSource("自定义配置名称");
* 如果不存在配置,将使用默认配置
public class JDBCUtils {
private static DataSource dataSource = new ComboPooledDataSource();
// 返回连接池,将连接池交给框架,框架自动获得连接 管理事务
public static DataSource getDataSource() {
return dataSource;
}
// 提供DBUtils 手动控制事务使用
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
}
3、Tomcat内置连接池
因为tomcat和 dbcp 都是Apache公司项目,tomcat内部连接池就是dbcp
* Tomcat 支持Servlet/JSP 容器 ,并不支持所有JavaEE 规范 ------- JNDI
开发者通过JNDI方式 访问Tomcat内置 连接池
将web工程部署到tomcat 三种方式: 配置server.xml <Context> 元素、配置独立xml文件 <Context> 元素 、直接将网站目录复制Tomcat/webapps
虚拟目录 ---- <Context> 元素
<Context>
<Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000"
username="root" password="123" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/day14"/>
</Context>
在哪里配置<Context> ???三个位置
1) tomcat安装目录/conf/context.xml --------- 对当前tomcat内部所有虚拟主机中任何工程都有效
2) tomcat安装目录/conf/Catalina/虚拟主机目录/context.xml -------- 对当前虚拟主机任何工程都有效
3) 在web工程根目录/META-INF/context.xml ------- 对当前工程有效
用JNDI访问连接池! 什么是JNDI ?
1)、将一个Java对象,绑定JNDI容器中,为java对象起一个名字
2)、Java其它程序 通过名字 检索到绑定对象
编写JNDI程序
1)、 将jar复制tomcat/lib
2)、 编写访问JNDI程序 运行Tomcat内部 ---- 通常Servlet、JSP
// 创建检索对象
Context initCtx = new InitialContext();
// 默认查找顶级java 名称串 固定:java:comp/env
Context envCtx = (Context) initCtx.lookup("java:comp/env");
// 根据设置名称 查找连接池对象
DataSource ds = (DataSource) envCtx.lookup("jdbc/TestDB");
总结:
1、mysql数据库控制事务 start transaction 、commit 、 rollback
* mysql默认事务自动提交,Oracle需要手动提交
2、JDBC程序控制事务 conn.setAutoCommit(false) conn.commit() conn.rollback();
* SavePoint
3、事务四大特性 ACID
4、如果不考虑隔离性 引发三类问题:脏读、不可重复读、虚读
5、数据库四个隔离级别 Serializable / Repeatable read / Read Committed / Read UnCommitted
* 隔离级别实验
6、丢失更新 悲观锁和乐观锁
悲观锁 select * ... for update
乐观锁 时间戳
7、连接池原理
8、三种方法增强 !!!!!!!!!!!!!
继承覆盖、装饰者、动态代理
9、Apache DBCP 、c3p0 、Tomcat内置连接池 使用 ------- c3p0
10、Tomcat内置连接池 --- JNDI原理
JNDI容器启动时,将对象绑定容器中,为对象起名字
JNDI容器中其它程序,可以通过名称 ---- 访问绑定对象
相关推荐
总结起来,事务管理和连接池是Java后端开发中的重要技术。事务保证了数据的正确性,而连接池则提高了数据库操作的效率。理解并熟练运用这些概念,对于提升应用性能和稳定性至关重要。在实际项目中,我们需要根据业务...
JDBC连接池是一种优化技术,它可以复用已建立的数据库连接,减少频繁创建和销毁连接带来的开销。常见的JDBC连接池实现有C3P0、Apache DBCP、HikariCP等。使用连接池,开发者需要在应用启动时配置连接池参数,然后...
【JDBC连接池&JDBCTemplate】是Java开发中处理数据库交互的重要技术。JDBC是Java Database Connectivity的缩写,它提供了一组接口和类,使得Java程序能够与各种类型的数据库进行通信。JDBC不仅是Java操作数据库的...
3. **连接池**:通过连接池管理数据库连接,避免频繁创建和销毁连接。 4. **结果集缓存**:如果可能,将ResultSet的结果缓存起来,避免多次查询。 5. **关闭结果集和Statement**:及时关闭,释放数据库资源。 **...
标题“day01_jdbc_consumer_”暗示了我们即将探讨的是关于Java数据库连接(JDBC)的初级内容,特别是从消费者或使用者的角度出发。描述中提到的“基础语法等等jdbc接口”表明我们将深入理解JDBC的基本操作和常用接口...
9. **数据库连接池**:"day04"和"day05"可能涉及到数据库连接池的使用,如Apache的Commons DBCP或C3P0,它们能够有效地管理数据库连接,提高系统性能。 10. **PL/SQL**:虽然不是JDBC的一部分,但"PL_SQL"可能是...
6. **优化与性能**:为了提高性能,可以考虑使用批处理(Batch Processing)来一次发送多个SQL语句,或者使用连接池(Connection Pool)来复用数据库连接,减少频繁的创建和销毁连接。 在"day17_2"这个文件中,可能...
在本课程中,崔希凡老师会展示如何创建和使用DBUtil,包括配置数据库连接信息,建立连接池,以及如何通过DBUtil执行SQL的增删改查操作。 接着,JDBC是Java连接数据库的标准接口,它提供了与各种数据库进行交互的...
10. **数据库连接池**:简述数据库连接池的概念,如C3P0、DBCP、HikariCP等,以及它们在提高性能和管理连接方面的优势。 通过这个压缩包的学习,你将能够掌握JDBC的基本操作,并具备使用Java连接数据库的基础能力。...
- 使用数据库连接池,减少连接创建和销毁的开销。 通过本套学习资料,你可以系统地了解和掌握JDBC的使用,包括建立连接、执行SQL、处理结果、事务处理、批处理和优化技巧等,为实际的数据库操作打下坚实的基础。...
在Java编程领域,JDBC(Java Database Connectivity)是Java应用程序连接和操作数据库的...在实际开发中,`JdbcUtil`类往往结合数据库连接池,提升应用性能,并通过参数化查询和事务管理,保证数据的安全性和一致性。
【数据库查询jdbc1】是关于Java数据库连接(JDBC)的学习资源,主要涉及如何通过Java程序与...此外,了解事务管理、批处理和连接池等高级特性也是十分重要的,它们在实际开发中能够显著提升应用程序的性能和稳定性。
因此,使用连接池(如C3P0、HikariCP或Apache DBCP)可以有效地管理数据库连接,提高系统性能。 10. **异常处理**:在进行JDBC操作时,应捕获并适当地处理可能出现的`SQLException`,确保程序的健壮性。 在`sd1008...
总结,"02_JDBC_day02(代码)" 包含的可能是从基础的数据库连接到更复杂的事务处理、批处理和预编译语句的示例代码,学习者可以通过这些代码深入了解和掌握JDBC在实际项目中的应用。通过分析并运行这些代码,可以更好...
9. **连接池**:在实际应用中,为了提高性能和减少资源消耗,通常会使用连接池(如C3P0、HikariCP或Apache DBCP)来管理和复用数据库连接。 10. **异常处理**:在Java代码中,使用`try-catch-finally`块来捕获并...
8. **JDBC连接池**:在实际应用中,通常会使用连接池(如C3P0、HikariCP、Apache DBCP等)来管理数据库连接,提高系统性能和资源利用率。 9. **数据库元数据**:`DatabaseMetaData`接口提供了获取数据库信息的方法...
在day01的学习中,首先会介绍JDBC的基本概念,包括JDBC API的组成、驱动管理、数据库连接的建立和关闭。你会了解到如何加载JDBC驱动,通过`Class.forName()`方法注册驱动,并使用`DriverManager.getConnection()`...
在"Mybatis_day03教案1"中,主要涵盖了Mybatis的连接池、动态SQL以及事务管理等核心概念。以下是对这些知识点的详细解释: 1. **连接池**: - **作用**:连接池是数据库连接管理的一种机制,它预先创建并维护一定...
9. **连接池**: 为了提高性能和减少资源消耗,通常使用连接池(如C3P0、HikariCP、DBCP等)来管理和复用数据库连接。 10. **JDBC API的其他接口和类**: `CallableStatement`用于调用存储过程,`DatabaseMetaData`...
10. **连接池**:在实际应用中,为了提高性能和减少资源消耗,通常会使用连接池(如C3P0、HikariCP等),它预先创建一定数量的数据库连接,供应用程序复用。 11. **JDBC工具**:例如Hibernate、MyBatis等ORM框架,...