Java的JDBC连接池底层封装

2023-12-13 14:39:07

目录

一、JDBC回顾

1.1 六个基本步骤

1.2 问题的引出

二、ORM框架

三、JDBC连接池性能优化

3.1 JDBC连接池在创建连接时优化

3.2 将连接(桥梁)和状态进行绑定

1、MyConnection

3.3 设计连接池用于装连接

1、ConnectionPool

四、JDBC连接池性能的测试

五、将JDBC整体优化

5.1 问题的产生

5.2 问题解决方案

1、单例的连接池对象

2、添加配置文件

3、多个人同时访问

4、防止多余的线程出现null

5、代理模式

(1)静态代理模式

(2)缺省适配器模式

5.3 完整的JDBC封装代码

1、MyConnection

2、ConnectionPool

3、ConfigReader

4、SystemBusyException

5、TestThread

6、TestThreadMain


一、JDBC回顾

1.1 六个基本步骤

package test;
?
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
?
public class TestMain {
    public static void main(String[] args) throws Exception {
        //JDBC全称----JAVA DataBase Connectivity
        //1.导包
        //2.加载驱动类
        Class.forName("com.mysql.cj.jdbc.Driver");
        //3.获取连接,只是一个桥梁,负责让驱动与数据库连接
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbctest?useSSL=false","root","12345678");
        //4.获取状态参数(流)
        //PreparedStatement预处理流
        PreparedStatement pstat = connection.prepareStatement("select * from users");
        //5.执行操作
        //增删改---executeUpdate()
        //查询---executeQuery()
        //ResultSet底层---Set<Map<String,Object>>
        ResultSet rs = pstat.executeQuery();
        //6.访问下一个rs中的数据,遍历
        while(rs.next()) {
            System.out.print(rs.getInt("id"));
            System.out.print(rs.getString("uname"));
            System.out.print(rs.getString("psw"));
            System.out.print(rs.getString("email"));
            System.out.print(rs.getDate("birthday"));
            System.out.print(rs.getDouble("sal"));
            System.out.println();
        }
        //7.关闭资源
        rs.close();
        pstat.close();
        connection.close();
    }
?
}

1.2 问题的引出

1、JDBC代码流程写在哪个层次中?----DAO数据读写持久化层

2、DAO层中通常的方法什么样子?----对于数据库的增删改查操作

3、DAO方法中该写什么样的代码?----纯粹的JDBC流程 + SQL执行语句

4、DAO层次中的每一个方法里都存在JDBC流程(6步),流程大体上是一样的。所以DAO层次中的代码冗余问题就很多。------封装,也就是(ORM框架)

5、我们所说的JDBC连接池封装,其实是对我们现在的流程进行优化;我们现在所写的六个基本步中是存在很多问题的;而我们最后学的将整个JDBC封装是一个ORM框架;

二、ORM框架

1、ORM框架(Object Relationship Mapping对象关系映射)

(1)对象---domain

(2)关系---对应(映射)

2、ORM框架封装以后的最终效果,我们不用写JDBC流程了,只写SQL + 那些问号值;我们DAO层次中的方法没有执行体,只有方法的壳 + 注解(写SQL)

3、在ORM框架中讲解。

三、JDBC连接池性能优化

3.1 JDBC连接池在创建连接时优化

1、我们发现每次运行TestMain中的JDBC六步时都会有几秒卡顿,要进行优化

package test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class TestMain {
    public static void main(String[] args) throws Exception {
        //JDBC全称----JAVA DataBase Connectivity
        //1.导包
        //2.加载驱动类
        long t1 = System.currentTimeMillis();
        Class.forName("com.mysql.cj.jdbc.Driver");
        //3.获取连接,只是一个桥梁,负责让驱动与数据库连接
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbctest?useSSL=false","root","12345678");
        long t2 = System.currentTimeMillis();
        //4.获取状态参数(流)
        //PreparedStatement预处理流
        PreparedStatement pstat = connection.prepareStatement("select * from users");
        //5.执行操作
        //增删改---executeUpdate()
        //查询---executeQuery()
        //ResultSet底层---Set<Map<String,Object>>
        ResultSet rs = pstat.executeQuery();
        //6.访问下一个rs中的数据,遍历
        while(rs.next()) {
            System.out.print(rs.getInt("id"));
            System.out.print(rs.getString("uname"));
            System.out.print(rs.getString("psw"));
            System.out.print(rs.getString("email"));
            System.out.print(rs.getDate("birthday"));
            System.out.print(rs.getDouble("sal"));
            System.out.println();
        }
        //7.关闭资源
        rs.close();
        pstat.close();
        connection.close();
        long t3 = System.currentTimeMillis();
        System.out.println(t2-t1);//794
        System.out.println(t3-t2);//127
    }

}

