JDBC深入

前言

  1. 大二上学期跟着学校里面的老师学JavaSE的时候写了一篇非常垃圾的JDBC的文章JDBC,那时候自认为自己对JDBC的认识已经很高了,认为它很简单,现在再回头看,发现自己那时候完全是井底之蛙,所以我觉得得再写一篇文章深入谈谈JDBC,算是给自己的黑历史抹白吧。
  2. 我之前写的那篇jdbc文章,使用statement操作sql语句会存在SQL注入问题,之后会再写一篇文件谈谈这个问题
  3. 鉴于之前写了关于MySQL事务机制的文章,之后,我也会谈谈JDBC事务机制的使用

JDBC本质

JDBC,本质上就是一套接口,是SUN公司制定的一套接口(interface),包名为:java.sql.*(这个软件包下有很多接口)

为什么面向接口编程

我们先来看看什么是面向接口编程:

接口都有调用者和实现者,面向接口调用、面向接口写实现类,这都属于面向接口编程。

为什么要面向接口编程?

  • 解耦合:
    • 降低程序的耦合度
    • 提高程序的扩展力。

多态机制就是非常典型的:面向抽象编程。(不要面向具体编程)

思考

现在思考:为什么SUN公司制定一套JDBC接口呢?

  1. 因为每一个数据库的底层实现原理都不一样。
  2. Oracle数据库有自己的原理。
  3. MySQL数据库也有自己的原理。
  4. MS SqlServer数据库也有自己的原理。

每一个数据库产品都有自己独特的实现原理,如果我们不写一套接口,那么我们这些程序员对同一套Java程序,需要准备几套面向不同数据库的Java代码,程序员会累死在重复且无意义的工作上。

而面向接口编程可以实现一个规范,从而让我们java程序员写一套java代码,适配不同的数据库,达到解放程序员的效果

三方模拟JDBC本质

我们扮演SUN公司,Java程序员,数据库厂商这三方来模拟一下JDBC

SUN公司

/*
SUN公司负责制定这套JDBC接口。
*/
public interface JDBC{
    /*
        连接数据库的方法。
    */
    void getConnection();
}

数据库厂商

数据库厂商对SUN公司制定出的接口的实现类,我们称之为驱动(Driver)

MySQL

/*
    MySQL的数据库厂家负责编写JDBC接口的实现类
*/
public class MySQL implements JDBC{

    public void getConnection(){
        // 具体这里的代码怎么写,对于我们Java程序员来说没关系
        // 这段代码涉及到mysql底层数据库的实现原理。
        System.out.println("连接MYSQL数据库成功!");
    }
}

Oracle数据库厂商

/*
    Oracle的数据库厂家负责编写JDBC接口的实现类
*/
public class Oracle implements JDBC{

    public void getConnection(){
        System.out.println("连接Oracle数据库成功!");
    }
}

Java程序员

显然,java程序员在操作MySQL数据库时,需要先把MySQL数据库厂商的对JDBC的实现类的jar包导入工程,也就是需要先把mysql驱动导入工程,不然接口的方法没有实现类,程序自然无法执行。

同理,java程序员在操作Oracle数据库时,也需要先导入Oracle驱动

/*
    Java程序员角色。
    不需要关心具体是哪个品牌的数据库,只需要面向JDBC接口写代码。
    面向接口编程,面向抽象编程,不要面向具体编程。
*/
import java.util.*;

public class JavaProgrammer
{
    public static void main(String[] args) throws Exception{
        // JDBC jdbc = new MySQL();
        // JDBC jdbc = new SqlServer();

        // 创建对象可以通过反射机制。
        ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
        String className = bundle.getString("className");
        Class c = Class.forName(className);
        JDBC jdbc = (JDBC)c.newInstance();

        // 以下代码都是面向接口调用方法,不需要修改
        jdbc.getConnection();
    }
}

三方之间的关系图

JDBC本质.jpg

JDBC编程六步法

第一步:注册驱动

  • 作用:告诉Java程序,即将要连接的是哪个品牌的数据库
Driver driver = new com.mysql.cj.jdbc.Driver(); // 使用多态,父类型引用指向子类型对象
DriverManager.registerDriver(driver);

