初学者学习Java与数据库的连接:JDBC

128 阅读9分钟

一.JDBC

1.1 JDBC概念

JDBC(Java Database Connectivity)是Java编程语言中用于连接数据库的一种API(应用程序接口)。它允许Java应用程序与数据库进行交互,执行SQL语句,并处理结果。JDBC是Java EE(现在称为Jakarta EE)的一部分,但它也可以被用于任何Java应用程序中,包括Java SE(标准版)应用程序。

JDBC的主要功能包括:

  1. 建立与数据库的连接:JDBC通过加载数据库驱动程序(Driver)来建立与数据库的连接。
  2. 发送SQL语句到数据库:通过JDBC,可以发送SQL语句到数据库,这些SQL语句可以是数据查询(SELECT),数据更新(INSERT、UPDATE、DELETE)或数据定义(CREATE TABLE等)语句。
  3. 处理数据库返回的结果:执行SQL语句后,JDBC提供了一系列方法来处理数据库返回的结果,包括遍历查询结果集(ResultSet),获取数据行中的字段值等。

JDBC API的核心类包括:

  • DriverManager:用于管理JDBC驱动程序。它用于建立与数据库的连接。
  • Connection:代表与数据库的连接。它提供了创建SQL语句和调用这些语句的方法。
  • Statement 和 PreparedStatement:用于执行SQL语句。PreparedStatement是Statement的子类,它允许SQL语句被预编译并存储在PreparedStatement对象中,从而可以多次执行以提高性能。
  • ResultSet:代表SQL查询的结果集。它提供了一系列方法来遍历和检索数据。

使用JDBC时,通常需要按照以下步骤进行:

  1. 加载和注册JDBC驱动:通过Class.forName()方法加载JDBC驱动类,并注册到DriverManager中。
  2. 建立数据库连接:通过DriverManager的getConnection()方法建立与数据库的连接。
  3. 创建Statement或PreparedStatement:使用连接对象(Connection)创建Statement或PreparedStatement对象。
  4. 执行SQL语句:通过Statement或PreparedStatement对象执行SQL语句。
  5. 处理结果:如果SQL语句是查询语句,则需要处理ResultSet对象来遍历和检索查询结果。
  6. 关闭连接:完成数据库操作后,需要关闭ResultSet、Statement和Connection对象,以释放数据库资源。

JDBC为Java应用程序提供了灵活且强大的数据库连接和操作能力,是Java开发人员进行数据库编程的重要工具之一。

1.2 连接数据库

  1. 获取驱动(可以省略)
  2. 获取连接
  3. 获取Statement对象
  4. 处理结果集(只在查询时处理)
  5. 释放资源

使用getConnection()方法

package com.jdbc.demo01;

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

public class JDBCDemo01 {

    public static void main(String[] args) throws Exception {
        //1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2.获取连接 connection连接对象
        String url="jdbc:mysql://localhost:3306/db4?characterEncoding=UTF-8";
     Connection con= DriverManager.getConnection(url,"root","123123");
        // 打印 连接对象com.mysql.jdbc.JDBC4Connection@2d6e8792
        System.out.println(con);

        //3.获取语句执行平台 Statement
        Statement statement = con.createStatement();
        //3.1 通过statement对象的方法 executeUpdate 方法 创建一张表
        String sql = "create table test(id int,name varchar(20),age int);";
        int i = statement.executeUpdate(sql); //返回值是int类型 表示受影响的行数
        System.out.println(i);

        //关闭流(先开后关 先开的最后关)
        statement.close();
        con.close();
    }
}

以上指令输入完毕后 可以发现数据库中已经创造出来一个表 image.png

URL组成 jdbc:mysql: // localhost: 3306/db4 ? 参数名=参数值

jdbc:mysql 为协议

localhost:3306 主机:端口

db4 数据库

1.3 JDBC开发_处理结果集对象

1.3(1) ResultSet接口

作用:封装数据库查询的结果集,对结果集进行遍历,取出每一条记录。

ResultSet接口方法

  1. boolean next()(判断是否有下一条数据 返回boolean)

(1) 游标向下一行

(2) 返回 boolean 类型,如果还有下一条记录,返回 true,否则返回 false

  1. xxx getXxx( String or int)

(1)通过列名,参数是 String 类型。返回不同的类型

image.png (2)通过列号,参数是整数(int),从 1 开始。返回不同的类型

image.png

完整代码过程:

package com.jdbc.demo01;

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

