【Java】JDBC 数据库连接 (JDK17+MySQL8)


JDBC 是什么?

Java Database Connectivity | Java 数据库连接 技术

通过 JDBC 提供的方法 可以发送字符串 类型的 SQL语句DBMS(数据库管理系统 比如 MySQL Oracle), 并且获取到语句的执行结果。
从而实现 对 数据库 数据的 CRUD ( 增删改查 )
1
JDBCJava语言的规范(接口)各个数据库厂商的实现驱动(jar) 组成
在这里插入图片描述
JDBC是一种典型的面向接口编程
2

导入JDBC jar包

mysql版本推荐驱动版本备注
mysql 5.5.x5.0.xcom.mysql.jdbc.Driver
mysql 5.7.x5.1.xcom.mysql.jdbc.Driver
msyql 8.x8.0.x建议: 8.0.25+省略时区设置com.mysql.cj.jdbc.Driver

Java工程导入依赖
1. 项目创建lib文件夹
2. 导入驱动依赖jar包
3. jar包右键-添加为项目依赖
2


一、JDBC的核心API和使用路线

JDBC 技术组成:

  1. JDK下JDBC规范接口 , 存储在 java.sql 和 javax.sql 包中的API

为了项目代码的可移植性,可维护性,SUN公司从最初就制定了Java程序连接各种数据库的统一接口规范。这样的话,不管是连接哪一种DBMS软件,Java代码可以保持一致性。

  1. 各个数据库厂商提供的驱动jar包

因为各个数据库厂商的DBMS软件各有不同,那么内部如何通过sql实现增、删、改、查等管理数据,只有这个数据库厂商自己更清楚,因此把接口规范的实现交给各个数据库厂商自己实现。

  1. jar包是什么?

java程序打包的一种压缩包格式,可以将这些jar包引入项目中,就可以使用这个java程序中类和方法以及属性了!


核心类和接口
1.2
DriverManager 获取连接;
接着建立连接;
PreparedStatement(最常用)发送sql语句;
若是查询操作,则对应的查询结果放在Result中。

  1. DriverManager
    • 将第三方数据库厂商的实现驱动jar注册到程序中
    • 可以根据数据库连接信息获取connection
  2. Connection [建立连接]
    • 和数据库建立的连接,在连接对象上,可以多次执行数据库CRUD动作
    • 可以获取 statementpreparedstatement , callablestatement对象
  3. Statement【适用静态sql路线 没有动态值的】
    PreparedStatement【预编译sql 有动态值语句】
    CallableStatement
    • 具体发送SQL语句到数据库管理软件的对象
    • 不同发送方式稍有不同! preparedstatement 使用为重点!
  4. Result【对查询语句才有】(查询的结果)
    • 面向对象思维的产物 (抽象成数据库的查询结果表)
    • 存储DQL查询数据库结果的对象
    • 需要我们进行解析,获取具体的数据库数据

API 使用路线
api

JDBC基本使用步骤分析(6步)

  1. 注册驱动【依赖的jar包 进行安装】
  2. 获取连接【connection建立连接】
  3. 创建发送sql语句对象【statement 创建发送sql语句的statement】
  4. 发送sql语句,并获取返回结果【statement发送sql语句到数据库 并且取得返回结构】
  5. 结果集解析【将result结果解析出来】
  6. 资源关闭【释放resultset、statement、connection】

二、基于 statement 演示 查询

  1. 准备数据库数据
CREATE DATABASE jdbc_test;

USE jdbc_test;

CREATE TABLE t_user(
   id INT PRIMARY KEY AUTO_INCREMENT COMMENT '用户主键',
   account VARCHAR(20) NOT NULL UNIQUE COMMENT '账号',
   PASSWORD VARCHAR(64) NOT NULL COMMENT '密码',
   nickname VARCHAR(20) NOT NULL COMMENT '昵称');
   
INSERT INTO t_user(account,PASSWORD,nickname) VALUES
  ('root','123456','经理'),('admin','666666','管理员');
  1. 代码实现
 // @Description: 使用statement查询t_user表下,全部数据
// 1. 注册驱动
        DriverManager.registerDriver(new Driver());

        // 2. 建立连接
        /*
         *       java连接数据库肯定是调用某个方法,方法也需要填入数据库的基本信息:
         *       数据库ip地址:127.0.0.1
         *       数据库端口号:3306
         *       账号:root
         *       密码:root
         *       连接数据库的名称:jdbc_test
         *
         *      DriverManager.getConnection的参数
         *      参数1 url:
         *      jdbc:数据库厂商名://ip地址:port/数据库名
         *      jdbc:mysql://127.0.0.1:3306/jdbc_test
         * */
        //java.sql 接口 = 实现类
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc_test",
                "root", "root");

        // 3. 创建statement对象
        Statement statement = connection.createStatement();

        // 4. 发送sql
        String sql = "SELECT * FROM t_user";
        ResultSet resultSet = statement.executeQuery(sql);

        // 5. 接收 解析返回的结果集
        while (resultSet.next()) {
            int id = resultSet.getInt("id");
            String account = resultSet.getString("account");
            String password = resultSet.getString("password");
            String nickname = resultSet.getString("nickname");
            //输出列的结果
            System.out.println(id + "--" + account + "--" + password + "--" + nickname);
        }
        // 6. 关闭资源【先打开的后关】
        resultSet.close();
        statement.close();
        connection.close();

