如何使用JDBC操作数据库?一文带你吃透JDBC规范

news2024/11/30 0:41:03

文章目录

  • 1. 前言
  • 2. JDBC 概述
    • 2.1 概念
    • 2.2 优点
  • 3. JDBC 快速入门
  • 4. JDBC API详解
    • 4.1 DriverManager
      • 4.1.1 注册驱动
      • 4.1.2 获取连接
    • 4.2 Connection
      • 4.2.1 获取执行sql的对象
      • 4.2.2 事务管理
    • 4.3 Statement
    • 4.4 ResultSet
    • 4.5 PreparedStatement
      • 4.5.1 sql注入问题
      • 4.5.2 preparedStatement 原理
  • 5. 数据库连接池
    • 5.1 概念
    • 5.2 实现
  • 6. Druid 连接池的使用
  • 7. 准备工作
  • 8. 实战案例
  • 9. 增删改查操作练习
    • 9.1 查询所有
    • 9.2 添加数据
    • 9.3 修改数据
    • 9.4 删除数据
  • 10. 总结
  • 📂橙子精品文章学习推荐

1. 前言

大家好,我是橙子。最近又肝了几个大夜,总结了 JDBC 完整版的基础教程和实战案例训练。快来看看这些 Java 基础性的代码你有没有忘记?

在这里插入图片描述

在 Java 开发中,使用 Java 语言操作数据库是非常重要的一部分,那么 Java 语言是如何操作数据库的呢?

我们需要使用不同厂商的数据库时,例如 MySQL,Oracle 等,显然一套 Java 代码是不能同时操作不同的数据库的,那么怎样实现一套 Java 代码对不同的数据库的操作呢?

在这里插入图片描述

JDBC 应运而生,JDBC 是使用 Java 语言操作关系型数据库的一套 API,其中定义了对数据库操作的规范,这里的规范在 Java 中就是指接口,不同的数据库厂商提供了不同的 JDBC 实现类,称为驱动,使用时,只需要导入需要的数据库驱动 jar 包,便可以操作不同的关系型数据库,其实际是使用了 jar 包中的实现类。

本系列文章的学习目标:

  • 什么是 JDBC,如何使用?
  • 使用 JDBC 完成数据的增删改查操作
  • JDBC API 熟练使用

学会使用Java代码操作数据库,本文是2 万字学习教程,阅读本文需要20 分钟,建议收藏长期学习使用。

2. JDBC 概述

2.1 概念

JDBC , Java DataBase Connectivity , 是使用 Java 语言操作关系型数据库的一套API。本质上来说,JDBC 中定义了一套操作关系型数据库的规范,但是我们不能直接使用这套接口来操作数据库,于是各大数据库厂商提供了 JDBC 不同的实现类,称为驱动,例如 MySQL 驱动,此时,我们只需要操作 JDBC 规范好的代码便可以完成对数据库的操作。在实现对数据库的操作时,其底层是使用了 jar 包中定义的实现类。

image-20230122171430643

当我们使用不同的数据库时,例如测试时使用 MySQL 数据库,部署时使用 Oracle 数据库,只需要编写一套 Java 代码便可以实现对不同关系型数据库的操作。

2.2 优点

使用 JDBC 操作关系型数据库时,各大数据库厂商提供了不同的实现类,我们不需要针对不同的数据库进行单独开发,因此,我们也可以随时替换数据库,而不用大量修改 Java 代码。

我们只需要在使用时导入需要使用的数据库对应的驱动 jar包到项目中,便可以实现对指定数据库的操作,使 Java 操作数据库变得轻松便捷。

下图就是MySQL的驱动jar包:

image-20230122170603015

不同版本的 jar 包可以在官网下载。

3. JDBC 快速入门

使用 Java 语言操作数据库,实际上就是 Java 代码将 sql 语句发送到 MySQL 数据库服务端,MySQL 服务端接收并执行 sql 语句,同时返回一个执行结果,最后该结果会发送到 Java 代码进行处理。

对数据库的操作大致分为以下几个步骤:

  1. 注册驱动
  2. 获取连接
  3. 编写sql
  4. 获取执行sql对象
  5. 执行sql
  6. 处理返回结果
  7. 释放资源

在进行编码之前,需要先创建工程,导入数据库的驱动 jar 包。这里以 JDBC 操作 MySQL 数据库为例,步骤如下:

创建空项目,定义项目名称及路径:

image-20230122173738084

在File / Project Structure 中进行项目设置,JDK版本,编译版本等:

image-20230122173942616

创建新模块,指定名称及位置:

image-20230122174154153

在 jdbc-demo 模块中新建 Lib 文件夹:

image-20230122174526391

将下载好的 MySQL驱动 jar 包导入 Lib 目录下作为库文件:

image-20230122174641452

右键 MySQL 驱动 jar 包,点击 Add as Library ,并选择模块下有效:

image-20230122174725653

创建好工程以后,就可以编写代码操作数据库啦!

在 idea 中右键 src / new / Java Class ,输入类名 JDBCDemo 创建一个新的类。

第一步:注册驱动

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

目的是把 Driver 类加载到内存中。其实,Java 中用于注册驱动的是 registerDriver() 方法,而在 Driver 类中使用了该方法,所以只要把 Driver 类加载到内存中 ,包含 registerDriver 方法的静态代码块就会执行,驱动就会被注册。我们可以查看JDK源码中的Driver 类:

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!");
        }
    }
}

