JDBC学习笔记

37 阅读5分钟

一、JDBC概述

1.1 简介

JDBC是sun公司提供一套用于数据库操作的接口,java程序员只需要面向这套接口编程即可。不同的数据库厂商,需要针对这套接口,提供不同实现。不同的实现的集合,即为不同数据库的驱动。它是一组通用的sql存取与操作的接口API,独立于特定的数据库管理系统,定义了对用来访问数据的类库,可以用来方便的获取数据库资源。简化了程序员的开发细节,为jdbc访问不同的数据库提供了统一的途径。示例图如下:

image.png

1.2 结构

jdbc接口API包括两个层次
- 面向数据库的API:Driver API,提供给数据库厂商开发驱动程序
- 面向开发者的API:提供开发人员连接数据库操作数据库等

1.3 jdbc编写步骤流程图

...

二、jdbc获取连接

@Test
    public void testConnection() {
        try {
            //1.数据库连接信息
            String url = "jdbc:mysql://localhost:3306/study";
            String user = "root";
            String password = "123456";
            String driverName = "com.mysql.jdbc.Driver";
            //2.加载驱动 (①实例化Driver ②注册驱动)
            Class.forName(driverName);
            //Driver driver = (Driver) clazz.newInstance();
            //3.注册驱动
            //DriverManager.registerDriver(driver);
            /*
            可以注释掉上述代码的原因,是因为在mysql的Driver类中声明有:
            static {
                try {
                    DriverManager.registerDriver(new Driver());
                } catch (SQLException var1) {
                    throw new RuntimeException("Can't register driver!");
                }
            }
             */
            //3.获取连接
            Connection conn = DriverManager.getConnection(url, user, password);
            System.out.println(conn);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

要点一:Driver接口

  • java.sql.Driver 接口是所有 JDBC 驱动程序需要实现的接口。这个接口是提供给数据库厂商使用的,不同数据库厂商提供不同的实现。
  • 在程序中不需要直接去访问实现了 Driver 接口的类,而是由驱动程序管理器类(java.sql.DriverManager)去调用这些Driver实现。

要点二:URL

JDBC URL 用于标识一个被注册的驱动程序,驱动程序管理器通过这个 URL 选择正确的驱动程序,从而建立到数据库的连接。 JDBC URL的标准由三部分组成,各部分间用冒号分隔。

  • jdbc:子协议:子名称
  • 协议:JDBC URL中的协议总是jdbc
  • 子协议:子协议用于标识一个数据库驱动程序
  • 子名称:一种标识数据库的方法。子名称可以依不同的子协议而变化,用子名称的目的是为了定位数据库提供足够的信息。包含主机名(对应服务端的ip地址),端口号,数据库名
  • 常见的连接url写法:jdbc:mysql://localhost:3306/study?useUnicode=true&characterEncoding=utf8

三、操作数据库

数据库连接被用于向数据库发送命令和sql,并接受数据库返回的结果。一个数据库连接就是一个Scoket连接。

在java.sql包中定义了三种调用数据库的方式:
- Statement:用于执行静态 SQL 语句并返回它所生成结果的对象。
- PrepatedStatement:SQL 语句被预编译并存储在此对象中,可以使用此对象多次高效地执行该语句。
- CallableStatement:用于执行 SQL 存储过程

3.1 Statement

  • 1、使用方法:通过Connection对象的createStatement()方法创建该对象
  • 2、常用方法:Statement常用方法有两个excuteUpdate(执行更新、插入、删除)、executeQuery(执行查询)

statement虽然可以操作数据库但是却存在弊端,主要有两个问题:一是可能存在sql注入的情况;二是使用Statement操作数据库需要进行字符串拼接,比较繁琐。

sql注入:sql注入是利用系统对用户输入的数据没有充分的检查,导致输入非法数据形成非法的sql语句或者命令。例如:SELECT user, password FROM user_table WHERE user=‘a’ OR 1 = ’ AND password = ’ OR ‘1’ = ‘1’,通过这个sql即使没有用户名和密码也会返回true。

代码演示

 @Test
    public void test() throws ClassNotFoundException, SQLException {
        //1.加载驱动:加载 JDBC 驱动需调用 Class 类的静态方法 forName(),向其传递要加载的 JDBC 驱动的类名
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2.注册驱动:DriverManager 类是驱动程序管理器类,负责管理驱动程序
        //我这里使用的驱动版本是8.x,如果在立即注册驱动是会报错误的,Driver类默认会自己注册驱动
//        DriverManager.registerDriver(new Driver());
        //3.获取数据库连接
        Connection connection = DriverManager.getConnection("jdbc:mysql://xxxxxx:3306/study?useUnicode=true&characterEncoding=utf-8", "root", "xxxxxx");
        //4.根据连接获取statement
        Statement statement = connection.createStatement();
        //5.执行查询sql,sql一般是需要自己拼接的
        ResultSet resultSet = statement.executeQuery("select * from user;");
        //6.获取结果集的元数据
        ResultSetMetaData metaData = resultSet.getMetaData();
        //7.获取列数
        int columnCount = metaData.getColumnCount();

        //8.从结果中取一行数据打印
        if (resultSet.next()) {
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < columnCount; i++) {
                String columnName = metaData.getColumnName(i + 1);

                Object object = resultSet.getObject(columnName);

                sb.append(columnName).append(":").append(object).append("\t");
            }

            System.out.println(sb);

        }
        //9.关闭所有连接,我这里为了方便没有try catch异常,建议try catch并把连接关闭写在finally块中保证执行
        resultSet.close();

        statement.close();

        connection.close();

    }

3.2 PreparedStatement

PreparedStatement是Statement的一个子接口,表示一条预编译的sql
  • 使用方法:通过Connection的PreparedStatement()方法获取该对象,并在获取对象时,可传递一条预编译的sql

  • 常用方法:和Statement一样

  • 注意点:PrepareStatement可以通过setXXX配置sql,下标从1开始

  • 与Statement对比:

    • 首先PrepareStatement可以解决Statement出现的SQL注入问题,因为会进行预编译,所以即使有敏感字符也会当做一个字符去处理
    • 提高了代码的可读性和维护性,不需要自己拼接sql
    • PrepareStatement提高性能,DBServer会对预编译语句提供性能优化。因为预编译语句有可能被重复调用,所以语句在被DBServer的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中就会得到执行。在statement语句中,即使是相同操作但因为数据内容不一样,所以整个语句本身不能匹配,没有缓存语句的意义.事实是没有数据库会对普通语句编译后的执行代码缓存。这样每执行一次都要对传入的语句编译一次。

代码演示

@Test
    public void test() throws ClassNotFoundException, SQLException {
        //1.加载驱动:加载 JDBC 驱动需调用 Class 类的静态方法 forName(),向其传递要加载的 JDBC 驱动的类名
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2.注册驱动:DriverManager 类是驱动程序管理器类,负责管理驱动程序
        //我这里使用的驱动版本是8.x,如果在立即注册驱动是会报错误的,Driver类默认会自己注册驱动
//        DriverManager.registerDriver(new Driver());
        //3.获取数据库连接
        Connection connection = DriverManager.getConnection("jdbc:mysql://124.220.191.79:3306/study?useUnicode=true&characterEncoding=utf-8", "root", "1Qaz2wsx@");
        //4.根据连接获取statement
        PreparedStatement statement = connection.prepareStatement("select * from user where user_name = ?");
        //4.1 设置参数
        statement.setString(1,"zs");
        //5.执行查询sql,sql一般是需要自己拼接的
        ResultSet resultSet = statement.executeQuery();
        //6.获取结果集的元数据
        ResultSetMetaData metaData = resultSet.getMetaData();
        //7.获取列数
        int columnCount = metaData.getColumnCount();

        //8.从结果中取一行数据打印
        if (resultSet.next()) {
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < columnCount; i++) {
                String columnName = metaData.getColumnName(i + 1);

                Object object = resultSet.getObject(columnName);

                sb.append(columnName).append(":").append(object).append("\t");
            }

            System.out.println(sb);

        }
        //9.关闭所有连接,我这里为了方便没有try catch异常,建议try catch并把连接关闭写在finally块中保证执行
        resultSet.close();

        statement.close();

        connection.close();

    }

3.3 ResultSet

如上述代码演示所示,ResultSet按照逻辑存储查询结果数据表。ResultSet内部维护了一个游标,可以通过next()方法移动游标,next方法是iterator的hashNext()和next()方法的结合,返回结果为boolean类型。初始游标默认停留在数据表第一行,可通过方法getxxx()通过传递列名或者列的下标获取该行数据

3.4 ResultSetMetaData

存储的是结果数据表的元数据信息,记录行的列名、类型、数量等信息,可通过ResultSet对象getMetaData()方法获取该对象

3.5 资源关闭

切记不可忘记关闭资源:ResultSet、Statement、Connection

  • 如果资源不关闭,积累多了之后可能导致系统宕机,所以用完之后尽快释放
  • 可在finally块中保证资源的最终释放

后续更新...

后续会更新批量插入、数据库连接池等学习记录笔记...