JDBC入门

发布于:2024-12-06 ⋅ 阅读:(25) ⋅ 点赞:(0)

JDBC入门

JDBC(Java Database Connectivity)是 Java 提供的一种标准 API,用于连接和操作关系型数据库。它是 Java 程序和数据库之间的桥梁,允许开发人员通过 Java 代码与数据库交互,执行查询、更新和其他数据库操作。

一、JDBC的快速入门

  1. 官网下载数据库连接驱动jar包。https://downloads.mysql.com/archives/c-j/
  2. 创建Java项目,在项目下创建lib文件夹,将下载的驱动jar包复制到文件夹里
  3. 选中lib文件夹右键->Add as Library,与项目集成

创建数据库

create database db05;

use db05;

create table t_emp
(
    emp_id     int auto_increment comment '员工编号'
        primary key,
    emp_name   varchar(100)  not null comment '员工姓名',
    emp_salary double(10, 5) not null comment '员工薪资',
    emp_age    int           not null comment '员工年龄'
);

insert into t_emp (emp_name,emp_salary,emp_age)
values  ('andy', 777.77, 32),
        ('大风哥', 666.66, 41),
        ('康师傅',111, 23),
        ('Gavin',123, 26),
        ('小鱼儿', 123, 28);

编写代码

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class JDBCQuick {
    public static void main(String[] args) throws Exception {
        // 1、注册驱动
        // 将数据库厂商提供的驱动类,通过类加载的方式加载到我们的程序当中
        Class.forName("com.mysql.cj.jdbc.Driver");

        // 2、获取连接对象
        String url = "jdbc:mysql://localhost:3306/db05";
        String username = "root";
        String password = "password";
        Connection connection = DriverManager.getConnection(url, username, password);

        // 3、获取执行SQL语句的对象
        Statement statement = connection.createStatement();

        // 4、编写SQL语句并执行,接收返回的结果集
        String sql = "select emp_id, emp_name, emp_salary, emp_age from t_emp";
        ResultSet resultSet = statement.executeQuery(sql);

        // 5、处理结果:遍历resultSet结果集
        // resultSet.next()是判断下一行是否存在数据,并返回布尔类型的值
        while(resultSet.next()) {
            int empId = resultSet.getInt("emp_id");
            String s = resultSet.getString("emp_name");
            double empSalary = resultSet.getDouble("emp_salary");
            int empAge = resultSet.getInt("emp_age");
            System.out.println(empId + "\t" + s + "\t" + empSalary + "\t" + empAge);

        }

        // 6、释放资源(先开后关原则)
        resultSet.close();
        statement.close();
        connection.close();
    }
}

二、代码核心API的理解

1、注册驱动

Class.forName("com.mysql.cj.jdbc.Driver");

  • 在 Java 中,当使用JDBC (Java Database Connectivity)连接数据库时,需要加载数据库特定的驱动程序,以使与数据库进行通信。加载驱动程序的目的是为了注册驱动程序,使得JDBC API能够识别并与特定的数据库进行交互
  • 在指定路径的Driver文件中,核心代码就是DriverManager.registerDriver(new Driver());相当于是底层的创建驱动,因此在java代码中也可以使用这行代码进行创建
  • JDK6开始,不再需要显式地调用Class.forName()来加载JDBC驱动程序,只要在类路径中集成了对应的jar文件,会自动在初始化时注册驱动程序

在这里插入图片描述

2、连接对象Connection

  • Connection接口是JDBC API的重要接口,用于建立与数据库的通信通道,只要Connection对象不为空,则代表一次数据库连接。

  • 在建立连接时,需要指定的数据库URL、用户名、密码参数。

    • URL:jdbc:mysql://localhost:3306/db05
    • jdbc:mysql://IP地址:端口号/数据库名称?参数键值对1&参数键值对2
    • 如果要连接的是本机的数据库,则可以简写为jdbc:mysql:///db05,省略本机的IP地址:端口号
  • Connection接口还负责管理事务,Connection接口提供了commitrollback方法,用于提交事务和回滚事务

  • 可以创建Statement对象,用于执行SQL语句并与数据库进行交互

  • 在使用JDBC技术时,必须要先获取Connection对象,在使用完毕后,要释放资源,避免资源占用浪费及泄漏