第二步:获取连接

String url="jdbc:mysql://localhost:3306?useSSL=false";
String username="root";
String pass word="abc123";
Connection conn = DriverManager.getConnection(url, username, password);

目的是通过 getConnection() 方法获取数据库连接 Connection 类对象,这里没有直接把数据传入方法之中,而是定义了三个局部变量,url,数据库用户名和密码,其中 url 参数有固定的语法格式:

jdbc:mysql://ip地址(域名):端口号?参数键值对1&参数键值对2

第三步:定义sql

String sql="update account set money=2000 where name='张三'";

目的是定义操作数据库的 sql 语句。

第四步:获取执行sql的对象

Statement stmt = conn.createStatement();

目的是在执行 sql 语句之前,先获取执行 sql 的对象 statement。

第五步:执行sql

int count = stmt.executeUpdate(sql);

目的是为了将 sql 发送到数据库服务端,让数据库服务端执行完 sql,并且返回一个结果,这里的 executeUpdate() 返回受影响的行数。

第六步:处理返回结果

 System.out.println(count);

目的是处理数据库服务端返回的 sq l执行结果,这里我们打印了 executeUpdate() 方法返回的受影响的行数。

第七步:释放资源

stmt.close();
conn.close();

目的是在完成对数据库的操作以后释放资源,否则数据库文件将一直被被程序占用,释放资源时遵循先开后释放的原则。

使用 JDBC 操作 MySQL 数据库完整代码:

public class JDBCDemo {
    public static void main(String[] args) throws Exception {
        //1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2.获取连接
        String url="jdbc:mysql://localhost:3306/db1?useSSL=false";
        String username="root";
        String pass word="abc123";
        Connection conn = DriverManager.getConnection(url, username, password);
        //3.定义sql
        String sql="update account set money=2000 where name='张三'";
        //4.获取执行sql的对象
        Statement stmt = conn.createStatement();
        //5.执行sql
        int count = stmt.executeUpdate(sql);
        //6.处理结果
        System.out.println(count);
        //7.释放资源
        stmt.close();
        conn.close();
    }
}

4. JDBC API详解

4.1 DriverManager

DriverManager ,驱动管理类。在 JDBC 入门篇中,我们使用了该类的方法来注册驱动和获取连接。

DriverManager 类主要有两个作用:

  1. 注册驱动
  2. 获取连接

4.1.1 注册驱动

在 Driver 类静态代码块中 DriverManager 类执行了其 registerDriver() 方法用于注册驱动,当我们把类Driver 加载到内存中后,该静态代码块就会执行,此时就完成了驱动注册,我们就是使用这样的方法注册驱动的。查看 JDK 源码就不难理解:

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!");
        }
    }
}

需要注意的是,在 MySQL 5 以后的驱动 jar 包可以不用进行此步骤来注册驱动,当我们把 jar 导入到项目以后,程序会自动加载 jar 包中 META-INF/services/java.sql.Driver 文件中的驱动类。

4.1.2 获取连接

我们使用 DriverManager 类的 getConnection() 静态方法来获取数据库连接对象,其方法有三个参数,分别是 url,数据库的用户名和密码。url 有其固定的语法格式:

jdbc:mysql://ip地址(域名):端口号/连接的数据库名?参数键值对1&参数键值对2

jdbc:mysql://连接的数据库名?参数键值对1&参数键值对2 //如果使用的是本地的 MySQL ,并且使用默认的端口号 3306 的,url 的值可以简写

例如:

jdbc:mysql://db1?useSSL=false

这里 useSSL = false 关闭了安全连接方式,解决了 idea 警告的问题。

4.2 Connection

Connection ,数据库连接对象接口。在入门篇中使用了该类获取 sql 的执行对象 statement。

Connection 接口的作用有:

  1. 获取执行 SQL 的对象
  2. 管理事务

4.2.1 获取执行sql的对象

Connection 中的 createStatement() 方法可以获取执行 sql 的对象 Statement ,用于把 sql 发送到数据库服务端。使用 preparedStatement() 方法可以获取预编译 sql 的执行 sql 的对象,这个方法可以有效的防止 sql 注入的问题。

4.2.2 事务管理

在JDBC 中使用 Connection 对象进行事务管理,Connection 中定义了三个对应的方法:

开启事务:

void setAutoCommit(boolean autoCommit) //将此连接的自动提交模式设置为给定状态

回滚事务:

void rollback() //撤消当前事务中所做的所有更改,并释放此 Connection对象当前持有的所有数据库锁

提交事务:

void commit() //使自上次提交/回滚以来所做的所有更改成为永久更改,并释放此 Connection对象当前持有的所有数据库锁

示例:

public class JDBCDemo {

    public static void main(String[] args) throws Exception {
        //1. 注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写
        String url = "jdbc:mysql://db1?useSSL=false";
        String username = "root";
        String ppassword = "abc123";
        Connection conn = DriverManager.getConnection(url, username, password);
        //3. 定义sql
        String sql1 = "update account set money = 3000 where id = 1";
        String sql2 = "update account set money = 3000 where id = 2";
        //4. 获取执行sql的对象 Statement
        Statement stmt = conn.createStatement();

        try {
            // 开启事务
            conn.setAutoCommit(false);
            //5. 执行sql
            int count1 = stmt.executeUpdate(sql1);//受影响的行数
            //6. 处理结果
            System.out.println(count1);
            int i = 3 / 0;
            //5. 执行sql
            int count2 = stmt.executeUpdate(sql2);
            //6. 处理结果
            System.out.println(count2);

            // 提交事务
            conn.commit();
        } catch (Exception e) {
            // 回滚事务
            conn.rollback();
            e.printStackTrace();
        }

        //7. 释放资源
        stmt.close();
        conn.close();
    }
}