result

三、基于 statement 查询的改进与问题

需求:模拟登录 输入用户名/密码

//1.输入账号和密码
        Scanner scanner = new Scanner(System.in);
        String account = scanner.nextLine();
        String password = scanner.nextLine();
        scanner.close();

        //方法1:DriverManager.registerDriver(new Driver());调用两次。不用
        //方法2:new Driver()  频繁修改不优雅
        //方法3.使用反射 注册一次驱动
        Class.forName("com.mysql.cj.jdbc.Driver");

        //获取连接
        //省略写法: jdbc:mysql://localhost:3306/test = jdbc:mysql:///test
        Connection connection = DriverManager.getConnection("jdbc:mysql:///jdbc_test", "root", "root");

        //固定方法固定剂
        //创建statement
        Statement statement = connection.createStatement();

        //执行SQL语句 [动态SQL语句,需要字符串拼接]
        String sql = "select * from t_user where account = '"+account+"' and password = '"+password+"' ;";

        /**
         *  ResultSet 结果集对象 = executeQuery(DQL语句)
         *  int响应行数  = executeUpdate(非DQL语句)
         */
        ResultSet resultSet = statement.executeQuery(sql);

        //ResultSet == navicat
        /**
         * 1.需要理解ResultSet的数据结构和navicat查询出来的是一样!
         * 2.有一个光标指向的操作数据行,默认指向第一行的上边!我们需要移动光标,指向行,在获取列即可!
         *        boolean = next()
         *              false: 没有数据,也不移动了!
         *              true:  有更多行,并且移动到下一行!
         *       推荐:推荐使用if 或者 while循环,嵌套next方法,循环和判断体内获取数据!
         *       if(next()){获取列的数据!} ||  while(next()){获取列的数据!}
         *
         *3.获取当前行列的数据!
         *        get类型(int columnIndex | String columnLabel)
         *        列名获取  //lable 如果没有别名,等于列名, 有别名label就是别名,他就是查询结果的标识!
         *        列的角标  //从左到右 从1开始! 数据库全是从1开始!
         */

        //进行结果集对象解析
        if (resultSet.next()){
            //只要向下移动,就是有数据 就是登录成功!
            System.out.println("登录成功");
        }else{
            System.out.println("登录失败");
        }

        //关闭资源
        resultSet.close();
        statement.close();
        connection.close();

存在的问题:

  1. SQL语句需要字符串拼接,比较麻烦
  2. 只能拼接字符串类型,其他的数据库类型无法处理
  3. 可能发生注入攻击

四、基于 preparedStatement 方式优化

利用preparedStatement解决上述案例注入攻击SQL语句拼接问题!

public class PreparedStatementLoginPart {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 1. 输入用户名 密码
        Scanner scanner = new Scanner(System.in);
        String account = scanner.nextLine();
        String password = scanner.nextLine();
        scanner.close();

        // 2. 注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");

        // 3. 获取连接
        Connection connection = DriverManager.getConnection("jdbc:mysql:///jdbc_test", "root", "root");

        // 4.创建sql对象 与 sql语句
        String sql = "SELECT * FROM t_user WHERE account = ? AND password = ?;" ;
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        // 占位符? 赋值  从左到右,从1开始
        // 参数:int 占位符下角标 ; objects 占位符的值
        preparedStatement.setObject(1,account);
        preparedStatement.setObject(2,password);

        // 5. 结果集解析
        ResultSet resultSet = preparedStatement.executeQuery();
        if (resultSet.next()) {
            //只要向下移动,就是有数据 就是登录成功!
            System.out.println("登录成功");
        }else{
            System.out.println("登录失败");
        }

        resultSet.close();
        preparedStatement.close();
        connection.close();
    }
}

五、基于 preparedStatement 演示 CRUD

C 、增加数据

       /**
     * 插入一条用户数据!
     * 账号: test
     * 密码: test
     * 昵称: 测试
     */
    @Test
    public void testInsert() throws SQLException, ClassNotFoundException {
        Class.forName("com.mysql.cj.jdbc.Driver");

        Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/jdbc_test", "root", "root");

        String sql = "INSERT INTO t_user(account,password,nickname) VALUES(?,?,?);";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setString(1,"test");
        preparedStatement.setString(2,"test");
        preparedStatement.setString(3,"测试");

        int rows = preparedStatement.executeUpdate();
        System.out