3、Statement

  • Staticment接口用于执行SQL语句并与数据库进行交互。它是JDBC API中的一个重要接口。通过Staticment对象,可以向数据库发送SQL语句并获取执行结果
  • 结果可以是一个或多个结果
    • 增删改:受影响行数单个结果
    • 查询:单行单列、多行多列、单行多列等结果
  • 但是Statement接口在执行SQL语句时,会产生SQL注入攻击问题:
    • 当使用Statement执行动态构建的SQL查询时,往往需要将查询条件与SQL语句拼接在一起,直接将参数SQL语句一并生成,让SQL的查询条件始终为true得到结果

SQL注入的演示:

import javax.lang.model.util.SimpleAnnotationValueVisitor6;
import java.sql.*;
import java.util.Scanner;

public class JDBCInjection {
    public static void main(String[] args) throws Exception {
        //Sql注入问题

        // 1、注册驱动

        // 2、获取连接对象
        Connection connection = DriverManager.getConnection("jdbc:mysql:///db05", "root", "password");

        // 3、获取执行SQL语句的对象
        Statement statement = connection.createStatement();

        System.out.println("请输入员工姓名:");
        Scanner in = new Scanner(System.in);
        // 允许输入空格
        String name = in.nextLine();

        // 4、编写SQL语句,并执行,接收返回的结果
        String sql = "select emp_id,emp_name,emp_salary,emp_age from t_emp where emp_name = '" + name + "'";
        ResultSet resultSet = statement.executeQuery(sql);

        // 5、处理结果,遍历resultSet
        while(resultSet.next()) {
            int empId = resultSet.getInt("emp_id");
            String s = resultSet.getString("emp_name");
            double empSalary = resultSet.getDouble("emp_salary");
            int empAge = resultSet.getInt("emp_age");
            System.out.println(empId + "\t" + s + "\t" + empSalary + "\t" + empAge);
        }

        // 6、释放资源(先开后关)
        resultSet.close();
        statement.close();
        connection.close();

    }
}

当从键盘获取的数据为:

在这里插入图片描述

组合后的语句就是select emp_id,emp_name,emp_salary,emp_age from t_emp where emp_name = 'abc' or '1' = '1',也就是只要有一个条件满足就会执行(自己加一个true条件),就会查询到数据库中的所有结果

4、PreparedStatement

  • 预防sql注入,将传入的值都变成字符,用户输入的单引号也会自动加上转义字符,进行成功预防sql注入

  • PreparedStatementStatement接口的子接口,用于执行预编译的SQL查询,作用如下:

    • 预编译SQL语句:在创建PreparedStatement时,就会预编译SQL语句,也就是SQL语句已经固定
    • 防止SQL注入:PreparedStatement支持参数化查询,将数据作为参数传递到SQL语句中,采用?占位符的方式,将传入的参数用一对单引号包裹起来,无论传递什么都作为值。有效防止传入关键字或值导致SQL注入问题
    • 性能提升:PreparedStatement是预编译SQL语句,同一SQL语句多次执行的情况下,可以复用,不必每次重新编译和解析
import java.sql.*;
import java.util.Scanner;

public class JDBCPrepared {
    public static void main(String[] args) throws Exception {
        //使用PrepareStatement来预防Sql注入问题

        // 1、注册驱动

        // 2、获取连接对象
        Connection connection = DriverManager.getConnection("jdbc:mysql:///db05", "root", "password");

        // 3、获取执行SQL语句的对象
        PreparedStatement preparedStatement = connection.prepareStatement("select emp_id, emp_name, emp_salary, emp_age from t_emp where emp_name = ?");



        System.out.println("请输入员工姓名:");
        Scanner in = new Scanner(System.in);
        // 允许输入空格
        String name = in.nextLine();

        // 4、为?占位符赋值,并执行SQL语句,接收返回的结果
        // 索引是从占位符的1开始的,而不是0
        preparedStatement.setString(1, name);
        ResultSet resultSet = preparedStatement.executeQuery();

        // 5、处理结果,遍历resultSet
        while(resultSet.next()) {
            int empId = resultSet.getInt("emp_id");
            String s = resultSet.getString("emp_name");
            double empSalary = resultSet.getDouble("emp_salary");
            int empAge = resultSet.getInt("emp_age");
            System.out.println(empId + "\t" + s + "\t" + empSalary + "\t" + empAge);
        }

        // 6、释放资源(先开后关)
        resultSet.close();
        preparedStatement.close();
        connection.close();

    }
}