我们使用 Java 中的异常捕获机制来进行事务的管理。将执行 sql 的语句放在 try 代码块中,并且使用 setAutoCommit() 方法开启事务,如果代码块中没有出现异常,则使用 commit() 方法提交事务,如果 try 代码块某处出现了异常,则需要回滚事务,所以将回滚事务的 rollback() 方法定义在 catch 语句块中。

4.3 Statement

Statement 类的对象用来执行 sql 语句,即把 sql 发送到数据库服务端,但是使用此类对象执行 sql 会出现 sql 注入的问题。不同的 sql 语句使用不同的方法执行,执行 DDL,DML 语句使用下面的方法:

int executeUpdate(String sql)//执行sql语句完成增删改操作

在执行 DML 语句完成对数据的增删改操作时,该方法返回数据表中受影响的行数,可以使用这个返回值来判断是否成功完成对数据的操作。而使用 DDL 来操作数据库和数据表时,返回值可能为 0,所以不能用作上面的判断,且在开发中 DDL 很少被用到。

示例1,使用 DML 修改数据:

//3.定义sql
String sql="update account set money=2000 where name='张三'";
//5.执行sql
int count = stmt.executeUpdate(sql);
//6.处理结果
if(count>0){
    System.out.println("修改成功!");
}else{
     System.out.println("修改失败!");
}

示例2,使用 DDL 删除数据库:

//3.定义sql
String sql="drop database db2";
//4.获取执行sql的对象
Statement stmt = conn.createStatement();
//5.执行sql
int count = stmt.executeUpdate(sql);
//6.处理结果
System.out.println(count);

此时,控制台返回了 0 ,但是对数据库的删除操作已经完成。

image-20230123111252544

执行 DQL 语句时需要使用下面的方法:

ResultSet excuteQuery(String sql)//执行sql语句完成查询操作,返回单个ResultSet对象

该方法在下面 ResultSet类中讲解。

4.4 ResultSet

ResultSet ,结果集对象类,其作用是封装 sql 查询语句的结果。执行了 DQL 查询语句后就会返回该类的对象,执行 DQL 语句的方法如下:

ResultSet excuteQuery(String sql)//执行sql语句完成查询操作,返回单个ResultSet对象

ResultSet 类提供了操作查询结果数据的方法,如下:

/*
作用:将指针从当前位置移动到下一行,然后这一行是否为有效行
返回值:true 当前行为有效行,false 当前行没有数据
*/
boolean next()
/*
作用:获取数据  
xxx表示数据类型 例如 int getInt() 
参数:int类型的参数表示列的编号,这个编号从0开始;String类型的参数,表示列的名称
*/
xxx getXxx(参数)
    
    

操作查询结果数据的方法如下图:

一开始指针位于第一行前,如图所示红色箭头指向于表头行。当我们调用了 next() 方法后,指针就下移到第一行数据,并且方法返回 true,此时就可以通过 getInt("id") 获取当前行 id 字段的值,也可以通过 getString("name") 获取当前行 name 字段的值。如果想获取下一行的数据,继续调用 next() 方法,以此类推。获取某个字段的值时,既可以传入 int 类型,即列的编号,也可以传入列对应的字段名。

示例,查询 account 表中数据,并且打印所有结果:

public class JDBCDemo {
    public static void main(String[] args) throws Exception {
        //1. 注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写
        String url = "jdbc:mysql://localhost:3306/db1?useSSL=false";
        String username = "root";
        String ppassword = "abc123";
        Connection conn = DriverManager.getConnection(url, username, password);
        //3. 定义sql
        String sql = "select * from account";
        //4. 获取执行sql对象
        Statement stmt = conn.createStatement();
        //5. 执行sql
        ResultSet rs = stmt.executeQuery(sql);
        //6. 处理返回结果, 遍历rs中的所有数据
        // 1 指针向下移动一行,并且判断当前行是否有数据
        while (rs.next()) {
            // 2 获取数据  getXxx()
            int id = rs.getInt("id");
            String name = rs.getString("name");
            double money = rs.getDouble("money");

            System.out.println(id);
            System.out.println(name);
            System.out.println(money);
        }
        //7. 释放资源
        rs.close();
        stmt.close();
        conn.close();
    }
}

4.5 PreparedStatement

4.5.1 sql注入问题

前面 statement 类的对象用来执行sql语句,例如 :

int executeUpdate(String sql)//执行sql语句完成增删改操作

但是使用此方法存在 sql 注入的问题,什么是 sql 注入呢?这里做一个大致的讲解。

SQL注入是通过操作输入来修改事先定义好的 sql 语句,用来达到执行代码对服务器进行攻击的方法。例如,在程序的登录操作中,用户输入的用户名和密码会被发送到 Java 代码,然后用于 Java 操作数据库的sql 语句中,只有当用户输入的用户名和密码与数据库中的数据匹配时才能实现登录,但是,只要我们输入事先定义好的语句,便可以进行破解。例如下面拼字符串的方式修改 sql 语句原来的含义:

