一.数据库连接池概念
什么是连接池? 数据库连接池(Database Connection Pool,简称DBCP)是一种数据库连接资源的缓存或池化技术,用于提高数据库操作的效率和性能。
实际开发中“获得连接”或“释放资源”是非常消耗系统资源的两个过程,为了解决此类性能问题,通常情况我们 采用连接池技术,来共享连接Connection。这样我们就不需要每次都创建连接、释放连接了,这些操作都交 给了连接池.
连接池的好处:
用池来管理Connection,这样可以重复使用Connection。 当使用完Connection后,调用Connection的 close()方法也不会真的关闭Connection,而是把Connection“归还”给池
JDBC与数据库连接池的区别:
普通JDBC
数据库连接池方式
如何使用数据库连接池:
Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。 这样应用程序可以方便的切换不同厂商的连接池! 常见的连接池有 DBCP连接池, C3P0连接池, Druid连接池
1.1常见配置项
最大空闲时间:
最大空闲连接数是指连接池中允许保持空闲状态的最大连接数。当连接池中的空闲连接数超过这个值时,连接池可能会关闭一些多余的连接,以释放数据库资源。这个参数的设置需要根据数据库服务器的性能、内存资源以及应用程序的并发需求来综合考虑。
最小空闲连接:
最小空闲连接数是指连接池中始终保持打开状态的最小连接数。这个设置确保即使在低负载时,连接池也维持一定数量的活跃连接,以便快速响应请求,减少新建立连接的开销。
二.DBCP连接池
1.2 编写DBCP连接池
package com.shujuchilianjieku.utils;
import org.apache.commons.dbcp.BasicDataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class DBCPUtils {
//1.定义常量 保存数据库连接的相关信息
public static final String DRIVERNAME = "com.mysql.jdbc.Driver";
public static final String URL = "jdbc:mysql://localhost:3306/db5?characterEncoding=UTF-8";
public static final String USERNAME = "root";
public static final String PASSWORD = "123123";
//2.创建连接池对象 (有DBCP提供的实现类)
public static BasicDataSource dataSource = new BasicDataSource();
//3.使用静态代码块进行配置
static{
dataSource.setDriverClassName(DRIVERNAME);
dataSource.setUrl(URL);
dataSource.setUsername(USERNAME);
dataSource.setPassword(PASSWORD);}
//4.获取连接的方法
public static Connection getConnection() throws SQLException {
//从连接池中获取连接
Connection connection = dataSource.getConnection();
return connection;
}
//5.释放资源方法
public static void close(Connection con, Statement statement){
if(con != null && statement != null){
try {
statement.close();
//归还连接
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection con, Statement statement, ResultSet resultSet){
if(con != null && statement != null && resultSet != null) {
try {
resultSet.close();
statement.close();
//归还连接
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
1.3 测试DBCP连接池
package com.shujuchilianjieku.utils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestDBCP {
/*
* 测试DBCP连接池
* */
public static void main(String[] args) throws SQLException {
//1.从DBCP连接池中拿到连接
Connection con = DBCPUtils.getConnection();
//2.获取Statement对象
Statement statement = con.createStatement();
//3.查询所有员工的姓名
String sql = "select ename from employee";
ResultSet resultSet = statement.executeQuery(sql);
//4.处理结果集
while(resultSet.next()){
String ename = resultSet.getString("ename");
System.out.println("员工姓名: " + ename);
}
//5.释放资源
DBCPUtils.close(con,statement,resultSet);
}
}
三.C3P0连接池
C3P0是一个开源的JDBC连接池,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate、 Spring等
1.2 编写C3P0工具类
package com.shujuchilianjieku.utils;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.mysql.jdbc.Statement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
public class C3P0Utils {
//1.创建连接池对象 C3P0对DataSource接口的实现类
//使用的配置是 配置文件中的默认配置
//public static ComboPooledDataSource dataSource = new ComboPooledDataSource();
//使用指定的配置
public static ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");
//获取连接的方法
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
//释放资源
public static void close(Connection con, Statement statement){
if(con != null && statement != null){
try {
statement.close();
//归还连接
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection con, Statement statement, ResultSet resultSet){
if(con != null && statement != null && resultSet != null){
try {
resultSet.close();
statement.close();
//归还连接
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
1.3 测试C3P0工具类
package com.shujuchilianjieku.utils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
public class TestC3P0 {
//需求 查询姓名为李白的 记录
public static void main(String[] args) throws SQLException {
//1.获取连接
Connection con = C3P0Utils.getConnection();
//2.获取预处理对象
String sql = "select * from employee where ename = ?";
PreparedStatement ps = con.prepareStatement(sql);
//3.设置占位符的值
ps.setString(1,"李白");
ResultSet resultSet = ps.executeQuery();
//4.处理结果集
while(resultSet.next()){
int eid = resultSet.getInt("eid");
String ename = resultSet.getString("ename");
int age = resultSet.getInt("age");
String sex = resultSet.getString("sex");
double salary = resultSet.getDouble("salary");
Date date = resultSet.getDate("empdate");
System.out.println(eid +" " + ename + " " + age +" " + sex +" " + salary +" "
+date);
}
//5.释放资源
C3P0Utils.close(con,ps,resultSet);
}
}
四.Druid连接池
1.4编写Druid工具类
通过工厂来来获取 DruidDataSourceFactory类的createDataSource方法
createDataSource(Properties p) 方法参数可以是一个属性集对象
编写Druid工具类代码:
package com.shujuchilianjieku.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class DruidUtils {
//1.定义成员变量
public static DataSource dataSource;
//2.静态代码块
static{
try {
//3.创建属性集对象
Properties p = new Properties();
//4.加载配置文件 Druid 连接池不能够主动加载配置文件 ,需要指定文件
InputStream inputStream = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");
//5. 使用Properties对象的 load方法 从字节流中读取配置信息
p.load(inputStream);
//6. 通过工厂类获取连接池对象
dataSource = DruidDataSourceFactory.createDataSource(p);
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接的方法
public static Connection getConnection(){
try {
return dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
//获取Druid连接池对象的方法
public static DataSource getDataSource(){
return dataSource;
}
//释放资源
public static void close(Connection con, Statement statement){
if(con != null && statement != null){
try {
statement.close();
//归还连接
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection con, Statement statement, ResultSet resultSet){
if(con != null && statement != null && resultSet != null){
try {
resultSet.close();
statement.close();
//归还连接
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
1.5 测试Druid工具类
package com.shujuchilianjieku.utils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestDruid {
// 查询 薪资在3000到5000之间员工的姓名
public static void main(String[] args) throws SQLException {
//1.获取连接
Connection con = DruidUtils.getConnection();
//2.获取Statement对象
Statement statement = con.createStatement();
//3.执行查询
ResultSet resultSet = statement.executeUpdate("select ename from employee where salary between 3000 and 5000");
//4.处理结果集对象
while(resultSet.next()){
resultSet.getString("ename");
System.out.println(ename);
}
//5.释放资源
DruidUtils.close(con,statement,resultSet);
}
}
五.DBUtils工具类介绍
使用JDBC我们发现冗余的代码太多了,为了简化开发 我们选择使用 DbUtils Commons DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程 序的开发,同时也不会影响程序的性能。
DBUtils核心功能
- QueryRunner 中提供对sql语句操作的API.
- ResultSetHandler接口,用于定义select操作后,怎样封装结果集.
- DbUtils类,他就是一个工具类,定义了关闭资源与事务处理相关方法
1.6 JavaBean组件
JavaBean 就是一个类, 开发中通常用于封装数据,有以下特点
- 需要实现 序列化接口, Serializable (暂时可以省略)
- 提供私有字段: private 类型 变量名;
- 提供 getter 和 setter
- 提供 空参构造
创建Employee类和数据库的employee表对应
public class Employee implements Serializable{
//成员变量的名称 与 表中的列要一样
private int eid;
private String ename;
private int age;
private String sex;
private double salary;
private Date empdate;
1.7 QueryRunner核心类以及创建
创建:
QueryRunner核心类实现对数据库增删改操作:
package com.shujuchilianjieku.utils.testDBUtils;
import com.shujuchilianjieku.utils.DruidUtils;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.dbutils.QueryRunner;
import java.sql.Connection;
import java.sql.SQLException;
/**
* 使用QueryRunner对象 完成增删改
* update(Connection con,String sql,Object... param)方法
*/
public class DBUtilsDemo02 {
public static void main(String[] args) {
try {
new DBUtilsDemo02().testDelete();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void testInsert() throws SQLException {
// 1. 创建QueryRunner 手动模式创建
QueryRunner qr = new QueryRunner();
// 2. 编写占位符方式 SQL
String sql = "insert into employee values(?,?,?,?,?,?)";
//3.设置占位符的参数
Object[] param = {null,"张百万",20,"女",10000,"1990-12-16"};
//4.执行update方法
Connection con = DruidUtils.getConnection();
int i = qr.update(con, sql, param);
//5.关闭连接(释放资源)
// con.close();
DbUtils.closeQuietly(con);
}
//修改操作(修改姓名为张百万员工的工资为15000)
public void testUpdate() throws SQLException {
//1.创建核心类(自动模式) 需要传递 数据库连接池对象
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
//2.编写SQL
String sql = "update employee set salary = ? where ename = ?";
//3.设置占位符的参数
Object[] param = {15000,"张百万"};
//4.执行修改操作 自动模式不需要传入connection对象
qr.update(sql,param);
}
//删除操作(删除id为1的记录)
public void testDelete() throws SQLException {
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
String sql = "delete from employee where eid = ?";
//如果只有一个参数不需要创建数组
qr.update(sql,1);
}
}
1.8 QueryRunner查询操作
ResultSerHandler接口介绍:
ResultSetHandler可以对查询出来的ResultSet结果集进行处理,达到一些业务上的需求。
本例展示的是使用ResultSetHandler接口的几个常见实现类实现数据库的增删改查,可以大大减少代码量,优化 程序。
ArrayHandler类实现:
public void testFinById() throws SQLException {
//1.创建QueryRunner(自动模式)
QueryRunner qr =new QueryRunner(DruidUtils.getDataSource());
//2.编写SQL
String sql="select * from employee where eid =?";
//3.执行查询
Object[] query = qr.query(sql, new ArrayHandler(),5);
//4.获取数据
System.out.println(Arrays.toString(query));
}
ArrayListHandler实现:
public void testFindAll() throws SQLException {
//1.创建QueryRunner
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
//2.编写SQL
String sql = "select * from employee";
//3.执行查询
List<Object[]> query = qr.query(sql, new ArrayListHandler());
//4.遍历数组获取数据
for (Object[] objects : query) {
System.out.println(Arrays.toString(objects));
}
BeanHandler实现
public void testFindByIdJavaBean() throws SQLException {
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
String sql = "select * from employee where eid = ?";
Employee employee = qr.query(sql, new BeanHandler<Employee>(Employee.class), 3);
System.out.println(employee);
}
BeanListHandler实现:
public void testFindBySalary() throws SQLException {
QueryRunner qr =new QueryRunner(DruidUtils.getDataSource());
String sql = "select * from employee where salary > ?";
List<Employee> list = qr.query(sql, new BeanListHandler<Employee>(Employee.class), 3000);
for (Employee employee : list) {
System.out.println(employee);
}
MapHandler实现:
public void testFindByname() throws SQLException {
QueryRunner qr =new QueryRunner(DruidUtils.getDataSource());
String sql = "select * from employee where ename = ?";
Map<String, Object> map = qr.query(sql, new MapHandler(), "张百万");
Set<Map.Entry<String,Object>> entries=map.entrySet();
for (Map.Entry<String, Object> entry : entries) {
//打印结果
System.out.println(entry.getKey()+"="+entry.getValue());
}
ScalarHandler实现:
public void testCount() throws SQLException {
QueryRunner qr =new QueryRunner(DruidUtils.getDataSource());
String sql = "select sum(salary) from employee";
Double sum= (Double)qr.query(sql, new ScalarHandler<>());
System.out.println("员工薪资总额:"+sum);
}
六.数据库批处理
什么是批处理
批处理(batch) 操作数据库 批处理指的是一次操作中执行多条SQL语句,批处理相比于一次一次执行效率会提高很多。 当向数据库中添加大量的数据时,需要用到批处理
用代码实现批处理
package com.shujuchilianjieku.utils.testbatch;
import com.shujuchilianjieku.utils.DruidUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Batchinsert {
//使用批处理 向表中添加10000条数据
public static void main(String[] args) throws SQLException {
//1.获取连接
Connection connection = DruidUtils.getConnection();
//2.获取预处理对象
PreparedStatement ps = connection.prepareStatement("insert into testBatch(uname) values(?)");
//3.执行批量插入操作
for (int i = 0 ; i<10000 ; i++){
ps.setString(1,"小强" +1);
// 将SQL添加到批处理列表
ps.addBatch();
}
//添加时间戳 测试执行效率
long start = System.currentTimeMillis();
//4.统一执行批量插入操作
ps.executeBatch();
// 结束时间
long end = System.currentTimeMillis();
System.out.println("插入10000条数据需要使用:"+(end-start)+"毫秒!");
//5. 关闭连接
DruidUtils.close(connection,ps);
}
}
七.MySql元数据
1.1 元数据基本概念
元数据查询代码:
SHOW STATUS;
-- 2 .查看MySql的版本信息
SELECT VERSION();
-- 3.查询表中的详细信息
SHOW COLUMNS FROM employee;
-- 4.显示数据库表的详细索引信息
SHOW INDEX FROM testBatch;
-- 5.列出所有数据库
SHOW DATABASES;
-- 6.显示当前数据库的所有表
SHOW TABLES;
-- 7.获取当前的数据库名
SELECT DATABASE();
2.2 使用JDBC 获取元数据
获取数据库URL:
package com.shujuchilianjieku.utils.testmetadata;
import com.shujuchilianjieku.utils.DruidUtils;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
public class TestMetaDate {
public static void main(String[] args) {
try {
new TestMetaDate().testDataBaseMetaData();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
//1.获取数据库相关的元数据信息 使用DatabaseMetaData
public void testDataBaseMetaData() throws SQLException {
//1.获取数据库连接对象 connection
Connection connection = DruidUtils.getConnection();
//2.获取代表数据库 的元数据对象 DatabaseMetaData
DatabaseMetaData databaseMetaData = connection.getMetaData();
//3.获取数据库相关的元数据信息
String url = databaseMetaData.getURL();
System.out.println("数据库URL:"+url);
connection.close();
}
}
获取当前用户:
String userName = databaseMetaData.getUserName();
System.out.println("当前用户:"+userName);
数据库产品名:
String productName = databaseMetaData.getDatabaseProductName();
System.out.println("数据库产品名:"+productName);
数据的版本号
String version = databaseMetaData.getDriverVersion();
System.out.println("数据库版本:"+version);
2.3 使用JDBC获取结果集元数据信息
使用ResultSetMetaData方法
代码示例:
public void testResultSetMetaData() throws SQLException {
//1.获取连接
Connection con = DruidUtils.getConnection();
//2.获取预处理对象
PreparedStatement ps = con.prepareStatement("select * from employee");
ResultSet resultSet = ps.executeQuery();
//3.获取结果集元数据对象
ResultSetMetaData metaData = ps.getMetaData();
// 获取当前结果集共有多少列
int count = metaData.getColumnCount();
System.out.println("当前结果集中共有:"+count+"列");
// 获取指定列号的列名 参数是整数 从1开始
String Name = metaData.getColumnName(1);
System.out.println("1列的列名是:"+Name);
//获取指定列号列的类型 参数是整数 从1开始
String TypeName = metaData.getColumnTypeName(1);
System.out.println("类型:"+TypeName);
// 释放资源
DruidUtils.close(con,ps,resultSet);
}