public class JDBCDemo02 {
    public static void main(String[] args) throws Exception {
        //1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2.获取连接
        Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db4", "root", "123123");
        //3.获取语句执行平台对象
        Statement statement = con.createStatement();
        //4.执行查询操作
        String sql = "SELECT * FROM jdbc_user;";
        //resultSet 是结果集对象
        ResultSet resultSet = statement.executeQuery(sql);
        //通过while循环 遍历获取resultSet中的数据
        while (resultSet.next()){
            //获取id
            int id = resultSet.getInt("id");
            //获取姓名
            String username = resultSet.getString("username");
            //获取密码
            String password = resultSet.getString("password");
            //获取生日
            Date birthday = resultSet.getDate("birthday");
            System.out.println(id+":"+username+":"+password+":"+birthday);

        }

        //5.关闭流操作
        resultSet.close();
        statement.close();
        con.close();
        //处理结果集对象 resulSet
//        boolean next = resultSet.next();
 //       System.out.println(next);
        //获取id 通过列名方式获取id
        //       int id = resultSet.getInt("id");
        //      System.out.println(id);
        //通过列号获取id
  //      int anInt =resultSet.getInt(1);
   //    System.out.println("通过列号获取id:"+anInt);

    }
}

1.4 释放资源

1.需要释放的对象:ResultSet 结果集,Statement 语句,Connection 连接

2.释放原则:先开的后关,后开的先关。ResultSet ==> Statement ==> Connection

3.放在哪个代码块中:finally 块 与IO流一样,使用后的东西都需要关闭!关闭的顺序是先开后关, 先得到的后关闭,后得到的先关闭

image.png

package com.jdbc.demo01;

import java.sql.*;

public class JDBCDemo03 {

    ResultSet resultSet= null;
    Connection connection=null;
    Statement statement=null;

    {
        try {
            connection = DriverManager.getConnection("","","");
           statement = connection.createStatement();
            String sql = "select * from jdbc_user";
             resultSet = statement.executeQuery(sql);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                resultSet.close();
                statement.close();
                connection.close();

            } catch (SQLException e) {
                throw new RuntimeException(e);
            }

        }
    }
}

1.5 编写JDBC工具类

JDBC本身是一组Java API,用于与数据库进行交互,但直接使用JDBC API进行数据库操作往往会导致代码冗余、难以维护以及可能的数据库连接泄露等问题。因此,JDBC工具类的作用主要是为了解决这些问题,提供更为简洁、高效、安全的数据库操作方式。

package utils;

import java.sql.*;

/**
 * JDBD工具类
 */