String name ="lisi";
String ppwd="' or '1' ='1";//事先定义好的输入,拼接到sql语句中后改变其含义
String sql = "Select * from tb_user where name='"+username+"' and ppassword='"+ppwd+"'";

此时,将用户名和密码拼接到 sql 中,如下:

select * from tb_user where username = 'lisi' and ppassword = ''or '1' = '1';

可以看到,sql 中的判断条件永远为 true ,不管输入什么样的用户名,这条 sql 都成立,实现了 sql 注入。

image-20230123152756547

为了解决这个问题,出现了 preparedStatement ,该类用于预编译 sql 语句并执行,其优点是可以防止sql 注入,并且预编译sql提高了性能。其实底层是将特殊字符进行了转义,转义的 sql 如下:

select * from tb_user where username = 'lisi' and ppassword = '\'or \'1\' = \'1'

示例:

public class JDBCDemo {
    public static void main(String[] args) throws Exception {
        //1. 注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2. 获取连接
        String url="jdbc:mysql://localhost:3306/db1?useSSL=false";
        String username="root";
        String ppassword="abc123";
        Connection conn = DriverManager.getConnection(url,username,password);
        //Java代码接收到客户端发送的用户名和密码
        String name="lisi";
        String ppwd="' or '1' ='1";
        //3. 定义sql
        String sql="select * from tb_user where username=? and password=?";
        //4. 获取执行sql的对象psmt
        PreparedStatement psmt = conn.prepareStatement(sql);
        //设置问号的值
        psmt.setString(1,name);
        psmt.setString(2,pwd);
        //5. 执行sql
        ResultSet rs = psmt.executeQuery();//此时不再需要传入sql语句
        //6. 处理返回结果
        if(rs.next()){
            System.out.println("yes");
        }else{
            System.out.println("no");
        }
        //7. 释放资源
        rs.close();
        psmt.close();
        conn.close();
    }
}

image-20230123152857215

4.5.2 preparedStatement 原理

前面使用 preparedStatement 解决了 sql 注入的问题,其实我们还没有开启预编译的功能,JDBC 中是如何通过预编译来提高性能的呢?

要学习 prepareStatement 实现预编译的原理,首先要明白 Java 操作数据库的步骤:

首先 Java 代码将 sql 发送到 MySQL 服务端,MySQL 服务端接收到 sql 语句以后,会对 sql 语句进行检查(检查 sql 语句的语法),编译(编译 sql 语句,将 sql 语句编译成可执行的函数),执行的操作。而检查和编译 sql 语句花费的时间往往较长,如果想要提高 sql 的性能,就可以从这方面入手。在使用预编译的方法时,检查和编译 sql 语句的操作将会在获取执行 sql 的对象时完成,并且不会重复执行,从而提高了性能。

image-20230123155242436

要想打开预编译的功能,就需要在 url 中设置如下的参数:

useServerPrepStmts=true //参数开启预编译功能

示例:

public class JDBCDemo {
    public static void main(String[] args) throws Exception {
        //1. 注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2. 获取连接
        String url="jdbc:mysql://localhost:3306/db1?useSSL=false&useServerPrepStmts=true";
        String username="root";
        String ppassword="abc123";
        Connection conn = DriverManager.getConnection(url,username,password);
        //Java代码接收到客户端发送的用户名和密码
        String name="lisi";
        String ppwd="'or '1' = '1";
        //3. 定义sql
        String sql="select * from tb_user where username=? and password=?";
        //4. 获取执行sql的对象psmt
        PreparedStatement psmt = conn.prepareStatement(sql);

        //设置问号的值
        psmt.setString(1,name);
        psmt.setString(2,pwd);
        ResultSet rs=null;
        //5. 执行sql
        rs = psmt.executeQuery();//此时不需要传入sql语句

        psmt.setString(1,"zhangsan");
        psmt.setString(2,"abc123");
        rs=psmt.executeQuery();

        //6. 处理返回结果
        if(rs.next()){
            System.out.println("yes");
        }else{
            System.out.println("no");
        }
        //7. 释放资源
        rs.close();
        psmt.close();
        conn.close();
    }
}

在获取 sql 的执行对象时,sql 会作为参数被发送到 MySQL 服务端进行检查和编译,在执行时,不再进行第二次检查和编译的操作,提高了 sql 执行的性能。

5. 数据库连接池

5.1 概念

前面在 JDBC API 详解中,获取的数据库连接对象 conn,在使用时创建,使用完毕就会将其销毁。这样重复创建和销毁的过程实际上是消耗性能和时间的,当大量用户访问数据库时,每次都要进行数据库连接对象的创建和销毁,对系统来说是一种大量的消耗。那么我们怎样来提升性能和节省时间呢?

在这里插入图片描述

我们使用数据库连接池来重复利用数据库的连接对象,即 Connection 类对象。

image-20230123214035329

数据库连接池是一个负责分配,管理数据库连接对象的容器,它允许应用程序重复使用同一个数据库连接对象。数据库连接池可以释放空闲时间超过最大空闲时间的数据库连接对象来避免因为没有释放数据库连接对象而引起的数据库连接遗漏。

image-20230123221109976