5、ResultSet

  • ResultSetJDBC API中的一个接口,用于表示从数据库中执行查询语句所返回的结果集。它提供了一种用于遍历和访问查询结果的方式
  • 遍历结果:ResultSet可以使用next()方法将游标移动到结果集的下一行,逐行遍历数据库查询到结果,返回值为boolean类型,true代表有下一行结果,false则代表没有
  • 获取单列结果:可以通过getXxx的方法获取单列的数据,该方法为重载方法,支持索引和列名进行获取

三、基于PreparedStatement实现CRUD

CRUD的理解

CRUD 是数据库和软件开发中常用的术语,表示对数据的基本操作,包含四种功能:

  • Create(创建):插入或添加新的数据。
  • Read(读取):查询或读取现有的数据。
  • Update(更新):修改已有的数据。
  • Delete(删除):移除数据。

这些操作是管理和操作数据库中数据的核心,是所有数据库应用程序和接口设计的基础。

1、查询单行单列

 @Test
    public void testQuerySingleRowAndCol() throws Exception {
        // 1.注册驱动
        // 2.获取连接
        Connection connection = DriverManager.getConnection("jdbc:mysql:///db05", "root", "password");
        // 3.预编译SQL语句,得到PreparedStatement对象
        PreparedStatement preparedStatement = connection.prepareStatement("select count(*) as count from t_emp");
        // 4.执行SQL语句,获取结果
        ResultSet resultSet = preparedStatement.executeQuery();
        // 5.处理结果(如果自己明确只有一个结果,那么resultSet最少要做一次next的判断,才能拿到我们要的列的结果)
        while(resultSet.next()) {
            // 对于结果是一行一列的情况,可以直接使用下标获取
            System.out.println(resultSet.getInt(1));	// 输出: 5
            // 也可以使用别名
            System.out.println(resultSet.getInt("count"));	// 输出: 5
        }
        // 6.关闭资源
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }

在这里插入图片描述

注意事项:

  • 使用.next()方法时,查询开始的第一行是在字段名的那一栏,所有即使是单行,仍然需要调用该方法,否则编译错误
  • 如果已知结果就是单行单列,也可以直接使用下标1获取
  • 如果名字较为复杂时,也可以通过给字段名取别名的形式使用别名的字段标签获取

2、查询单行多列

@Test
    public void testQuerySingleRow() throws Exception {
        // 1.获取驱动
        // 2.获取连接
        Connection connection = DriverManager.getConnection("jdbc:mysql:///db05", "root", "password");
        // 3.预编译SQL语句获取PreparedStatement对象
        PreparedStatement preparedStatement = connection.prepareStatement("select emp_id,emp_name,emp_salary,emp_age from t_emp where emp_id = ?");

//        System.out.println("请输入要查找的员工ID:");
//        Scanner in = new Scanner(System.in);
//        int id = in.nextInt();

        // 给占位符?赋值
        preparedStatement.setInt(1, 5);

        // 4.执行,并接收结果
        ResultSet resultSet = preparedStatement.executeQuery();

        // 5.处理结果
        while(resultSet.next()) {
            int empId = resultSet.getInt("emp_id");
            String empName = resultSet.getString("emp_name");
            double empSalary = resultSet.getDouble("emp_Salary");
            int empAge = resultSet.getInt("emp_age");
            System.out.println(empId + "\t" + empName + "\t" + empSalary + "\t" + empAge);
        }

        // 6.资源释放
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }

在这里插入图片描述

注意事项:

  • 使用占位符的时候,一定要给占位符赋值
  • 如果使用注解@Test,测试方法在运行时,无法接收用户的输入,如果想要接收,则可以使用main方法
  • 同样的,即使只有一行结果,仍然需要先调用一次.next()方法

3、查询多行多列

@Test
    public void testQueryMoreRow() throws Exception {
        Connection connection = DriverManager.getConnection("jdbc:mysql:///db05", "root", "password");
        PreparedStatement preparedStatement = connection.prepareStatement("select emp_id, emp_name, emp_salary, emp_age from t_emp where emp_age > ?");
        preparedStatement.setInt(1, 25);
        ResultSet resultSet = preparedStatement.executeQuery();
        while (resultSet.next()) {
            int empId = resultSet.getInt("emp_id");
            String empName = resultSet.getString("emp_name");
            double empSalary = resultSet.getDouble("emp_Salary");
            int empAge = resultSet.getInt("emp_age");
            System.out.println(empId + "\t" + empName + "\t" + empSalary + "\t" + empAge);
        }
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }

