欢迎访问shiker.tech

请允许在我们的网站上展示广告

您似乎使用了广告拦截器,请关闭广告拦截器。我们的网站依靠广告获取资金。

java如何连接数据库?
(last modified Apr 17, 2023, 11:52 AM )
by
侧边栏壁纸
  • 累计撰写 176 篇文章
  • 累计创建 61 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

java如何连接数据库?

橙序员
2023-03-26 / 0 评论 / 0 点赞 / 445 阅读 / 1,607 字 / 正在检测百度是否收录... 正在检测必应是否收录...
文章摘要(AI生成)

本文旨在说明:如何使用java连接数据库?jdk又是如何规范各个数据库驱动实现的?执行sql需要做哪些过程?在jdk自带的jre中,我们可以在java.sql.*中看到一些关于sql相关的接口和工具类:这些类都为我们做了什么呢?假如你来出一个需求。。。如果你是技术,来不依赖任何包实现查询mysql数

本文旨在说明:

  1. 如何使用java连接数据库?
  2. jdk又是如何规范各个数据库驱动实现的?
  3. 执行sql需要做哪些过程?

在jdk自带的jre中,我们可以在java.sql.*中看到一些关于sql相关的接口和工具类:

image-1680615704369

这些类都为我们做了什么呢?

假如你来出一个需求。。。

如果你是技术,来不依赖任何包实现查询mysql数据库,那么在你的技术方案中大概要实现以下几点:

  1. 连接mysql服务端(如:mysql -h 182.167.12.3 -u user1 -p -P 3308
  2. 执行我们构建好的命令创建数据库连接
  3. 在创建好的连接上执行sql
  4. 使用对象解析sql返回结果,创建结果集

上面这4步只是面向查询mysql数据库的过程,提出的4个实现步骤。

面向对象的实现

把上述4个步骤使用面向对象进行实现,我们首先要把上述的过程中进行对象示例化,面向对象一个很重要的思维就是:

我吃饭了 = 【发起对象:我】【执行器:吃】【目标对象:饭】

所以对于我们查询mysql数据库的这4步,我们也可以拆分为

  1. 构建【数据库驱动】,保存数据库连接信息
  2. 使用【数据库驱动】创建【数据库连接】
  3. 在创建好的【数据库连接】上,通过【sql执行器】执行sql
  4. 使用对象解析sql返回结果,创建【结果集】

基于上述过程中的对象分析,我们也可以定义出各个对象的职责:

  1. 【数据库驱动】:

    1. 保存数据库连接信息
    2. 提供创建数据连接的能力
  2. 【数据库连接】:

    1. sql相关的事务管理
    2. 连接的关闭超时设置
    3. 创建sql执行器
  3. 【sql执行器】:

    1. 执行sql语句
  4. 【结果集】:

    1. 解析sql返回结果

看JDK如何封装

既然我们已经清楚各个对象的职责,那么还需分析,哪些对象是可拓展的,哪些对象是过程对象无须拓展。针对上述我们梳理好的割割对象,可以看到【数据库驱动】、【数据库连接】、【sql执行器】都是可拓展对象,针对不同的数据库,完全可以有不同的实现。而不管对于何种数据库,根据驱动创建连接都是一个固定过程,所以jdk将根据驱动创建连接封装为一个【驱动管理器】

我们新抽象的【驱动管理器】职责如下:

  1. 维护驱动注册表,管理驱动的注册注销
  2. 使用数据库驱动创建数据库连接

为了增强我们sql执行器的拓展性,JDK又给我们的sql执行器划分成三种:

  1. 静态sql执行器,适合执行没有参数的sql语句
  2. 预占用sql执行器,适合执行有参数的sql语句
  3. 存储过程执行器,用来执行存储过程

所以对上述执行过程进行总结,我们可以得到如下过程:

image-20230401004144234

结合源码我们可以对照:

名称 源码类名 类型
驱动管理器 java.sql.DriverManager
数据库驱动 java.sql.Driver 接口
数据库连接 java.sql.Connection 接口
静态sql执行器 java.sql.Statement 接口
预占用sql执行器 java.sql.PreparedStatement 接口
存储过程执行器 java.sql.CallableStatement 接口
结果集 java.sql.resultSet 接口

其他框架如何进一步封装

针对数据库驱动:有对应的mysql、oracle、sql server等数据库驱动的实现

针对数据库连接:数据库连接又可以通过池化,使用连接池技术我们又有C3P0、Druid、DBCP等数据库连接池

针对执行器和结果集:这方面我们可以通过持久化技术,例如mybatis、hibernate、JPA等持久层框架进一步封装

拓展后我们可以得到如下结构图:

image-1680615704369

加亿点点技术~

类加载阶段注册驱动

注册驱动这一步对用户使用来说至关重要,如果我们能够让用户在调用驱动时就能完成驱动注册那就再好不过,如何能够让用户在调用驱动时就完成驱动注册呢?

这里我们可以通过构造方法和静态块来实现,但java规定必须使用后者来完成驱动的创建及注册:

  public class Driver extends NonRegisteringDriver implements java.sql.Driver {
  	static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }
    
    public Driver() throws SQLException {
        // Required for Class.forName().newInstance()
    }
}

这是为了在类加载的时候就完成驱动注册,所以我们可以得到一个很重要的结论:数据库驱动的加载是在类加载阶段就完成的

这样,用户只要通过Class.forName()加载对应的驱动类,就能完成数据库驱动的注册。

socket编程

第二点,基于java socket编程,连接数据库

    public <T extends Closeable> T connect(String hostname, int portNumber, PropertySet pset, int loginTimeout) throws IOException {

        this.loginTimeoutCountdown = loginTimeout;

        if (pset != null) {
            this.host = hostname;
            this.port = portNumber;

            String localSocketHostname = pset.getStringProperty(PropertyKey.localSocketAddress).getValue();
            InetSocketAddress localSockAddr = localSocketHostname != null && localSocketHostname.length() > 0
                    ? new InetSocketAddress(InetAddress.getByName(localSocketHostname), 0)
                    : null;
            int connectTimeout = pset.getIntegerProperty(PropertyKey.connectTimeout).getValue();

            if (this.host != null) {
                InetAddress[] possibleAddresses = InetAddress.getAllByName(this.host);

                if (possibleAddresses.length == 0) {
                    throw new SocketException("No addresses for host");
                }

                // save last exception to propagate to caller if connection fails
                SocketException lastException = null;

                // Need to loop through all possible addresses. Name lookup may return multiple addresses including IPv4 and IPv6 addresses. Some versions of
                // MySQL don't listen on the IPv6 address so we try all addresses.
                for (int i = 0; i < possibleAddresses.length; i++) {
                    try {
                        this.rawSocket = createSocket(pset);

                        configureSocket(this.rawSocket, pset);

                        InetSocketAddress sockAddr = new InetSocketAddress(possibleAddresses[i], this.port);
                        // bind to the local port if not using the ephemeral port
                        if (localSockAddr != null) {
                            this.rawSocket.bind(localSockAddr);
                        }

                        this.rawSocket.connect(sockAddr, getRealTimeout(connectTimeout));

                        break;
                    } catch (SocketException ex) {
                        lastException = ex;
                        resetLoginTimeCountdown();
                        this.rawSocket = null;
                    }
                }

                if (this.rawSocket == null && lastException != null) {
                    throw lastException;
                }

                resetLoginTimeCountdown();

                this.sslSocket = this.rawSocket;
                return (T) this.rawSocket;
            }
        }

        throw new SocketException("Unable to create socket");
    }
0

评论区