连接池是在一开始就创建好了一些连接对象存储起来,用户需要连接数据库时,不需要自己创建连接对象,而只需要从连接池中获取一个连接对象进行使用,使用完毕后再将连接对象归还给连接池。这样就可以起到资源重用的作用,也节省了频繁创建连接销毁连接所花费的时间,从而提升了系统响应的速度

总结来说使用数据库连接池有以下几点好处:

  • 实现资源重用
  • 提升系统响应速度
  • 避免数据库连接遗漏

5.2 实现

sun 公司提供了数据库连接池的标准接口 DataSource ,我们一般使用第三方实现该接口的实现类,所有实现类都继承了其获取连接的方法:

Connection getConnection()

常见的数据库连接池有:

  • Druid(德鲁伊)
  • C3P0
  • DBCP

使用了数据库连接池以后,在获取数据库连接对象时不需要通过 DriverManager类的 getConnection() 方法,而是直接从数据库连接池中获取。我们今天要使用的是 Druid 连接池,它是阿里巴巴开源的数据库连接池项目,其功能强大,性能优秀,使用占比高,是一款优秀的数据库连接池。

6. Druid 连接池的使用

下面以 Druid 连接池为例,讲解通过数据库连接池获取数据库连接对象,主要有以下几个步骤:

  1. 导入Druid 连接池的 jar 包
  2. 定义配置文件
  3. 加载配置文件
  4. 获取数据库连接池对象
  5. 获取连接

第一步:将 Druid 的 jar 包放入项目中的 Lib 目录下作为库文件,jar 包自行下载。

image-20230123222631273

选择 Add as Library,选择模块下有效:

image-20230123222706346

第二步:编写配置文件,在 src 目录下创建文件 druid.properties

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///db1?useSSL=false&useServerPrepStmts=true
username=root
password=1234
# 初始化连接数量
initialSize=5
# 最大连接数
maxActive=10
# 最大等待时间
maxWait=3000

Druid 配置文件中有很很多的参数,这里配置了用到的几项,其中有连接数据库的名称和密码,初始连接数,最大连接数,最大等待时间等,超过了最大等待时间配置文件还没有加载成功的话,程序就会报错。

第三步:在代码中加载配置文件

//3. 加载配置文件
Properties prop = new Properties();
prop.load(new FileInputStream("jdbc-demo/src/druid.properties"));

第四步:在代码中获取连接池对象

//4. 获取连接池对象
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);

第五步:获取数据库连接对象

 //5. 获取数据库连接 Connection
Connection connection = dataSource.getConnection();
System.out.println(connection); //获取到了连接后就可以继续做其他操作了

示例,Druid的使用完整代码:

public class DruidDemo {

    public static void main(String[] args) throws Exception {
        //1.导入jar包
        //2.定义配置文件
        //3. 加载配置文件
        Properties prop = new Properties();
        prop.load(new FileInputStream("jdbc-demo/src/druid.properties"));
        //4. 获取连接池对象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
        //5. 获取数据库连接 Connection
        Connection connection = dataSource.getConnection();
        System.out.println(connection); //获取到了连接后就可以继续做其他操作了  
    }
}

运行结果:

image-20230123224744196

其中,DruidDataSourceFactory 类中的 createDataSource() 方法既可以传入一个 Map 集合,也可以传入 prop 对象,其中都存放配置信息,用来获取连接池对象。

小tips:如果代码中文件的相对路径报错,可以使用 System.getProperty("user.dir") 获取项目的当前路径。

在这里插入图片描述

7. 准备工作

前面我们说过,在 Java 代码中,执行 sql 查询语句以后返回一个 ResultSet 类的对象,并且 ResultSet 类提供了方法让我们可以操作查询结果数据,例如可以直接打印所有查询的数据。

示例:

public class JDBCDemo {

    public static void main(String[] args) throws Exception {
        //1. 注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2. 获取连接
        String url="jdbc:mysql://localhost:3306/blog?useSSL=false";
        String username="root";
        String ppassword="abc123";//密码
        Connection conn = DriverManager.getConnection(url,username,password);
        //3. 定义sql
        String sql="select * from student";
        //4. 获取sql执行对象
        Statement stmt = conn.createStatement();
        //5. 执行sql
        ResultSet rs = stmt.executeQuery(sql);
        //6. 处理结果
        while (rs.next()){
            int id=rs.getInt(1);
            String name=rs.getString(2);
            String gender=rs.getString(3);

            System.out.println(id);
            System.out.println(name);
            System.out.println(gender);

            System.out.println("---------");
        }
        //7. 释放资源
        rs.close();
        stmt.close();
        conn.close();
    }
}

运行结果:

image-20230124144624978

显然这样并不是我们想要的效果,数据零散不好处理。所以我们可以把每条数据封装为一个实体类的对象,每个对象对应数据库表的一条记录,并且把每个对象放到集合中存储。练习使用的数据库表:

drop table if exists student;

create table student(
	id int primary key auto_increment,
	name varchar(10),
	gender char(1)
);

insert into student(name,gender) values
('张三','男'),
('李四','女'),
('王五','男');

在 Navicat 中查看数据表:

image-20230124162412597

8. 实战案例

查询学生信息表数据,封装为 Student 类对象,并存放在 ArrayList 集合中。

思路:要解决这个问题,大概分为以下几个步骤:

  • 创建一个 Student 实体类
  • 查询数据库中的数据,并且赋值给对象的属性
  • 将对象存储到 ArrayList 集合中