在这里插入图片描述

4、插入数据

@Test
    public void testInsert() throws Exception {
        Connection connection = DriverManager.getConnection("jdbc:mysql:///db05", "root", "password");
        PreparedStatement preparedStatement = connection.prepareStatement("insert into t_emp (emp_name, emp_salary, emp_age) VALUES (?, ?, ?)");
        preparedStatement.setString(1, "小明");
        preparedStatement.setDouble(2, 15000.26);
        preparedStatement.setInt(3, 25);

        int result = preparedStatement.executeUpdate();
        if (result > 0) {
            System.out.println("成功!");
        }else {
            System.out.println("失败!");
        }

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

注意事项:

  • 添加完数据只用通过调用preparedStatement.executeUpdate()方法判断此作用的影响行即可,大于0,则表示插入成功;反之失败
  • 该方法无需调用.executeQuery()创建ResultSet对象,因此释放内存时也只用释放两个变量

5、修改数据

@Test
    public void testUpdate() throws Exception {
        Connection connection = DriverManager.getConnection("jdbc:mysql:///db05", "root", "password");
        PreparedStatement preparedStatement = connection.prepareStatement("update t_emp set emp_salary = ? where emp_id = ?");
        preparedStatement.setDouble(1, 23000.66);
        preparedStatement.setInt(2, 5);

        int result = preparedStatement.executeUpdate();
        if (result > 0) {
            System.out.println("成功!");
        }else {
            System.out.println("失败!");
        }
        preparedStatement.close();
        connection.close();

    }

注意事项:

  • 修改数据和插入数据一样,都不需要调用.executeQuery()创建ResultSet对象,且调用的判断方法也是.executeQuery()

6、删除数据

@Test
    public void testDelete() throws Exception {
        Connection connection = DriverManager.getConnection("jdbc:mysql:///db05", "root", "password");
        PreparedStatement preparedStatement = connection.prepareStatement("delete from t_emp where emp_name = ?");
        preparedStatement.setString(1, "小明");
        int result = preparedStatement.executeUpdate();
        if (result > 0) {
            System.out.println("成功!");
        }else {
            System.out.println("失败!");
        }
        preparedStatement.close();
        connection.close();
    }

四、可能会出现的问题

1、资源的管理

  • 在使用JDBC相关的资源时,使用完毕后需要及时关闭这些资源以释放数据库服务器资源和避免内存泄漏是很重要的

2、SQL语句问题

  • java.sql.SQLSyntaxErrorExceptionSQL语句错误:
    1. SQL语句有错误,建议先在sql工具中调试好
    2. 连接数据库的URL中,数据库名称编写错误,也会报这个错误

3、SQL语句未设置参数问题

  • java.sql.SQLException: No value specified for parameter 1在使用预编译的SQL语句时,如果有?占位符,要为给每一个占位符赋值,否则就会报该错

4、用户名或密码错误问题

  • 连接数据库时,如果用户名或密码输入错误,也会报SQLException,看清楚后面错误的描述

5、通信异常

  • CommunicationsException在连接数据库的URL中如果IP或端口写错了,就会报这种错误

资源以释放数据库服务器资源和避免内存泄漏是很重要的

2、SQL语句问题

  • java.sql.SQLSyntaxErrorExceptionSQL语句错误:
    1. SQL语句有错误,建议先在sql工具中调试好
    2. 连接数据库的URL中,数据库名称编写错误,也会报这个错误

3、SQL语句未设置参数问题

  • java.sql.SQLException: No value specified for parameter 1在使用预编译的SQL语句时,如果有?占位符,要为给每一个占位符赋值,否则就会报该错

4、用户名或密码错误问题

  • 连接数据库时,如果用户名或密码输入错误,也会报SQLException,看清楚后面错误的描述

5、通信异常

  • CommunicationsException在连接数据库的URL中如果IP或端口写错了,就会报这种错误

网站公告

今日签到

点亮在社区的每一天
去签到