2、我们认为t1 和 t2 之间的时间是创建连接的时间,t2 和 t3 之间的时间是整个执行的时间

3、发现毫秒差很大,原因:

(1)DAO层的代码冗余问题

(2)JDBC流程中有一个性能卡顿,是在创建连接这个地方(由于每次进行数据操作都要去创建连接,每操作一次数据库,就会创建一次连接)

(3)解析:这个Connection就好比是一个桥梁,假如有5个人要过桥,但是此时并没有桥,每一个人都会先创建一个桥,然后过去之后再把桥炸了,然后第二个人想过桥会在创建一个桥过去之后在把桥炸了;所以每个人都会有创建桥的这个过程,里面增加了很多冗余。

4、如何提高创建连接的性能?

(1)假设我们创建5个连接,5个连接不销毁,留着复用。5个连接存在哪里?---集合(也就是连接池),还需要对集合进行管理

(2)需要将每一个连接身上绑定一个状态,连接类不是我们自己写的(Connection不是我们自己写的类),所以我们要创建一个新对象来将连接绑定状态。

3.2 将连接(桥梁)和状态进行绑定

1、MyConnection

1、MyConnection:描述一个真实连接和一个状态的关系,将一个真实可用连接和一个状态封装(绑定)在一起形成一个新的对象

(1)我们首先要在MyConnection类中创建连接和状态,最好是属性,创建属性的目的是为了描述对象的特征和状态;

//创建连接
private Connection conn;
//创建状态(false表示连接空闲--可用,true表示连接已被其他人占用--不可用)默认false;
private boolean used = false;

(2)上面创建了两个属性,发现conn连接这个属性它是没有完成连接操作的。所以我们创建一个普通块,目的是为了给我们创建的conn赋予一定的能力,就是为了我们每次new MyConnection的时候,能得到这个连接,这里的连接指的是让Java代码与数据库连接(桥梁);

{
        //我们为什么不直接private Connection conn = DriverManager.getConnection();因为 DriverManager.getConnection();是有异常的
        //这两行代码不能给属性,因为属性是不能抛异常的,而且属性这一行无法完成
        Class.forName("");
        conn = DriverManager.getConnection();
}

(3)那么问题来了,属性不是一个,因为每次new 都会创建新的属性,然后完成一次类加载,然后在创建桥梁,可是我们的类加载只需创建一次就够了啊,所以还需要创建一个静态块,目的为了让类加载执行一次

static {
        //由于每new一次就执行一次普通块,所以把类加载放在静态块中
        Class.forName("");
	}

(4)连接需要获取get,让外界在创建对象时可以获取MyConnection对象中的连接(桥梁)getConn,让用户能获取到这个桥梁;used状态需要来回切换,所以还有set/get方法

 //让外界能获取到连接(桥梁)
    public Connection getConn() {
        return conn;
    }
	//used状态需要来回切换
    public boolean isUsed() {//boolean的get方法,名字是is开头的---规约
        return used;
    }
    public void setUsed(boolean used) {
        this.used = used;
    }

(6)完整版

package pool;

import java.sql.Connection;
import java.sql.DriverManager;

/**
 * @Description: TODO 描述一个真实连接和一个状态的关系
*                     将一个真实可用连接(桥梁)和一个状态封装在一起形成一个新的对象MyConnection
 * @Author: 曹宇希
 * @Date: 2023/11/30 12:20
 * @Version: 1.0
 * @Company: 版权所有
 */
public class MyConnection {
    private static String className = "com.mysql.cj.jdbc.Driver";
    private static String url = "jdbc:mysql://localhost:3306/jdbctest?serverTimezone=GMT%2B8";
    private static String user = "root";
    private static String password = "12345678";

    //连接
    private Connection conn;
    //状态(false表示连接空闲--可用,true表示连接已被其他人占用--不可用)默认false
    private boolean used = false;

    //一个静态块,目的为了让类加载执行一次

文章来源:https://blog.csdn.net/weixin_55415300/article/details/134902485
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。