第一步:创建一个Student 实体类,并定义 set() 方法,重写 Object 类中的 toString 方法,方便查看打印效果。

public class Student {
    private int id;
    private String name;
    private String gender;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                '}';
    }
}

小tips :在 idea 中使用 Alt + Inset 快捷键可以快速给类添加或重写一些方法,例如 get() ,set() ,toString() 方法等。使用 Ctrl + 鼠标左键可以快速选择多项方法。

第二步:使用 Java 代码操作数据库,查询数据库表中所有学生信息并通过 set() 方法赋值给 Student 类的对象,将对象存储到集合中。

public class JDBCDemo {

    public static void main(String[] args) throws Exception {
        //1. 注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2. 获取连接
        String url = "jdbc:mysql://localhost:3306/blog?useSSL=false";
        String username = "root";
        String ppassword = "abc123";//密码
        Connection conn = DriverManager.getConnection(url, username, password);
        //3. 定义sql
        String sql = "select * from student";
        //4. 获取sql执行对象
        Statement stmt = conn.createStatement();
        //5. 执行sql
        ResultSet rs = stmt.executeQuery(sql);
        //6. 处理结果
        List<Student> students = new ArrayList<>();

        while (rs.next()) {
            Student s = new Student();
            int id = rs.getInt(1);
            String name = rs.getString(2);
            String gender = rs.getString(3);

            s.setId(id);
            s.setName(name);
            s.setGender(gender);

            students.add(s);
        }
        System.out.println(students);
        //7. 释放资源
        rs.close();
        stmt.close();
        conn.close();
    }
}

运行结果:

image-20230124151820540

这样,我们的程序就达到了预期的效果,将来需要使用的数据全部封装为对象并存放在了集合中。

9. 增删改查操作练习

在数据库连接池入门篇中,我们学习了 Druid 连接池的使用,数据库连接池允许重复使用一个现有的数据库连接对象,提升了系统的响应速度和时间。下面我们使用数据库连接池来练习解决上面的问题,并且在获取 sql 执行对象时,我们使用 PreparedStatement 类,解决sql 注入的问题。

9.1 查询所有

查询所有数据,并把查询结果数据封装为对象存储在集合中,这里的数据表,Student 实体类和上面例子中相同。

public class DruidDemo {

    public static void main(String[] args) throws Exception {
        //加载配置文件
        Properties prop = new Properties();
        prop.load(new FileInputStream("jdbc-demo/src/druid.properties"));
        //获取数据库连接池对象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
        //获取数据库连接对象
        Connection conn = dataSource.getConnection();
        //定义sql
        String sql = "select * from student";
        //获取 sql 执行对象
        PreparedStatement pstmt = conn.prepareStatement(sql);
        //执行 sql
        ResultSet rs = pstmt.executeQuery();
        //处理数据,将查询结果数据封装为对象存储在集合中
        List<Student> students = new ArrayList<>();
  
        while (rs.next()) {
            Student s = new Student();
            int id = rs.getInt(1);
            String name = rs.getString(2);
            String gender = rs.getString(3);

            s.setId(id);
            s.setName(name);
            s.setGender(gender);

            students.add(s);
        }
        System.out.println(students);
        //释放资源
        rs.close();
        pstmt.close();
        conn.close();
    }
}

9.2 添加数据

现在演示往数据库中添加一条记录,应用场景为用户在客户端输入一条数据时,我们需要将数据添加到数据库。示例:

public class DruidDemo {

    public static void main(String[] args) throws Exception {
        //接收到用户的数据
        int id=4;
        String name="小美";
        String gender="女";
        //加载配置文件
        Properties prop = new Properties();
        prop.load(new FileInputStream("jdbc-demo/src/druid.properties"));
        //获取数据库连接池对象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
        //获取数据库连接对象
        Connection conn = dataSource.getConnection();
        //定义sql
        String sql = "insert into student values(?,?,?)";
        //获取 sql 执行对象
        PreparedStatement pstmt = conn.prepareStatement(sql);
        //设置参数
        pstmt.setInt(1,id);
        pstmt.setString(2,name);
        pstmt.setString(3,gender);
        //执行 sql
        int count = pstmt.executeUpdate();//返回受影响的行数
        //处理数据
        if(count>0){
            System.out.println("添加成功");
        }else{
            System.out.println("添加失败");
        }
        //释放资源
        pstmt.close();
        conn.close();
    }
}

image-20230124161437384

此时的数据表:

image-20230124162502370

9.3 修改数据

现在数据表中的数据,应用场景为用户在客户端修改数据,对应数据库中的数据也要完成修改。示例:

public class DruidDemo {

    public static void main(String[] args) throws Exception {
        //接收到用户的数据
        int id=1;
        String name="小王";
        String gender="女";
        //加载配置文件
        Properties prop = new Properties();
        prop.load(new FileInputStream("jdbc-demo/src/druid.properties"));
        //获取数据库连接池对象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
        //获取数据库连接对象
        Connection conn = dataSource.getConnection();
        //定义sql
        String sql = "update student set name=?,gender=? where id=?";
        //获取 sql 执行对象
        PreparedStatement pstmt = conn.prepareStatement(sql);
        //设置参数
        pstmt.setString(1,name);
        pstmt.setString(2,gender);
        pstmt.setInt(3,id);
        //执行 sql
        int count = pstmt.executeUpdate();//返回受影响的行数
        //处理数据
        if(count>0){
            System.out.println("修改成功");
        }else{
            System.out.println("修改失败");
        }
        //释放资源
        pstmt.close();
        conn.close();
    }
}