public class JDBCutils {
    //1.将连接信息定义为字符串常量
    public static final String DRIVERNAME = "com.mysql.jdbc.Driver";
    public static final String URL = "jdbc:mysql://localhost:3306/db4";
    public static final String USER ="root";
    public static final String PASSWORD = "123123";
    //2.静态代码块
    static {
        try {
            //1.注册驱动
            Class.forName(DRIVERNAME);
        }catch (ClassNotFoundException e){
            e.printStackTrace();



        }




    }
    //3.获取连接的静态方法
    public static Connection getConnection(){

        try {
            //获取连接对象 并返回
            Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
            return connection;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }


    }
    // 4.关闭资源的方法
    public static void close(Connection con, Statement statement,ResultSet resultSet){
        if(con != null && statement != null){
            try {
                statement.close();
                con.close();
                resultSet.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
          

        }



    }
}

1.6 DML操作(增删改)

代码示例:

package jdbc02;

import org.junit.Test;
import utils.JDBCutils;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;


public class TestDML {
    /*
      *插入数据
     */
    @Test
    public void testInsert() throws SQLException {
        //1.通过JDBCUtils工具类 获取连接
        Connection con = JDBCutils.getConnection();
        //2.获取Statement对象
        Statement statement = con.createStatement();
        //2.1编写SQL
        String sql = "insert into jdbc_user values(null,'张百万','123','2020/11/11')";
        //2.2执行SQL
        int i = statement.executeUpdate(sql);
        System.out.println(i);
        //3.关闭流
        JDBCutils.close(con,statement);

    }
    /**
     * 更新操作 根据id修改用户名
     */
    @Test
    public void testUpdate() throws SQLException{
        Connection connection = JDBCutils.getConnection();
        Statement statement = connection.createStatement();
        String sql = "update jdbc_user set username = 'liuneng' where id = 1";
        statement.executeUpdate(sql);
        JDBCutils.close(connection,statement);

    }
    /**
     * 删除操作
     */
    @Test
    public void testDelete() throws SQLException{
        Connection connection = JDBCutils.getConnection();
        Statement statement = connection.createStatement();
        String sql = "delete from jdbc_user where id in(1,2)";
        statement.executeUpdate(sql);
        JDBCutils.close(connection,statement);

    }
}

1.6 DQL操作(查询)

代码示例:

package jdbc02;

import utils.JDBCutils;
import java.util.Date;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class TestDQL {
    //查询姓名为liuneng的一条记录
    public static void main(String[] args) throws SQLException {
      //1.获取连接
        Connection connection = JDBCutils.getConnection();
       //2.创建Statement对象
        Statement statement = connection.createStatement();
        //3.编写sql

        String sql = "select * from jdbc_user where username = 'test1'";

        ResultSet resultSet = statement.executeQuery(sql);
        //4.处理结果集
        while (resultSet.next()){
            int id = resultSet.getInt("id");
            String username = resultSet.getString("username");
            String password = resultSet.getString("password");
            Date birthday = resultSet.getDate("birthday");
            System.out.println(id+":"+username+":"+password+":"+birthday);



        }
        //5.释放资源
        JDBCutils.close(connection,statement,resultSet);
    }
}

1.7 SQL注入问题的出现与解决(使用预处理对象)

SQL注入(SQL Injection)是一种常见的网络攻击手段,它允许攻击者将恶意的SQL代码插入到应用程序的数据库查询中,从而控制后台数据库服务器。这种攻击方法利用了应用程序没有对用户输入进行充分验证或清理的漏洞。当应用程序使用用户输入来构造数据库查询时,如果没有进行适当的过滤或转义,攻击者就可以注入恶意的SQL代码,执行未授权的数据库操作,如数据泄露、数据篡改、甚至执行系统命令(取决于数据库服务器的配置和权限)。

**我们让用户输入的密码和 SQL 语句进行字符串拼接。用户输入的内容作为了 SQL 语句语法的一部分,改变了 原有 SQL 真正的意义,以上问题称为 SQL 注入 ** 代码演示:

插入两条数据

INSERT INTO jdbc_user VALUES(NULL,'jack','123456','2020/2/24');

INSERT INTO jdbc_user VALUES(NULL,'tom','123456','2020/2/24')

填写一个错误的密码

SELECT * FROM jdbc_user WHERE username = 'tom' AND PASSWORD = '123' OR '1' = '1'

如上,即使密码是错误的 也会登陆成功.

解决SQL注入:

PreparedStatement 接口介绍

PreparedStatement 是 Statement 接口的子接口,继承于父接口中所有的方法。它是一个预编译的 SQL 语 句对象.

预编译:  是指SQL 语句被预编译,并存储在 PreparedStatement 对象中。然后可以使用此对象多次高效地执行 该语句。

特点:

因为有预先编译的功能,提高 SQL 的执行效率。

可以有效的防止 SQL 注入的问题,安全性更高

如何获取PreparedStatement对象

image.png

PreparedStatement接口常用方法

image.png

使用PreparedStatement的步骤:

1.获取数据库连接对象

2.编写SQL 使用? 占位符方式

3.获取预处理对象 (预编译对象会将Sql发送给数据库 进 行预编译)

4.提示用户输入用户名 & 密码

5.设置实际参数:setXxx(占位符的位置, 真实的值)

6.执行查询获取结 果集

7.判断是否查询到数据

8.关闭资源

package jdbc03;

import utils.JDBCutils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;

public class TestLogin02 {

    public static void main(String[] args) throws SQLException {
        //1.获取连接
        Connection con = JDBCutils.getConnection();
        //2.获取PreparedStatement 预处理对象
        //使用?占位符的方式来设置参数
        String sql = "select * from jdbc_user where username = ? and password = ?";
        PreparedStatement ps =con.prepareStatement(sql);
        //3.获取用户输入的用户名和密码
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String name = sc.nextLine();
        System.out.println("请输入密码:");
        String pass = sc.nextLine();
        //4.设置参数 使用setXXX(占位符的位置(整数),要设置的值)的方法设置占位符的参数)
        ps.setString(1,name);
        ps.setString(2,pass);
        //5.执行查询
        ResultSet resultSet = ps.executeQuery();
        //6.处理结果集
        if(resultSet.next()){

            System.out.println("登录成功! 欢迎您: " + name);
        }else {

            System.out.println("登录失败!");
        }
         //7.关闭流
        JDBCutils.close(con,ps,resultSet);
    }

}

Statement 与 PreparedStatement的区别?

  • Statement用于执行静态SQL语句,在执行时,必须指定一个事先准备好的SQL语句。
  • PrepareStatement是预编译的SQL语句对象,语句中可以包含动态参数“?”,在执行时可以为“?”动态设置参数 值。
  • PrepareStatement可以减少编译次数提高数据库性能。

image.png