Review: Java SE 05(JDBC、数据库连接池和DBUtils)
Java SE 05
一、JDBC
1. JDBC 概述
- JDBC(Java Data Base Connectivity) 是 Java 访问数据库的标准规范。是一种用于执行 SQL 语句的 Java API,可以为多种关系数据库提供统一访问,它由一组用 Java 语言编写的类和接口组成。是 Java 访问数据库的标准规范
- JDBC 就是由 sun 公司定义的一套操作所有关系型数据库的规则 (接口),而不同的数据库厂商需要实现这套接口,提供数据库驱动 jar 包,开发者可以使用这套接口编程, 真正执行的代码是对应驱动包中的实现类
2. 使用 JDBC 实现类进行开发的流程
-
添加 MySQL 驱动 jar 包到当前项目下
-
注册驱动
作用:将厂商的 jar 包中的实现类加载到当前类下,比如
com.mysql.jdbc.Driver
中定义了静态代码块当 Driver 类加载后执行静态代码块中的注册驱动语句从 JDBC3 开始,目前已经普遍使用的版本,可以不用注册驱动而直接使用,Class.forName 这句话可以省略
-
获得连接
使用 DriverManager 类中 getConnection 方法来获取连接即获取 Connection 对象
getConnection 方法中需要传入的参数有 3 个
URL(包括数据库厂商名称、服务器所在 IP、端口号、所选数据库、参数信息(可选)),数据库用户名,密码
-
获取语句执行平台(获取 Statement 接口对象)
通过 Connection 对象调用 createStatement 方法来获取 Statement 对象
Statement 对象:代表一条语句对象,用于发送 SQL 语句给服务器,用于执行静态 SQL 语句并返回它所生成结果的对象
Statement 对象中常用方法:
int executeUpdate(String sql)
用于执行 insert update delete 语句,也支持 DDL.返回 int 类型, 代表受影响的行数ResultSet executeQuery(String sql)
用于执行 select 语句, 返回 ResultSet 结果集对象注意:sql 字符串中写 SQL 语句,其中字符串使用单引号,语句结尾
;
可以省略 -
处理结果集(只在查询时处理)
只有在进行查询操作 executeQuery 的时候, 才会处理结果集 ResultSet
ResultSet 常用方法:
boolean next()
游标指向下一行,还有下一条记录就返回 truexxx getXxx( String or int)
重载方法,通过列名或列号(索引从 1 开始)进行获取数据 -
释放资源
-
需要释放的对象:ResultSet 结果集 (非查询,不用释放),Statement 语句,Connection 连接
-
释放原则:先开的后关,后开的先关;ResultSet ==> Statement ==> Connection
释放方法就是调用对应的 close 方法
-
一般将释放资源的语句放在异常处理的 finally 代码块中
-
3. JDBC 实现增删改查
-
JDBC 工具类
-
工具类:如果一个功能经常要用到,建议把这个功能做成一个工具类,可以在不同的地方重用
-
获取 Connection和释放资源在增删改查中会重复使用,所以将这两个操作的方法封装为 JDBCUtils 工具类中的静态方法
获取 Connection 需要 URL、数据库用户名、密码、驱动全类名的字符串(可省)可以将这些字符串设置为工具类中的常量,
若使用低版本的 JDBC 中还需要将 Class.forName 加载驱动的操作也封装在工具类中
-
封装 JDBCUtils 工具类的实例(不含注册驱动的版本)
public class JDBCUtils { //1. 定义字符串常量, 记录获取连接所需要的信息 public static final String URL = "jdbc:mysql://localhost:3306/db_jdbc_test"; public static final String USERNAME = "root"; public static final String PASSWORD = "xxxx";
<span class="token comment">//2.获取连接的静态方法</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">Connection</span> <span class="token function">getConnection</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token comment">//获取连接对象</span> <span class="token class-name">Connection</span> conn <span class="token operator">=</span> <span class="token class-name">DriverManager</span><span class="token punctuation">.</span><span class="token function">getConnection</span><span class="token punctuation">(</span><span class="token constant">URL</span><span class="token punctuation">,</span> <span class="token constant">USERNAME</span><span class="token punctuation">,</span> <span class="token constant">PASSWORD</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//返回连接对象</span> <span class="token keyword">return</span> conn<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">SQLException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span> e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">//关闭资源的方法(DML)</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">close</span><span class="token punctuation">(</span><span class="token class-name">Connection</span> conn<span class="token punctuation">,</span> <span class="token class-name">Statement</span> stmt<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>conn <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">&&</span> stmt <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> conn<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> stmt<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">SQLException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span> e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">//关闭资源的方法(重载方法用于DQL后的释放资源)</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">close</span><span class="token punctuation">(</span><span class="token class-name">Connection</span> conn<span class="token punctuation">,</span> <span class="token class-name">Statement</span> stmt<span class="token punctuation">,</span> <span class="token class-name">ResultSet</span> res<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>res <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token comment">//对ResultSet对象进行关闭</span> res<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">SQLException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span> e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">//直接复用DML的释放资源方法</span> <span class="token function">close</span><span class="token punctuation">(</span>conn<span class="token punctuation">,</span> stmt<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
}
-
-
DML 操作
-
防止插入中文乱码的解决方案
将工具类中 URL 字符串中指定字符的编解码格式
jdbc:mysql://localhost:3306/db4?characterEncoding=UTF-8
-
使用工具类进行插入、删除、更改
public void test() throws SQLException { //1. 通过工具类获取连接 Connection connection = JDBCUtils.getConnection(); //2. 获取 Statement Statement statement = connection.createStatement(); //2.1 编写 Sql String sql = "xxx"; //2.2 执行 Sql statement.executeUpdate(sql); //3. 通过工具类关闭流 JDBCUtils.close(connection,statement); }
-
-
DQL 操作
-
使用工具类进行查询操作
public void test() throws SQLException { //1. 通过工具类获取连接 Connection connection = JDBCUtils.getConnection(); //2. 获取 Statement Statement statement = connection.createStatement(); //2.1 编写 Sql String sql = "xxx"; //2.2 执行 Sql, 获取 ResultSet 对象 ResultSet res = statement.executeUpdate(sql); //2.3 处理结果集 //3. 通过工具类关闭流 JDBCUtils.close(connection, statement, res); }
-
4. SQL 注入问题
-
问题:用户输入的密码和 SQL 语句进行字符串拼接。用户输入的内容作为了 SQL 语句语法的一部分,改变了原有 SQL 真正的意义
拼接后 WHERE 条件永远为真,相当于没有过滤,查询全部数据,造成密码不正确也能进行查询现象
-
解决:防止用户输入的密码和执行查询的 SQL 语句进行拼接
5. PreparedStatement 接口
-
PreparedStatement 接口概述
-
PreparedStatement 接口是 Statement 接口的子接口,继承了 Statement 中的所有方法
-
PreparedStatement 是一个预编译的 SQL 语句对象
预编译是指 SQL 语句被预编译, 并存储在 PreparedStatement 对象中,然后可以使用此对象多次高效地执行该语句
-
-
PreparedStatement 特点
- 预编译可以提高 SQL 执行的效率
- 可以有效防止 SQL 注入的问题
- 可以减少编译次数提高数据库性能
- PrepareStatement 是预编译的 SQL 语句对象,语句中可以包含动态参数
?
,在执行时可以为?
动态设置参数值
-
获取 PreparedStatement 对象
通过 Connection 中的方法
PreparedStatement prepareStatement(String sql)
通过 Connection 中的方法获取 Statement 对象的方法
Statement createStatement()
中是空参的,使用 Statement 对象时才传入 SQL 参数
指定预编译的 SQL 语句,SQL 语句中使用占位符
?
创建一个语句对象如:
"SELECT * FROM jdbc_user WHERE username=? AND password=?";
-
使用 PreparedStatement 步骤
-
获取 PreparedStatement 对象(传入包含动态参数的 SQL 语句)
-
使用
setXxx(占位符位置,真实的值)
方法设置占位符参数占位符的起始索引是 1
-
执行 PreparedStatement 中处理 SQL 的方法
PreparedStatement 接口中的常用方法同 Statement 接口,不同是 Statement 的方法中传参是完整的静态 SQL 字符串:
int executeUpdate();
执行 insert、update、delete 语句ResultSet executeQuery();
执行 select 语句,返回结果集对象 ResuletSet -
释放资源(调用 PreparedStatement 对象中的 close 方法)
-
-
Statement 与 PreparedStatement 的对比
-
先通过 Connection 生成对象 Statement,Statement 对象每调用一次处理 SQL 的方法,就会将 SQL 发送给数据库,数据库要先编译再执行;
假如有一万条数据,就会编译一万次
-
通过 Connection 生成 PreparedStatement 对象的同时会将 SQL 语句发送给数据库进行预编译,后面执行多次插入操作,每次只需要调用 PreparedStatement 中的 setXxx 方法进行占位符的参数设置和调用处理 SQL 的方法;
假如有一万条数据,数据库也只是进行一次编译,所以可以减少编译次数,提高效率
-
6. JDBC 控制事务
-
可以使用 MySQL 的命令来操作事务,也可以使用 JDBC 实现类来操作事务
-
一般使用 Connection 中的方法实现事务管理
使用步骤:
-
获取连接
-
开启事务
void setAutoCommit(boolean autoCommit)
参数是 true 或 false 如果设置为 false,表示关闭自动提交,相当于开启事务
-
获取到 PreparedStatement , 执行更新操作
-
正常情况下提交事务
void commit()
-
出现异常回滚事务
void rollback()
-
关闭资源
-
二、数据库连接池和 DBUtils
1. 数据库连接池
-
连接池概述
-
作用:实际开发中获得连接或释放资源是非常消耗系统资源的两个过程,为了解决性能的问题,通常采用连接池技术,来共享 Connection。这样就不需要每次都创建连接、释放连接了,将这些操作都交给连接池了。
用池来管理 Connection,这样可以重复使用 Connection。 当使用完 Connection 后,调用 Connection 的 close() 方法也不会真的关闭 Connection,而是把 Connection“归还”给池。
-
Java 为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池。
-
-
DBCP 连接池
-
在 DBCP 包中提供了 DataSource 接口的实现类,具体是连接池 BasicDataSource 类
DataSource 接口中的常用方法:
Connection getConnection()
-
封装 DBCPUtils 工具类:
- 定义常量保存数据库连接的相关信息(DRIVERNAME、URL、USERNAME、PASSWORD)
- 创建连接池对象(使用 DBCP 提供的实现类 BasicDataSource 类,将该对象定义为静态成员变量)
- 使用静态代码块进行数据库连接的相关信息配置(使用 BasicDataSource 中的 setXxxx 方法)
- 封装获取连接的静态方法(使用 BasicDataSource 类创建的静态成员变量调用 getConnection 方法)
- 封装释放资源的静态方法(DML、DQL 两种 close 方法重载)
-
使用 DBCPUtils 工具类:
在获得连接和释放资源时使用 DBCPUtils 中的方法来实现对数据库的 DML、DQL 操作
-
-
C3P0 连接池
-
C3P0 提供的核心工具类, ComboPooledDataSource , 如果想使用连接池, 就必须创建该类的对象
-
封装 C3P0Utils 工具类:
-
创建连接池对象通过使用 C3P0 对 DataSource 接口的实现类,将这个对象作为 C3P0Utils 的静态成员变量
C3P0 中的配置信息都存储在配置文件 c3p0-config.xml 中,可以定义默认配置和指定配置名的配置
在 ComboPooledDataSource 构造方法中空参为使用 XML 的默认配置,带参为使用 XML 中指定配置名
-
封装获取连接的方法(使用 ComboPooledDataSource 类创建的静态成员变量调用 getConnection 方法)
-
封装释放资源的方法(DML、DQL 两种 close 方法重载)
-
-
使用 C3P0Utils 工具类
在获得连接和释放资源时使用 C3P0Utils 中的方法来实现对数据库的 DML、DQL 操作
-
-
Druid 连接池
-
Druid(德鲁伊)是阿里巴巴开发的号称为监控而生的数据库连接池,Druid 是目前最好的数据库连接池。在功能、性能、扩展性方面,都超过其他数据库连接池,同时加入了日志监控,可以很好的监控 DB 池连接和 SQL 的执行情况
-
必要的 jar 包
-
封装 DruidUtils 工具类:
-
通过工厂类 DruidDataSourceFactory 的
createDataSource(Properties p)
方法获取连接池 DataSource 类型对象-
Druid 中的配置信息一般存储在
.properties
文件中,文件名可自定义 -
设置一个静态代码块在 DruidUtils 工具类加载时创建连接池 DataSource 类型的对象:
Druid 连接池不能够主动加载配置文件 , 需要指定文件,将指定的文件加载到字节输入流对象中传入 Properties 类型对象的 load 方法中,传入工厂类
createDataSource(Properties p)
方法中来创建连接池对象
-
-
封装获取连接的方法(使用 DataSource 类型的静态成员变量调用 getConnection 方法)
-
封装释放资源的方法(DML、DQL 两种 close 方法重载)
-
-
使用 DruidUtils 工具类
在获得连接和释放资源时使用 DruidUtils 中的方法来实现对数据库的 DML、DQL 操作
-
2. DBUtils 工具类
-
DBUtils 中的核心功能
DbUtils 类, 是一个工具类, 定义了关闭资源与事务处理相关方法
-
JavaBean 组件的概念
- JavaBean 就是一个类,开发中通常用于封装数据
- 需要实现序列化接口 Serializable
- 提供私有字段
- 提供 getter 和 setter
- 提供空参构造
- JavaBean 就是一个类,开发中通常用于封装数据
-
使用 DBUtils 中的 QueryRunner 完成增删改
-
创建 QueryRunner
自动方式创建需要向 QueryRunner 构造中传入 DataSource 类型的数据库连接池对象
手动方式创建需要使用 QueryRunner 空参构造
-
QueryRunner 实现增删改操作主要方法
update(Connection conn, String sql, Object... params)
update(String sql, Object... params)
-
update 方法是有重载的方法,当 QueryRunner 是自动方式创建的不用传 Connection 对象,
手动方式创建 QueryRunner 要传入 Connection 对象,使用连接池工具类调用其中的 getConnection 方法获取
-
动态 SQL 中使用
?
作为占位符 -
params 是可变长度 Object 类型的变量(一般传入 Object[] 类型数组),用来设置占位符上的参数
-
当动态 SQL 中使用
?
作为占位符的参数只有一个时,不需要创建数组传参,直接在 params 位置传入这一个参数即可
-
-
QueryRunner 实现增删改操作主要流程
- 创建 QueryRunner 对象(自动或手动)
- 调用 QueryRunner 对象中的 update 方法
-
-
使用 DBUtils 中的 QueryRunner 完成查询操作
-
创建 QueryRunner
-
QueryRunner 实现查询操作主要方法
query(Connection con,String sql,handler,Object[] param)
query(String sql, handler ,Object[] param)
query 方法的返回值都是泛型, 具体的返回值类型, 会根据结果集的处理方式, 发生变化
其中 handler 需要的对象类型是 ResultSetHandler 接口的实现类,ResultSetHandler 可以对查询出来的 ResultSet 结果集进行处理,达到一些业务上的需求,每一种实现类都代表了对查询结果集的一种处理方式
常用的 ResultSetHandler 实现类:
ArrayHandler 将结果集的第一条数据封装到数组中
ArrayListHandler 可以将每条数据先封装到数组中, 再将数组封装到集合中
BeanHandler 将结果集的第一条数据封装到 JavaBean 中
BeanListHandler 将结果集的每一条和数据封装到 JavaBean 中 再将 JavaBean 放到 list 集合中
MapHandler 将结果集的第一条记录封装到 Map<String,Object> 中 key 对应的是 列名 value 对应的是 列的值
ScalarHandler 用于封装单个的数据
-
QueryRunner 实现查询操作主要流程
- 创建 QueryRunner 对象(自动或手动)
- 调用 QueryRunner 对象中的 query 方法
-
3. 数据库批处理
-
数据库批处理概述
- 批处理指的是一次操作中执行多条 SQL 语句,批处理相比于一次一次执行效率会提高很多
- 批处理操作主要使用了 Statement 或 PreparedStatement 中的批处理方法
-
实现数据库批处理
-
mysql 批处理是默认关闭的,所以需要加一个参数才打开 mysql 数据库批处理,在 url 中添加
rewriteBatchedStatements=true
-
批处理主要方法
void addBatch()
将给定的 SQL 命令添加到此 Statement 对象的当前命令列表中,
通过调用方法 executeBatch 可以批量执行此列表中的命令
int[] executeBatch()
每次提交一批命令到数据库中执行,如果所有的命令都成功执行了,
那么返回一个数组,这个数组是说明每条命令所影响的行数
-
实现数据库批处理的主要流程
- 获取 Connection 对象
- 获取 PreparedStatement 或 Statement 对象
- 将 SQL 添加到批处理列表
- 统一进行批量处理
-
4. MySQL 元数据
-
MySQL 元数据概述
- 除了表之外的数据都是元数据,包括查询结果信息、数据库和数据表的信息、MySQL 服务器信息
- 查看 MySQL 元数据的常用命令
-
JDBC 获取数据库元数据信息
connection 连接对象, 调用
getMetaData()
方法, 获取的是 DatabaseMetaData 数据库元数据对象调用 DatabaseMetaData 中的方法获取数据库元数据信息
-
JDBC 获取结果集元数据信息
PreparedStatement 预处理对象调用
getMetaData()
, 获取的是 ResultSetMetaData , 结果集元数据对象调用 ResultSetMetaData 中的方法获取结果集元数据信息