image-20230124161542027

此时的数据表:

image-20230124162612729

9.4 删除数据

下面演示删除数据,用户在客户端选择删除某条数据记录时,数据库中的数据也要完成删除操作。示例:

public class DruidDemo {

    public static void main(String[] args) throws Exception {
        //接收到用户的数据
        int id=4;
        //加载配置文件
        Properties prop = new Properties();
        prop.load(new FileInputStream("jdbc-demo/src/druid.properties"));
        //获取数据库连接池对象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
        //获取数据库连接对象
        Connection conn = dataSource.getConnection();
        //定义sql
        String sql = "delete from student where id=?";
        //获取 sql 执行对象
        PreparedStatement pstmt = conn.prepareStatement(sql);
        //设置参数
        pstmt.setInt(1,id);

        //执行 sql
        int count = pstmt.executeUpdate();//返回受影响的行数
        //处理数据
        if(count>0){
            System.out.println("删除成功");
        }else{
            System.out.println("删除失败");
        }
        //释放资源
        pstmt.close();
        conn.close();
    }
}

image-20230124161944039

此时的数据表:

image-20230124162647734

10. 总结

学习完这篇文章,你将学会使用 Java 代码操作数据库,包括数据库的连接,创建,数据的增删改查等操作。

JDBC 作为基础性的代码,在开发中其实存在着很多的缺点,所以在开发中并不会直接使用,而一般会使用框架开发,使用框架开发不仅能够提高开发的效率,而且大大提高了代码的规范性。MyBatis 就是一款优秀的持久层框架,专门用来简化 JDBC 开发,后面我们马上会接触到,但是在这之前,一定要先熟练 JDBC 的使用。


📂橙子精品文章学习推荐

❓很多朋友问我:怎样系统的学习一门编程语言?怎样学好 Java?强烈推荐大家学习:Java编程基础教程系列专栏(零基础小白搬砖逆袭),博主正处于学习成长阶段,更明白初学者的状态和需求,文章结合自身经验和经历,风趣幽默。本专栏主要更新 Java 基础,MySQL 数据库,JavaWeb,SSM 框架,SpringBoot 框架等,零基础小白搬砖逆袭,请相信自己。如图,专栏文章持续更新,快来和小伙伴们一起学习!!

在这里插入图片描述

Java编程基础教程系列 👈快速订阅

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1165995.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

不用动脑小白也能制作出精美的电子杂志

随着互联网技术的发展和微信的普及程度&#xff0c;电子杂志制作已经受到越来越多人的关注&#xff0c;毕竟电子杂志的阅读便利性是纸质杂志无法比拟的。那如何制作一本精美的电子杂志呢&#xff1f; 这其实很easy&#xff0c;可以使用在线电子杂志制作平台FLBOOK &#xff0c…

Linux使用挂载Windows共享文件夹

1、在linux下要挂载windows的共享文件&#xff0c;需要安装cifs-utils软件包。 dpkg -l|grep cifs-utils可以查看是否安装了这个软件包&#xff0c;which mount.cifs可以查看这个软件包安装在哪里。sudo apt-get install cifs-utils可以安装软件包。 2、建立一个目录用来作挂载…

AIR700 改变AIR530Z串口波特率

AIR530Z串口波特率默认9600&#xff0c;但实际应用中需要更高的波特率&#xff0c;可选波特率见下图。 主控采用合宙AIR700E核心板&#xff0c;通过UART1连接AIR530Z 合宙Luatos uart官方文档 主要问题 AIR700E要改变AIR530Z的波特率&#xff0c;在默认9600波特率情况下通过发…

跨境电商商城源码(支持多种支付方式+多语言+多货币+快速部署)

随着跨境电商的繁荣发展&#xff0c;支付方式的多样性已成为消费者和电商平台关注的焦点。本文将介绍一款支持多种支付方式的跨境电商源码&#xff0c;以提升用户的购物体验和平台的竞争力。 一、背景概述 近年来&#xff0c;跨境电商市场呈现出迅速增长的趋势。然而&#xff0…

建设城市展厅用的多媒体互动装置有哪些作用?

随着科技的迅速进步&#xff0c;智慧城市这一概念已经逐渐从理论走向现实&#xff0c;而智慧城市展厅则成为了集中展示智慧城市理念、技术和规划的重要场所&#xff0c;在其中发挥着重要的作用&#xff0c;并且还在建设这些展厅的过程中&#xff0c;应用了大量的多媒体互动装置…

fpmarkets总结的交易员5个阶段,您处在第几级

交易员的发展是一个逐步上升的阶梯式过程。总体上&#xff0c;这个过程被fpmarkets总结的五个阶段&#xff1a; 第一阶段&#xff0c;新手期。在这个阶段&#xff0c;新手交易者会接触到大量的市场工具和信息。主要目标是学习如何在市场中交易&#xff0c;同时避免产生亏损。许…

MySQL:至少参与xxx参与的全部事件(二)

MySQL&#xff1a;至少参与xxx参与的全部事件&#xff08;二&#xff09; – WhiteNights Site 标签&#xff1a;MySQL 本来不难的&#xff0c;结果实验课上又没能当场做出来。还是回到宿舍复盘才看到问题所在&#xff0c;令人感慨。 头歌例题 仔细审题 任务描述 创建一个名…