上面代码可以合并为下面一行

DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());

注册驱动这里还可以进行代码优化,我们就在下面JDBC工具类,DBUtils里面进行

第二步:获取连接

  • 表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的操作,使用完之后一定要关闭通道

获取连接的url需要遵循通信协议

  • 通信协议是通信之前就提前定好的数据传送格式
  • 它包括数据包具体怎样传数据,其格式是提前定好的

url案例

jdbc:mysql://127.0.0.1:3306/swu

swu是西南大学的英文简写,我自己设置的数据库的名字

  • jdbc:mysql:// -------->协议
  • 127.0.0.1 ------------->IP地址
  • 3306/mysql----------->数据库端口号
  • swu--------------------->具体的数据库名
String url = "jdbc:mysql://192.168.151.9:3306/bjpowernode";
String user = "root";
String password = "981127";
String connection = DriverManager.getConnection(url,user,password);

注意

不同数据库的通信的协议不同,例如Oracle数据库的通信协议如下:

jdbc:oracle:thin:@localhost:1521:数据库名

第三步:获取数据库操作对象

  • 专门执行sql语句的对象
Statement stmt = connection.createStatement();

一般较少使用statement执行SQL语句,因为有SQL注入问题。

我们一般使用PrepareStatement对象,之后会谈到

第四步:执行SQL语句

JDBC中的sql语句不需要提供分号结尾

String sql = "insert into dept(deptno,dname,loc) values(50,'人事部','北京')";
// 专门执行DML语句的(insert delete update)
// 返回值是“影响数据库中的记录条数”
int count = stmt.executeUpdate(sql); 
System.out.println(count == 1 ? "保存成功" : "保存失败");

第五步:处理查询结果集

  • 只有当第四步执行的是select语句的时候,才要处理结果集

我们用ResultSet对象来接收执行sql语句的返回值后,这个数据集是什么样子呢?

如下图:

遍历结果集.jpg

这里详细谈谈ResultSetnextLine()方法,getString()方法

nextLine()方法判断下一行是否有数据,它类似于一个浮标的移动,如果下一行有数据,则返回true,否则返回false

getString()方法:不管数据库中的数据类型是什么,都以String的形式取出,同理:还有getInt,getDouble等

当然,如果在()内输入字段名,则会以列的名字获得该数据,输入单纯的数字n,则取出第n列的数据

注意

  1. JDBC中所有下标从1开始,不是从0开始。
  2. 列名称不是表中的列名称,是查询结果集的列名称

第六步:释放资源

  • 使用完资源之后一定要关闭资源。
  • Java和数据库属于进程间的通信,开启之后一定要关闭。
  • 不关闭通道,时间一长可能出现问题

为了保证资源一定释放,在finally语句块中关闭资源,并且要遵循从小到大依次关闭

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

JDBC工具类的写法

我们通常创建一个类名为DBUtils的工具类来封装JDBC中注册驱动,获取连接,释放资源部分的代码,提高代码复用性

import java.sql.*;
/**
 * JDBC工具类,简化JDBC编程。
 */
public class DBUtil {
    /**
     * 工具类中的构造方法都是私有的。
     * 因为工具类当中的方法都是静态的,不需要new对象,直接采用类名调用。
     */
    private DBUtil() {
    }
    // 静态代码块在类加载时执行,并且只执行一次。
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }


    /**
     * 获取数据库连接对象
     *
     * @return 连接对象
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection("jdbc:mysql://localhost:3306/swu", "root", "password");
    }

    /**
     * 关闭资源
     * @param conn 连接对象
     * @param ps 数据库操作对象
     * @param rs 结果集
     */
    public static void close(Connection conn, Statement ps, ResultSet rs){
        if(rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(ps != null){
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

ResourceBundle绑定属性配置文件

资源配置文件准备

文件名:jdbc.properties

内容:

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://主机IP/数据库名
user=数据库用户名
password=数据库用户密码

绑定

使用ResourceBundle的getBundle方法,传递参数为资源配置文件(.properties后缀文件)名字,不包括后缀

ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
String driver = bundle.getString("driver");
String url = bundle.getString("url");
String user = bundle.getString("user");
String password = bundle.getString("password");
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容