JDBC_Java Database Connectivity

JDBC

JDBC 概述

Java Database Connectivity

由sun公司制定,是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了很多直接操作数据库的增删改查方法

JDBC 核心包

java.sql.*
javax.sql.*

JDBC - MySQL【重点】

用来连接和操作MySQL数据库,需要导入一个核心jar包

【核心 jar 文件】

mysql-connector-java-5.1.47.jar

【注意】此jar包可以直接在MySQL官网进行下载(比较慢),或者通过 Maven 仓库也能下载(这里能下载到大部分能用到的 jar 包,建议收藏

https://mvnrepository.com/artifact/mysql/mysql-connector-java/5.1.47

在这里插入图片描述

通过 JDBC 连接 MySQL 数据库

前提条件

1、jar包:连接驱动包

2、url:确定连接的数据库所在网络地址和对应操作哪一个数据库,由协议名+子协议名+数据源名构成,即
		jdbc:mysql://主机地址:端口号/数据库名
		
		【例】jdbc:mysql://localhost:3306/fc2020

3、user:用户名

4、password:密码

格式

1、加载驱动
2、准备必要的连接参数
3、获取数据库连接
4、关闭资源!!!

案例代码

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * 测试数据库连接
 */
public class Demo1 {
    public static void main(String[] args) {

        try {
            // 加载驱动
            Class.forName("com.mysql.jdbc.Driver");

            // 声明连接数据库所需要的参数,包括但不限于IP地址、端口号、连接到哪个数据库以及相关参数、用户名、密码
            String url = "jdbc:mysql://localhost:3306/FC2020?useSSL=true&characterEncoding=utf8";
            String user = "root";
            String password = "root";

            // 通过参数获取连接
            Connection connection = DriverManager.getConnection(url, user, password);

            System.out.println(connection);

            // 关闭资源
            connection.close();
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
    }
}

【注意】连接数据库可以配置一些相关的参数,数据库名与参数之间用 ? 进行分隔,参数之间通过 & 分隔,常用参数及格式如下

参数描述
characterEncoding=utf8设置字符集为UTF8
serverTimezone=Asia/Shanghai设置时区(JDBC8.0以上必备)
useSSL=true使用SSL(JDBC8.0以上要改为false)
jdbc:mysql://localhost:3306/FC2020?useSSL=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai

数据库驱动加载过程

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    /*
     * Register ourselves with the DriverManager
     * 在.class文件加载到内存时运行,并且有且只执行一次
     * 代码初始化过程!!!
     */ 
    static {
        try {
            // DriverManager驱动管理器注册了当前com.mysql.jdbc.Driver
            // 相对于当前Java程序拥有了连接MySQL数据库的必要的驱动条件
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }

    /**
     * Construct a new driver and register it with DriverManager
     * 
     * @throws SQLException
     *             if a database error occurs.
     */
    public Driver() throws SQLException {
        // Required for Class.forName().newInstance()
    }
}

【注意】后续会用到静态代码块去完成一些初始化的操作

JDBC 核心API

// 驱动管理类
class java.sql.DriverManager
   
/*
 * 这里是根据数据库连接URL,对应的user用户名和password密码,获取数据库连接对象
 */
static java.sql.Connection getConnection(String url, String user, String password);

// 数据库连接接口
interface java.sql.Connection 
 
/*
 * 获取数据库SQL语句搬运工对象,从Java程序搬运SQL语句到数据库中,同时Statement也是一个资源对象。
 */
java.sql.Statement createStatement();

/*
 * 获取数据库SQL语句【预处理】搬运工对象,Java程序的SQL语句,在创建PreparedStatement对象时,将SQL语句交给数据库预处理操作,可以解决一定的【SQL语句注入问题】,同时提高一定的效率,PreparedStatement也是一个资源对象
 */
java.sql.PreparedStatement prepareStatement(String sql);

// 数据库SQL语句搬运工对象接口
interface java.sql.Statement 

/*
 * 执行数据库修改数据,insert,update,delete...,返回值类型是int类型,是当前SQL语句搬运到数据库执行之后,数据库运行对于当前操作受到影响的行数
 * 2 rows affected in 5 ms
 */
int executeUpdate(String sql);

/*
 * 执行数据库查询语句,select操作,执行的结果是一个java.sql.ResultSet,结果集对象,当前操作返回值never null
 */
java.sql.ResultSet executeQuery(String sql);

// 数据库SQL语句【预处理】搬运工对象接口
public interface java.sql.PreparedStatement extends java.sql.Statement
    
/*
 * 执行数据库修改操作,insert,update,delete...处理的SQL语句是在创建PreparedStatement对象过程预处理的SQL语句,并且返回值是int类型,为当前操作对于数据表中收到影响的行数
 */
int executeUpdate();

/*
 * 执行数据库查询语句,select操作,的SQL语句是在创建PreparedStatement对象过程预处理的SQL语句,执行的结果是一个java.sql.ResultSet,结果集对象,当前操作返回值never null
 */
java.sql.ResultSet executeQuery();

/*
 * PreparedStatement预处理的SQL语句是可以带有参数的,这里是对于SQL语句参数进行赋值操作,这里有指定的操作下标,和对应的数据,数据类型繁多
 */
setXXX(int parameterIndex, XXX value)
    
// 数据库结果集接口
interface java.sql.ResultSet 
    
/*
 * 根据查询结果中,字段所处的位置下标获取对应数据,XXX是指定类型(int、String用的最多)
 */
XXX getXXX(int columnIndex);

/*
 * 根据查询结果中,字段所处的字段名获取对应数据,XXX是指定类型(int、String用的最多)
 * 例int getInt(String columnLabel)
 */
XXX getXXX(String columnLabel);

/*
 * 判断当前查询结果集中是否还有数据可以继续遍历,如果没有。或则当前结果集中是无数据情况 Empty Set,直接返回fasle
 */
boolean next();

Statement 操作 SQL 语句【鸡肋】

增删改操作步骤【重点】
1、加载驱动
2、准备连接数据库所需要的参数
3、获取数据库连接
4、获取Statement搬运工对象
5、准备SQL语句
6、执行SQL语句获取受影响的行数
7、关闭资源
Statement 插入 SQL 数据操作
public class Demo2 {
    public static void main(String[] args) {
        Connection connection = null;
        Statement statement = null;

        try {
            // 加载驱动
            Class.forName("com.mysql.jdbc.Driver");

            // 声明连接数据库所需要的参数,包括但不限于IP地址、端口号、连接到哪个数据库、用户名、密码
            String url = "jdbc:mysql://localhost:3306/FC2020?useSSL=true&characterEncoding=utf8";
            String user = "root";
            String password = "root";

            // 通过参数获取数据库连接
            connection = DriverManager.getConnection(url, user, password);

            // 获取Statement对象
            statement = connection.createStatement();

            // 准备SQL语句
            String sql = "insert into student() values(1, '张三', 16, '男', '真帅');";

            // 执行SQL语句获取受影响的行数
            int affectedRows = statement.executeUpdate(sql);

            System.out.println("受影响的行数:" + affectedRows);

        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            try {
                if (statement != null) {
                    statement.close();
                }

                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
Statement 修改 SQL 数据操作
public class Demo3 {
    public static void main(String[] args) {
        Connection connection = null;
        Statement statement = null;

        try {
            // 加载驱动
            Class.forName("com.mysql.jdbc.Driver");

            // 声明连接数据库所需要的参数,包括但不限于IP地址、端口号、连接到哪个数据库、用户名、密码
            String url = "jdbc:mysql://localhost:3306/FC2020?useSSL=true&characterEncoding=utf8";
            String user = "root";
            String password = "root";

            // 通过参数获取数据库连接
            connection = DriverManager.getConnection(url, user, password);

            // 获取Statement对象
            statement = connection.createStatement();

            // 准备SQL语句
            String sql = "update student set name = '彭于晏' where id = 1";

            // 执行SQL语句获取受影响的行数
            int affectedRows = statement.executeUpdate(sql);

            System.out.println("受影响的行数:" + affectedRows);

        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            try {
                if (statement != null) {
                    statement.close();
                }

                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
Statement 删除 SQL 数据操作
public class Demo4 {
    public static void main(String[] args) {
        Connection connection = null;
        Statement statement = null;

        try {
            // 加载驱动
            Class.forName("com.mysql.jdbc.Driver");

            // 声明连接数据库所需要的参数,包括但不限于IP地址、端口号、连接到哪个数据库、用户名、密码
            String url = "jdbc:mysql://localhost:3306/FC2020?useSSL=true&characterEncoding=utf8";
            String user = "root";
            String password = "root";

            // 通过参数获取数据库连接
            connection = DriverManager.getConnection(url, user, password);

            // 获取Statement对象
            statement = connection.createStatement();

            // 准备SQL语句
            String sql = "delete from student where id = 1;";

            // 执行SQL语句获取受影响的行数
            int affectedRows = statement.executeUpdate(sql);

            System.out.println("受影响的行数:" + affectedRows);
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            try {
                if (statement != null) {
                    statement.close();
                }

                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
查询操作步骤【重点】
1、加载驱动
2、准备连接数据库所需要的参数
3、获取数据库连接
4、获取Statement搬运工对象
5、准备SQL语句
6、执行SQL语句获取结果集对象
7、判断结果集对象中是否有数据
8、如果结果集对象中存在数据,获取每个数据库字段对应类型的数据
9、关闭资源
Statement 查询 SQL 数据操作

准备实体类

public class Student {
    private Integer id;
    private String name;
    private Integer age;
    private String gender;
    private String info;

    // Constructor、Getter and Setter、toString
}

【注意】根据阿里巴巴开发手册,实体类成员变量要用包装类!!!

案例代码一:查询单行

public class Demo5 {
    public static void main(String[] args) {

        // 声明资源
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;

        try {
            // 加载驱动
            Class.forName("com.mysql.jdbc.Driver");

            // 准备参数
            String url = "jdbc:mysql://localhost:3306/FC2020?useSSL=true&characterEncoding=UTF8";
            String user = "root";
            String password = "root";

            // 获取连接
            connection = DriverManager.getConnection(url, user, password);

            // 获取搬运工对象
            statement = connection.createStatement();

            // 准备SQL语句
            String sql = "select * from student where id = 1";

            // 执行SQL语句并获取结果集
            resultSet = statement.executeQuery(sql);

            Student student = null;

            // 判断结果集中是否还有下一行数据
            while (resultSet.next()) {
                // 从结果集中获取对应类型的数据
                int id = resultSet.getInt(1);
                String name = resultSet.getString(2);
                int age = resultSet.getInt(3);
                String gender = resultSet.getString(4);
                String info = resultSet.getString(5);

                // 通过获取到的数据创建实体类对象
                student = new Student(id, name, age, gender, info);
            }

            // 展示
            System.out.println(student);

        } catch (SQLException | ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            try {
                if (resultSet != null) {
                    resultSet.close();
                }

                if (statement != null) {
                    statement.close();
                }
                
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

案例代码二:查询多行

public class Demo6 {
    public static void main(String[] args) {

        // 声明资源
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;

        try {
            // 加载驱动
            Class.forName("com.mysql.jdbc.Driver");

            // 准备参数
            String url = "jdbc:mysql://localhost:3306/FC2020?useSSL=true&characterEncoding=UTF8";
            String user = "root";
            String password = "root";

            // 获取连接
            connection = DriverManager.getConnection(url, user, password);

            // 获取搬运工对象
            statement = connection.createStatement();

            // 准备SQL语句
            String sql = "select * from student";

            // 执行SQL语句并获取结果集
            resultSet = statement.executeQuery(sql);

            // 声明一个集合用来存储多条数据对应的实体类对象
            ArrayList<Student> arrayList = new ArrayList<>();

            // 判断结果集中是否还有下一行数据
            while (resultSet.next()) {
                // 从结果集中获取对应类型的数据
                int id = resultSet.getInt(1);
                String name = resultSet.getString(2);
                int age = resultSet.getInt(3);
                String gender = resultSet.getString(4);
                String info = resultSet.getString(5);

                // 通过获取到的数据创建实体类对象
                Student student = new Student(id, name, age, gender, info);

                // 将实体类对象添加到集合中
                arrayList.add(student);
            }

            // 展示
            System.out.println(arrayList);

        } catch (SQLException | ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            try {
                if (resultSet != null) {
                    resultSet.close();
                }

                if (statement != null) {
                    statement.close();
                }
                
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

封装工具类简化操作

核心API

// 获取一个属性对象
Properties();

// 通过字符输入流加载数据到属性对象中
void load(Reader reader);

// 获取属性文件中的键的对应值
String getProperty(String key)

db.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/FC2020?useSSL=true&characterEncoding=UTF8
user=root
password=root

JdbcUtils

import java.io.FileReader;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;

/**
 * JDBC工具类
 */
public class JdbcUtils {
    private static String url = null;
    private static String user = null;
    private static String password = null;
    private static Connection connection = null;
    private static Statement statement = null;
    private static ResultSet resultSet = null;

    // 使用静态代码块进行初始化操作:加载驱动
    static {
        try {
            // 创建Properties对象用于从Properties文件中读取数据
            Properties properties = new Properties();

            // 通过字符输入流加载数据到properties中
            properties.load(new FileReader("./src/db.properties"));

            // 获取对应的数据
            String driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            user = properties.getProperty("user");
            password = properties.getProperty("password");

            // 加载驱动
            Class.forName(driver);
        } catch (ClassNotFoundException | IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取连接
     *
     * @return 返回数据库的连接
     */
    public static Connection getConnection() {
        try {
            connection = DriverManager.getConnection(url, user, password);
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return connection;
    }

    /**
     * 获取搬运工对象
     *
     * @return 返回一个搬运工对象
     */
    private static Statement getStatement() {
        if (connection == null) {
            connection = getConnection();
        }

        try {
            statement = connection.createStatement();
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return statement;
    }

    /**
     * 增删改方法
     *
     * @param sql 传入一个SQL语句
     * @return 返回受影响的行数
     */
    public static int executeUpdate(String sql) {
        if (statement == null) {
            statement = getStatement();
        }

        int affectedRows = 0;

        try {
            affectedRows = statement.executeUpdate(sql);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            close(statement, connection);
        }

        return affectedRows;
    }

    /**
     * 查询方法
     *
     * @param sql 传入一个SQL语句
     * @return 返回一个受影响的行数
     */
    public static ResultSet executeQuery(String sql) {
        try {
            resultSet = getStatement().executeQuery(sql);
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return resultSet;
    }

    /*
     * 重载关闭资源方法,以满足多种场景
     */
    public static void close(Connection connection) {
        close(resultSet, statement, connection);
    }

    public static void close(ResultSet resultSet) {
        close(resultSet, statement, connection);
    }

    public static void close(Statement statement, Connection connection) {
        close(resultSet, statement, connection);
    }

    public static void close(ResultSet resultSet, Statement statement, Connection connection) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }

            if (statement != null) {
                statement.close();
            }

            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

使用单元测试简化操作

【注意】需要导入一个单元测试所需要的 jar 包

junit-4.10.jar

使用单元测试不需要再声明 main 方法,只需要在方法声明上加上 @Test 注解即可,各个测试方法之间相互独立不受影响

案例代码

public class Demo1 {
    /**
     * 测试修改数据
     */
    @Test
    public void testUpdate() {
        String sql = "update student set age = 20 where id = 1;";

        int affectedRows = JdbcUtils.executeUpdate(sql);

        System.out.println("受影响的行数为:" + affectedRows);
    }

    /**
     * 测试删除数据
     */
    @Test
    public void testDelete() {
        String sql = "delete from student where id = 4;";

        int affectedRows = JdbcUtils.executeUpdate(sql);

        System.out.println("受影响的行数为:" + affectedRows);
    }

    /**
     * 测试插入数据
     */
    @Test
    public void testInsert() {
        String sql = "insert into student(name, age, gender, info) values('张三', 18, '男', '多财多亿');";

        int affectedRows = JdbcUtils.executeUpdate(sql);

        System.out.println("受影响的行数为:" + affectedRows);
    }

    /**
     * 测试查询单条数据
     */
    @Test
    public void testQueryOne() {
        String sql = "select * from student where id = 1";

        ResultSet resultSet = JdbcUtils.executeQuery(sql);

        try {
            while (resultSet.next()) {
                int id = resultSet.getInt("id");
                String name = resultSet.getString("name");
                int age = resultSet.getInt("age");
                String gender = resultSet.getString("gender");
                String info = resultSet.getString("info");

                Student student = new Student(id, name, age, gender.charAt(0), info);

                System.out.println(student);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.close(resultSet);
        }
    }

    /**
     * 测试查询多条数据
     */
    @Test
    public void testQueryAll() {
        ArrayList<Student> list = new ArrayList<>();

        String sql = "select * from student";

        ResultSet resultSet = JdbcUtils.executeQuery(sql);

        try {
            while (resultSet.next()) {
                int id = resultSet.getInt("id");
                String name = resultSet.getString("name");
                int age = resultSet.getInt("age");
                String gender = resultSet.getString("gender");
                String info = resultSet.getString("info");

                Student student = new Student(id, name, age, gender.charAt(0), info);

                list.add(student);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.close(resultSet);
        }

        System.out.println(list);
    }
}

SQL注入【重点】

SQL 注入是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的 SQL 语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息,甚至篡改数据库

案例代码

public class Demo1 {

    // 使用正确的用户名和密码登录成功
    @Test
    public void testLogin() {
        String sql = "select * from account where username = '张三' and password = '123456';";

        ResultSet resultSet = JdbcUtils.executeQuery(sql);

        try {
            if (resultSet.next()) {
                System.out.println("登录成功");
            } else {
                System.out.println("登录失败");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.close(resultSet);
        }
    }

    // 通过SQL注入使用异常的密码登录成功
    @Test
    public void testSqlInject() {
        String sql = "select * from account where username = '张三' and (password = 'iglrne' or 1 = 1);";

        ResultSet resultSet = JdbcUtils.executeQuery(sql);

        try {
            if (resultSet.next()) {
                System.out.println("登录成功");
            } else {
                System.out.println("登录失败");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.close(resultSet);
        }
    }
}

【注意】Statement 存在 SQL 注入问题,而 PreparedStatement 可以有效的避免 SQL 注入!

以后只能使用 PreparedStatement ,因为操作性更强,并且安全性更高

通过 PreparedStatement 操作 SQL 语句

PreparedStatement 实例包含已编译的 SQL 语句。这就是使语句“准备好”。包含于 PreparedStatement 对象中的 SQL 语句可具有一个或多个 IN 参数。IN参数的值在 SQL 语句创建时未被指定。相反的,该语句为每个 IN 参数保留一个问号(“?”)作为占位符。每个问号的值必须在该语句执行之前,通过适当的setXXX 方法来提供。

由于 PreparedStatement 对象已预编译过,所以其执行速度要快于 Statement 对象。因此,多次执行的 SQL 语句经常创建为 PreparedStatement 对象,以提高效率。

作为 Statement 的子类,PreparedStatement 继承了 Statement 的所有功能。另外它还添加了一整套方法,用于设置发送给数据库以取代 IN 参数占位符的值。同时,三种方法 execute、 executeQuery 和 executeUpdate 已被更改以使之不再需要参数。这些方法的 Statement 形式(接受 SQL 语句参数的形式)不应该用于 PreparedStatement 对象。

【注意】应该始终以PreparedStatement代替Statement,也就是说,在任何时候都不要使用Statement。

PreparedStatement 查询操作
public class Demo2 {
    @Test
    public void testPreparedStatement() {
        // 获取数据库连接
        Connection connection = JdbcUtils.getConnection();

        // 提取资源
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        // 准备SQL语句,? 是占位符
        String sql = "select * from account where username = ? and password = ?;";

        try {
            // 获取预处理搬运工对象,并对SQL语句进行预处理
            preparedStatement = connection.prepareStatement(sql);

            // 设置参数
            preparedStatement.setObject(1, "张三");
            preparedStatement.setObject(2, "123456");

            // 执行SQL语句
            resultSet = preparedStatement.executeQuery();

            // 判断是否还有数据
            if (resultSet.next()) {
                System.out.println("登录成功");
            } else {
                System.out.println("登录失败");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            JdbcUtils.close(resultSet, preparedStatement, connection);
        }
    }
}
PreparedStatement 增加操作
public class Demo3 {
    @Test
    public void testInsert() {
        // 准备SQL语句
        String sql = "insert into student(name, age, gender, info) values(?, ?, ?, ?)";

        // 获取连接
        Connection connection = JdbcUtils.getConnection();

        PreparedStatement preparedStatement = null;

        try {
            // 获取预处理对象
            preparedStatement = connection.prepareStatement(sql);

            // 设置参数
            preparedStatement.setObject(1, "赵四");
            preparedStatement.setObject(2, 17);
            preparedStatement.setObject(3, "男");
            preparedStatement.setObject(4, "你愁啥");

            // 执行SQL语句
            int affectedRows = preparedStatement.executeUpdate();

            System.out.println("受影响的行数:" + affectedRows);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            JdbcUtils.close(preparedStatement, connection);
        }
    }
}
PreparedStatement 修改操作
public class Demo3 {
    @Test
    public void testUpdate() {
        // 准备SQL语句
        String sql = "update student set age = ? where id = ?";

        // 获取连接
        Connection connection = JdbcUtils.getConnection();

        PreparedStatement preparedStatement = null;

        try {
            // 获取预处理对象
            preparedStatement = connection.prepareStatement(sql);

            // 设置参数
            preparedStatement.setObject(1, 61);
            preparedStatement.setObject(2, 6);

            // 执行SQL语句
            int affectedRows = preparedStatement.executeUpdate();

            System.out.println("受影响的行数:" + affectedRows);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            JdbcUtils.close(preparedStatement, connection);
        }
    }
}
PreparedStatement 删除操作
class Demo3{
    @Test
    public void testDelete() {
        // 准备SQL语句
        String sql = "delete from student where id = ?";

        // 获取连接
        Connection connection = JdbcUtils.getConnection();

        PreparedStatement preparedStatement = null;

        try {
            // 获取预处理对象
            preparedStatement = connection.prepareStatement(sql);

            // 设置参数
            preparedStatement.setObject(1, 3);

            // 执行SQL语句
            int affectedRows = preparedStatement.executeUpdate();

            System.out.println("受影响的行数:" + affectedRows);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            JdbcUtils.close(preparedStatement, connection);
        }
    }
}

JDBC

轻量级 ORM 框架 DbUtils【重点】

概述

Commons DbUtils 是 Apache 组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能。

jar包

commons-dbutils-1.7.jar

下载路径

http://commons.apache.org/proper/commons-dbutils/download_dbutils.cgi
核心 API
// DbUtils 核心类,提供简易的对数据库进行操作的方法
class QueryRunner {}

// update 核心方法对应增删改操作 insert,update,delete
// conn   -> 数据库连接
// sql    -> sql语句
// params -> 参数列表,这里使用可变长参数,底层是Object类型的数组
int update(Connection conn, String sql, Object... params);
    
// query 核心方法,对应查询操作
// T 泛型
// conn   -> 数据库连接
// sql    -> sql语句
// rsh    -> 结果集处理器接口,需要传入一个实现类,或者使用匿名内部类的形式并重写handler方法,推荐使用其实现类 BeanHandler 以及 BeanListHandler
// params -> 参数列表,这里使用可变长参数,底层是Object类型的数组
<T> T query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params)

// 结果集映射器接口,核心方法为 handler()
interface ResultSetHandler<T> {}

// 处理符合JavaBean规范的类对象,传入参数是对应JavaBean规范 Class对象,返回一个对应的实体类对象
class BeanHandler<T> implements ResultSetHandler<T> {}
    
// 处理符合JavaBean规范的实体类,并且返回值是一个List集合包含制定的JavaBean实体类    
class BeanListHandler<T> implements ResultSetHandler<List<T>> {}
使用 DbUtils 插入数据
public class Demo1 {
    @Test
    public void testInsert() {
        // 获取 QueryRunner对象
        QueryRunner queryRunner = new QueryRunner();

        // 获取连接
        Connection connection = JdbcUtils.getConnection();

        // 准备SQL语句
        String sql = "insert into student(name, age, gender, info) values(?, ?, ?, ?)";

        // 准备参数
        Object[] parameters = {"刘能", 37, "男", "人生没有如果,只有后果和结果"};

        try {
            // 执行SQL语句并获取受影响的行数
            int affectedRows = queryRunner.update(connection, sql, parameters);

            System.out.println("受影响的行数:" + affectedRows);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            JdbcUtils.close(connection);
        }
    }
}
使用 DbUtils 修改数据
public class Demo1 {   
    @Test
    public void testUpdate() {
        // 获取 QueryRunner对象
        QueryRunner queryRunner = new QueryRunner();

        // 获取连接
        Connection connection = JdbcUtils.getConnection();

        // 准备SQL语句
        String sql = "update student set age = ? where id = ?";

        // 准备参数
        Object[] parameters = {48, 7};

        try {
            // 执行SQL语句并获取受影响的行数
            int affectedRows = queryRunner.update(connection, sql, parameters);

            System.out.println("受影响的行数:" + affectedRows);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            JdbcUtils.close(connection);
        }
    }
}
使用 DbUtils 删除数据
public class Demo1 {
    @Test
    public void testDelete() {
        // 获取 QueryRunner对象
        QueryRunner queryRunner = new QueryRunner();

        // 获取连接
        Connection connection = JdbcUtils.getConnection();

        // 准备SQL语句
        String sql = "delete from student where id = ?";

        // 准备参数
        Object[] parameters = {7};

        try {
            // 执行SQL语句并获取受影响的行数
            int affectedRows = queryRunner.update(connection, sql, parameters);

            System.out.println("受影响的行数:" + affectedRows);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            JdbcUtils.close(connection);
        }
    }
}
使用 DbUtils 的 ResultSetHandler 进行查询单条数据【鸡肋】
public class Demo2 {
    @Test
    public void testQueryOne() {
        // 获取QueryRunner对象
        QueryRunner queryRunner = new QueryRunner();

        // 获取连接
        Connection connection = JdbcUtils.getConnection();

        // 准备SQL语句
        String sql = "select * from student where id = ?";

        // 准备参数
        Object[] params = {1};
        try {
            // 通过匿名内部类实现ResultSetHandler,并重写handler方法,执行SQL语句
            Student student = queryRunner.query(connection, sql, new ResultSetHandler<Student>() {
                // 重写handler方法
                @Override
                public Student handle(ResultSet resultSet) throws SQLException {

                    Student student1 = null;

                    while (resultSet.next()) {
                        int id = resultSet.getInt("id");
                        String name = resultSet.getString("name");
                        int age = resultSet.getInt("age");
                        String gender = resultSet.getString("gender");
                        String info = resultSet.getString("info");

                        student1 = new Student(id, name, age, gender, info);
                    }

                    return student1;
                }
            }, params);

            System.out.println(student);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            JdbcUtils.close(connection);
        }
    }
}
使用DbUtils 的 BeanHandler 进行查询单条数据
public class Demo3 {
    @Test
    public void testQuery() {
        // 获取QueryRunner对象
        QueryRunner queryRunner = new QueryRunner();

        // 准备SQL语句
        String sql = "select * from student where id = ?";

        // 准备参数
        Object[] params = {1};

        // 获取连接
        Connection connection = JdbcUtils.getConnection();

        try {
            // 执行SQL语句,传入BeanHandler对象
            Student student = queryRunner.query(connection, sql, new BeanHandler<Student>(Student.class), params);

            System.out.println(student);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            JdbcUtils.close(connection);
        }
    }
}
使用 DbUtils 的 BeanListHandler 进行查询多条数据
public class Demo4 {
    @Test
    public void testQueryAll() {
        // 获取QueryRunner对象
        QueryRunner queryRunner = new QueryRunner();

        // 准备SQL语句
        String sql = "select * from student";

        // 获取连接
        Connection connection = JdbcUtils.getConnection();

        try {
            // 执行SQL语句,传入BeanListHandler对象,获取一个包含实体类对象的集合
            List<Student> list = queryRunner.query(connection, sql, new BeanListHandler<Student>(Student.class));

            System.out.println(list);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            JdbcUtils.close(connection);
        }
    }
}
【补充】使用 DbUtils 的 ArrayHandler 获取包含对应一条数据的数组
// 处理符合JavaBean规范的类对象,能够将数据转为一个Object类型的数组
class ArrayHandler implements ResultSetHandler<Object[]> {}

案例代码

public class Demo5 {
    @Test
    public void testQueryOne() {
        // 获取QueryRunner对象
        QueryRunner queryRunner = new QueryRunner();

        // 准备SQL语句
        String sql = "select * from student where id = 1";

        // 获取连接
        Connection connection = JdbcUtils.getConnection();

        try {
            // 执行SQL语句,ArrayHandler,获取一个包含对应字段数据的数组
            Object[] objects = queryRunner.query(connection, sql, new ArrayHandler());

            // 将数组转为字符串并展示
            System.out.println(Arrays.toString(objects));
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            JdbcUtils.close(connection);
        }
    }
}
【补充】使用 DbUtils 的 ArrayListHandler 获取包含对应多条数据的数组
// 处理符合JavaBean规范的类对象,能够将数据转为一个包含Object类型的数组的集合
class ArrayListHandler extends AbstractListHandler<Object[]> {}

案例代码

public class Demo5 {
    @Test
    public void testQueryOne() {
        // 获取QueryRunner对象
        QueryRunner queryRunner = new QueryRunner();

        // 准备SQL语句
        String sql = "select * from student where id = 1";

        // 获取连接
        Connection connection = JdbcUtils.getConnection();

        try {
            // 执行SQL语句,ArrayHandler,获取一个包含对应字段数据的数组
            Object[] objects = queryRunner.query(connection, sql, new ArrayHandler());

            // 将数组转为字符串并展示
            System.out.println(Arrays.toString(objects));
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            JdbcUtils.close(connection);
        }
    }
}

数据库连接池

概述

数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。

常见的数据库连接池

C3P0

DBCP

Druid【常用】

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gyw3BZgu-1659699924406)(Pictures/常用数据库连接池对比.png)]

C3P0连接池的使用

导入jar包

mchange-commons-java-0.2.19.jar
c3p0-0.9.5.2.jar

配置文件

<!-- c3p0-config.xml需要存储于src目录下 -->
<c3p0-config>
    <!-- 使用默认的配置读取连接池对象 -->
    <default-config>
        <!--  连接参数 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/FC2020?useSSL=true&amp;characterEncoding=UTF8</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <!-- 连接池参数 -->
        <!--初始化申请的连接数量-->
        <property name="initialPoolSize">5</property>
        <!--最大的连接数量-->
        <property name="maxPoolSize">10</property>
        <!--超时时间-->
        <property name="checkoutTimeout">3000</property>
    </default-config>

    <named-config name="otherc3p0">
        <!--  连接参数 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/student?useSSL=true</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <!-- 连接池参数 -->
        <property name="initialPoolSize">5</property>
        <property name="maxPoolSize">8</property>
        <property name="checkoutTimeout">1000</property>
    </named-config>
</c3p0-config>

【注意】此配置文件需要放在src目录下,不需要手动编写,直接复制粘贴后修改数据库名和密码即可!!!

使用 C3P0 封装 JdbcUtilsOnC3P0工具类

public class JdbcUtilsOnC3P0 {
    // 声明一个c3p0连接池
    private static ComboPooledDataSource pool = new ComboPooledDataSource();
    private static Connection connection = null;
    private static Statement statement = null;
    private static ResultSet resultSet = null;

    /**
     * 获取连接
     *
     * @return 返回数据库的连接
     */
    public static Connection getConnection() {

        try {
            // 通过c3p0连接池获取连接
            connection = pool.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return connection;
    }

    /*
     * 重载关闭资源方法,以满足多种场景
     */
    public static void close(Connection connection) {
        close(resultSet, statement, connection);
    }

    public static void close(ResultSet resultSet) {
        close(resultSet, statement, connection);
    }

    public static void close(Statement statement, Connection connection) {
        close(resultSet, statement, connection);
    }

    public static void close(ResultSet resultSet, Statement statement, Connection connection) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }

            if (statement != null) {
                statement.close();
            }

            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

案例代码

public class Demo {
    @Test
    public void testQuery() {
        // 获取QueryRunner对象
        QueryRunner queryRunner = new QueryRunner();

        // 通过C3P0连接池获取连接
        Connection connection = JdbcUtilsOnC3P0.getConnection();

        // 准备SQL语句
        String sql = "select * from student";

        try {
            // 使用BeanListHandler执行SQL语句获取对应数据的集合
            List<Student> list = queryRunner.query(connection, sql, new BeanListHandler<Student>(Student.class));

            // 增强for循环遍历
            for (Student student : list) {
                System.out.println(student);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            JdbcUtilsOnC3P0.close(connection);
        }
    }
}
Druid 连接池的使用

导包

druid-1.0.9.jar

配置文件

# 文件名 druid.properties 存储在src目录下
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/FC2020?useSSL=true&characterEncoding=UTF-8
username=root
password=root

# 初始化数据库连接池容量
initialSize=5

# 最大容量
maxActive=20

# TimeOut 等待超时时间
maxWait=2000

【注意】此配置文件需要放在src目录下,不需要手动编写,直接复制粘贴后修改数据库名和密码即可!!!

使用 Druid 封装JdbcUtilsOnDruid 工具类

public class JdbcUtilsOnDruid {

    // 数据库源
    private static DataSource dataSource;
    private static Connection connection = null;
    private static Statement statement = null;
    private static ResultSet resultSet = null;

    static {
        try {
            // 加载Properties文件
            Properties properties = new Properties();
            properties.load(new FileReader("./src/druid.properties"));

            // 已经完成数据库连接池操作
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 返回数据库连接对象,连接失败返回null
     *
     * @return java.sql.Connection 数据库连接对象
     */
    public static Connection getConnection() {
        try {
            // 通过数据源获取连接
            connection = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }

    /*
     * 重载关闭资源方法,以满足多种场景
     */
    public static void close(Connection connection) {
        close(resultSet, statement, connection);
    }

    public static void close(ResultSet resultSet) {
        close(resultSet, statement, connection);
    }

    public static void close(Statement statement, Connection connection) {
        close(resultSet, statement, connection);
    }

    public static void close(ResultSet resultSet, Statement statement, Connection connection) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }

            if (statement != null) {
                statement.close();
            }

            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

案例代码

public class Demo {
    @Test
    public void testDruid() {
        // 获取QueryRunner对象
        QueryRunner queryRunner = new QueryRunner();

        // 准备SQL语句
        String sql = "select * from student";

        // 从Druid连接池中获取连接
        Connection connection = JdbcUtilsOnDruid.getConnection();

        try {
            // 使用BeanListHandler执行SQL语句并获取带有数据的集合
            List<Student> list = queryRunner.query(connection, sql, new BeanListHandler<Student>(Student.class));

            // 增强for循环遍历集合
            for (Student student : list) {
                System.out.println(student);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            JdbcUtilsOnDruid.close(connection);
        }
    }
}

【补充】常见数据库连接

-------------------------------oracle------------------
驱动:oracle.jdbc.driver.OracleDriver
URL:jdbc:oracle:thin:@machine_name:port:dbname
注:machine_name:数据库所在的机器的名称;
      port:端口号,默认是1521

-------------------------------mysql-------------------
驱动:com.mysql.jdbc.Driver
URL:jdbc:mysql://machine_name:port/dbname
注:machine_name:数据库所在的机器的名称;
      port:端口号,默认3306    

------------------------------SQL Server---------------
驱动:com.microsoft.jdbc.sqlserver.SQLServerDriver
URL:jdbc:microsoft:sqlserver://<machine_name><:port>;DatabaseName=<dbname>
注:machine_name:数据库所在的机器的名称;
      port:端口号,默认是1433

------------------------------DB2----------------------
驱动:com.ibm.db2.jdbc.app.DB2Driver
URL:jdbc:db2://<machine_name><:port>/dbname
注:machine_name:数据库所在的机器的名称;
      port:端口号,默认是5000
-------------------------------------------------------
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值