每日一篇JavaJava 杂谈java 成神之路

JDBC基础(一)

2018-08-13  本文已影响38人  sixleaves

JDBC

什么是JDBC

今天我来介绍下JDBC. JDBC英文全程Java Database Connectivity.是一个独立于特定DBMS(数据库管理系统), 通用的SQL数据库存储和操作的公共接口集合。该接口定义在java.sqljavax.sql下。
说白了JDBC是一套接口, 规定了访问数据库的规范和标准

JDBC的好处

JDBC

JDBC体系结构

JDBC接口包括两个层次

JDBC驱动程序的分类

第二类
第三类
  • 这种驱动采用中间件的应用服务器来访问数据库。应用服务器作为一个到多个数据库的网关,客户端通过它间接联接数据库。
  • 应用服务器通常有自己的网络协议.Java程序通过JDBC驱动程序将JDBC调用发送给应用服务器, 应用服务器使用本地程序驱动访问数据, 从而完成请求。
    例子:举个例子, 比如阿里云提供了访问数据库服务器的接口, 这就是阿里云厂商提供的本地API, 而应用服务器者是买了阿里云的第三方自己去订制开发, 根据需求可以自己定义一套通信协议,并实现JDBC驱动来间接的调用阿里云
  • 数据库厂商已经提供了网络协议, 用来约定客户端程序通过网络直接与数据库通信
  • 这类驱动完全使用Java编写, 通过与数据库建立的Socket连接, 采用具体的网络协议, 把JDBC调用转换为直接连接的网络调用
    第四类

第三和第四种都是走的是网络.
不同的是前者是通过数据库网关间接和数据库通信, 而不同的数据库网关可能有不同的协议, 后者是直接和数据库建立网络连接, 进行远程接口调用。
第二种走的是数据库本地API, 相当于提供头文件和库直接给你调用.扩展性不好,对于不同的语言就需要提供另外一套的接口,没有走网络协议的兼容性好
第一种的Windows提供的已经淘汰忽略.

JDBC的API构架

JDBC API是一套接口集合.约定了应用程序能够进行数据库联接、执行SQL语句、并且得到返回结果。

JDBC的API构架

Driver 接口

常用的数据库驱动

JDBC建立和数据库通信和交互的步骤

引入驱动包,这里我使用Maven引入

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>

加载和注册驱动

一、通过Class.forName

通过反射直接使用Class.forName方式进行加载和注册.

Class.forName("com.mysql.jdbc.Driver");
原理

通过Class.forName直所以能够加载注册驱动, 是因为Mysql驱动的实现类,使用了静态代码块, 并在静态代码块中使用驱动管理器(DriverMangaer)进行驱动的注册。如下是Mysql驱动实现的代码


package com.mysql.jdbc;

import java.sql.DriverManager;
import java.sql.SQLException;

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}
二、通过DriverManager自动进行加载和注册.

在JDK6.0, JDBC 4.0之后实际上我们不需要再调用Class.forName来加载驱动程序了, 我们只需要把驱动的jar包放到工程的类加载路径里,那么驱动就会被自动加载.

    public static Connection getConn() throws ClassNotFoundException, SQLException {
        Connection conn = null;
        String url = config.getProperty(JDBCUtil.URL_KEY);
        conn = DriverManager.getConnection(
                    url,
                    config);
        return conn;
    }
原理
注意

Web开发中我们还需要加入Class.forName.

    public static Connection getConn() throws ClassNotFoundException, SQLException {
        Connection conn = null;
        String url = config.getProperty(JDBCUtil.URL_KEY);
        Class.forName(config.getProperty(JDBCUtil.DRIVE_KEY));
        conn = DriverManager.getConnection(
                    url,
                    config);
        return conn;
    }

建立连接