数据结构(超详细讲解!!)第二十节 数组

1.定义 1.概念 相同类型的数据元素的集合。 记作&#xff1a;A(A0,A1,…,Am-1) 二维数组可看作是每个数据元素都是相同类型的一维数组的一维数组。多维数组依此类推。 二维数组是数据元素为线性表的线性表。 A(A0&#xff0c;A1&#xff0c;……&#xff0c;An-1) 其中…

docker 下安装mysql8.0

在docker中查询mysql镜像 PS C:\Users\admin> docker search mysql NAME DESCRIPTION STARS OFFICIAL AUTOMATED mysql MySQL is a widely used, open-source relation……

每个外贸人都有一颗不甘的心

是不是每个三十多岁的外贸人都有一颗不安现状的心&#xff1f;总是想挑战一下自己的极限或者是拓宽一下自己未知的领域&#xff1f; 最近遇到一个经常去各个国家参加展会来获取客户的外贸人&#xff0c;他的第一话题不是自己去展会的效果如何&#xff0c;也不是说自己现在做的…

更好看更好听的百元真无线耳机,BarbetSound Buds T60上手

现在的真无线蓝牙耳机已经很成熟了&#xff0c;选择也特别多&#xff0c;大家很容易找到适合自己的款式。我比较关注耳机在续航、音质等方面的表现&#xff0c;最近用的是来自BarbetSound的Buds T60&#xff0c;这款耳机轻巧便携&#xff0c;而且音质非常好&#xff0c;性价比十…

【LLM】预训练||两句话明白儿的底层原理

预训练鼻祖阶段&#xff1a;目前认为是Bert时期 从字面上看&#xff0c;预训练模型&#xff08;pre-training model&#xff09;是先通过一批语料进行训练模型&#xff0c;然后在这个初步训练好的模型基础上&#xff0c;再继续训练或者另作他用。这样的理解基本上是对的&#…

学习笔记|两组率卡方检验和Fisher确切法|适用条件|规范表达|《小白爱上SPSS》课程:SPSS第十五讲 | 两组率卡方检验和Fisher确切法怎么做?

目录 学习目的软件版本原始文档两组率卡方检验和Fisher确切法适用条件简述一、实战案例读数据&#xff1a; 二、统计策略三、SPSS操作四、结果解读第一&#xff0c;分组统计描述结果第二&#xff0c;卡方检验。 五、规范报告1、规范表格2、规范文字 学习目的 SPSS第十五讲 | 两…

Scala语言用Selenium库写一个爬虫模版

首先&#xff0c;我将使用Scala编写一个使用Selenium库下载yuanfudao内容的下载器程序。 然后我们需要在项目的build.sbt文件中添加selenium的依赖项。以下是添加Selenium依赖项的代码&#xff1a; libraryDependencies "org.openqa.selenium" % "selenium-ja…

Web APIs——节点操作

1、DOM节点 DOM节点&#xff1a;DOM树里每一个内容都称之为节点 节点类型&#xff1a; 元素节点 所有的标签 比如body、div属性节点 所有的属性 比如 href文本节点 所有的文本其他 2、查找节点 关闭二维码案例&#xff1a; 点击关闭按钮&#xff0c;关闭的是二维码的盒子&#…

Maven系列第10篇:设计你自己的maven插件【高手必备】

Maven默认提供了很多插件&#xff0c;功能也非常强大&#xff0c;但是如果我们想自己开发一些插件&#xff0c;比如自定义一款自动打包并且发布到服务器然后重启服务器的插件&#xff1b;或者定义一款插件自动打包自动运行打包好的构件。各种好玩的东西只要你能想到&#xff0c…

Python中表单的处理

目录 1. 表单的基本概念 2. 使用Flask处理表单 3. 表单验证 4. 数据存储 5. 使用数据库存储表单数据 6. 安全性考虑 总结 当我们谈论Web开发时&#xff0c;表单处理是一个不可避免的话题。表单是Web应用程序中用户输入数据的界面&#xff0c;而处理这些数据则是后端开发…

从零开始制作一个割草机器人

项目背景 为啥要做一个割草机器人呢&#xff1f;&#xff08;个人因素&#xff1a;我梦想就是做一款人形机器人保护人类&#xff0c;解放人类&#xff09; 基础准备&#xff1a;我们公司本身做过高精度&#xff0c;基于高精度的技术扩展到农机自动化驾驶。目前可以实现AB线拖…

【qemu逃逸】BlizzardCTF2017-strng

前言 虚拟机用户名&#xff1a;ubuntu 密码&#xff1a;passw0rd 一道入门题&#xff0c;看下启动脚本&#xff1a; ./qemu-system-x86_64 \-m 1G \-device strng \-hda my-disk.img \-hdb my-seed.img \-nographic \-L pc-bios/ \-enable-kvm \-device e1000,netdevnet0 \…

微信自动回复有什么好处?怎么设置?

微信自动回复有什么好处&#xff1f; 首先&#xff0c;它可以帮助我们节省时间和精力。当我们无法立即回复消息时&#xff0c;自动回复可以及时告知对方我们的状况&#xff0c;避免对方的焦虑和不便。 其次&#xff0c;自动回复可以增强沟通效率。无论是工作还是个人生活中&a…