一旦加载完驱动, 我们就可以通过驱动和数据库服务器建立远程通信联接, 并获取到该联接对象, 使用如下代码.也可以使用Class.forName的方式,通过反射, 创建Driver对象,并进行注册

    public static Connection getConn() throws ClassNotFoundException, SQLException {
        Connection conn = null;
        String url = config.getProperty(JDBCUtil.URL_KEY);
        Class.forName(config.getProperty(JDBCUtil.DRIVE_KEY));
        conn = DriverManager.getConnection(
                    url,
                    config);
        return conn;
    }

获取执行体对象或其子对象

我们可以通过联接对象,来获取执行体对象.通过执行体对象来执行sql语句.一般我们都用预加载执行体对象PrepareStatement, 而不不用其父类Statement对象.主要原因在于Statement对象对于存在sql注入的,没有对sql语句进行严格的过滤.

    @Test
    public void exer1() {

        // 创建Customer表, 包含id, name, age, gender,birthday。插入数据

        Connection conn = null;
        PreparedStatement ps = null;
        StringBuilder sb = new StringBuilder();
        try {
            conn = JDBCUtil.getConn();

            sb.append("create table if not exists Customer (id int auto_increment, name varchar(20),").
                    append("age int, gender varchar(3) not null,").
                    append("birthdate date,").
                    append("primary key(id)").append(") engine innodb charset utf8");

            System.out.println(sb);
            PreparedStatement preparedStatement = conn.prepareStatement(sb.toString());
            preparedStatement.execute();

            sb = new StringBuilder();
            sb.append(" insert into Customer (name, age, gender, birthdate) ");
            sb.append(" values (?,?,?,?) ");
            preparedStatement = conn.prepareStatement(sb.toString());

            preparedStatement.setObject(1, "SweetCS");
            preparedStatement.setObject(2, 26);
            preparedStatement.setObject(3, "男");
            preparedStatement.setObject(4,  LocalDate.of(1992,1,1).toString());
            System.out.println(preparedStatement);
            preparedStatement.execute();



        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

执行查询

调用执行体对象的executeQuery()方法或者executeUpdate()执行sql。

释放资源

        Connection conn = null;
        String url = "jdbc:mysql://127.0.0.1:3306/jdbc";
        try {
            conn = DriverManager.getConnection(url, "root", "admin");
            System.out.println(conn);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {

            if (null != conn) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

        }

JDBCUtil封装(版本一)

只要思路在于,方便后续进行数据库联接。如果我们使用JDBC除了每次肯定要进行的操作主要有四步.

除了第三点之外, 其他三个可以说是完全一样的操作,对于重复代码我们要进行封装.


import java.io.IOException;
import java.sql.*;
import java.util.Properties;

public class JDBCUtil {

    static Properties config = null;
    private static final String URL_KEY = "url";
    private static final String DRIVER_CLASS_NAME_KEY = "driverClassName";
    private static String configFileName = "config.properties";

    public static String getConfigFileName() {
        return configFileName;
    }

    public static void setConfigFileName(String configFileName) {
        JDBCUtil.configFileName = configFileName;
    }

    static {
        config = new Properties();
        try {
            config.load(JDBCTest.class.getResourceAsStream(configFileName));
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static Connection getConn() throws ClassNotFoundException, SQLException {
        Class.forName(config.getProperty(DRIVER_CLASS_NAME_KEY));
        String url = config.getProperty(JDBCUtil.URL_KEY);
        Connection conn = DriverManager.getConnection(
                    url,
                    config);
        return conn;
    }

    public static void close(Connection conn) {
        if (null != conn) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public static void close(Connection conn, Statement stmt) {
        try {
            if (null != stmt) {
                stmt.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(conn);
        }
    }

    public static void close(Connection conn, Statement stmt, ResultSet rs) {
        try {
            if (null != rs) {
                rs.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(conn, stmt);
        }

    }

}
config.properties

总结

Statement产生注入的演示和PreparedStatement的防护

PreparedStatement的预编译性能测试

Statement和PreparedStatement的区别

Class.forName为何能加载驱动

JDBC操作口诀

JDBC释放资源的异常处理顺序

上一篇下一篇

猜你喜欢

热点阅读