java基础-JDBC

JDBC

JDBC概述

  1. JDBC为访问不同的数据库提供了统一的接口,为使用者屏蔽了细节问题·
  2. Java程序员使用JDBC,可以连接任何提供了JDBC驱动程序的数据库系统,从而完成对数据库的各种操作。

image-20231215211346660

image-20231215213322506

  • 说明:JDBC是Java提供一套用于数据库操作的接口API,Java程序员只需要面向这套接口编程即可。不同的数据库厂商,需要针对这套接口,提供不同实现。

  • JDBC API

    JDBCAPI是一系列的接口,它统一和规范了应用程序与数据库的连接、执行SQL语句,并到得到返回结果等各类操作,相关类和接口在java.sql与javax.sgl包中

    image-20231215213432225

JDBC快速入门

JDBC程序编写步骤

  1. 注册驱动 ——加载Driver类
  2. 获取连接——得到Connection
  3. 执行增删改查——发送SQL给MySQL执行
  4. 释放资源——关闭相关连接

JDBC第一个程序

public class Jdbc01 {
    public static void main(String[] args) throws SQLException {
        // 前置工作,在项目下创建文件夹libs
        // 将mysql.jar复制到libs下,点击 add to library 加入到项目
        // 1. 注册驱动
        Driver driver = new Driver();  //  创建driver对象
        // 2. 得到连接
        // (1) jdbc:mysql:// 规定好的表示协议,通过jdbc的当时连接mysql
        // (2) localhost:主机,可以是ip地址
        // (3) 3306:端口号,表示mysql监听的端口
        // (4) db02:数据库名,来年街道mysql dbms的哪个数据库
        // (5) mysql的连接本质就是前面学过的socket连接
        String url = "jdbc:mysql://localhost:3306/db02";
        // 将用户名和密码放入到Properties对象
        Properties properties = new Properties();
        // 说明:user和password是固定的,不能修改,后面的值根据实际情况写
        properties.setProperty("user", "root");  // 用户
        properties.setProperty("password", "root");  // 密码

        Connection connect = driver.connect(url, properties);
        // 3. 执行sql
        String sql = "insert into actor values(null, '刘德华', '男', '1970-01-01', '1500543')";
        // statement用于执行静态SQL语句并返回其生成的结果对象
        Statement statement = connect.createStatement();
        int rows = statement.executeUpdate(sql);  // 如果是dml语句,返回的就是影响的行数
        System.out.println(rows > 0 ? "成功" : "失败");

        // 4. 关闭连接
        statement.close();
        connect.close();
    }
}

连接JDBC的方式

  1. 获取Driver实现类对象

    //方式1
    @Test
    public void connect01() throws SQLException {
        Driver driver = new Driver(); //创建driver对象
        String url = "jdbc:mysql://localhost:3306/db02";
        //将 用户名和密码放入到Properties 对象
        Properties properties = new Properties();
        //说明 user 和 password 是规定好,后面的值根据实际情况写
        properties.setProperty("user", "root");// 用户
        properties.setProperty("password", "root"); //密码
        Connection connect = driver.connect(url, properties);
        System.out.println(connect);
    }
    

    属于静态加载,依赖性比较强,灵活性不够高。

  2. 使用反射机制,动态加载

    // 方式2
    @Test
    public void connect02() throws Exception {
        // 使用反射加载 Driver 类, 动态加载,更加的灵活,减少依赖性
        Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
        Driver driver = (Driver) aClass.getConstructor().newInstance();
    
        String url = "jdbc:mysql://localhost:3306/db02";
        //将 用户名和密码放入到Properties 对象
        Properties properties = new Properties();
        //说明 user 和 password 是规定好,后面的值根据实际情况写
        properties.setProperty("user", "root");// 用户
        properties.setProperty("password", "root"); //密码
        Connection connect = driver.connect(url, properties);
        System.out.println("方式二:" + connect);
    }
    
  3. 使用 DriverManager 替换Driver,进行统一管理,具有更好的扩展性

    // 方式3
    @Test
    public void connect03() throws Exception {
        // 使用反射加载 Driver
        Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
        Driver driver = (Driver) aClass.getConstructor().newInstance();
    
        // 创建 url 和 user 和 password
        String url = "jdbc:mysql://localhost:3306/db02";
        String  user = "root";
        String password = "root";
    
        DriverManager.registerDriver(driver);  // 注册Driver驱动
    
        Connection connect = DriverManager.getConnection(url, user, password);
        System.out.println("方式三:" + connect);
    }
    
  4. 使用Class.forName自动完成注册驱动,简化代码 【这种方式使用最多】

    // 方式4
    @Test
    public void connect04() throws Exception {
    
        // 使用反射加载 Driver
        // 在加载 Driver 类是,完成注册
        /*
            源码:
            1. 静态代码块,在类加载的时候会执行一次
            2. DriverManager.registerDriver(new Driver());
            3. 因此注册driver的工作已经完成
            static {
                try {
                    DriverManager.registerDriver(new Driver());
                } catch (SQLException var1) {
                    throw new RuntimeException("Can't register driver!");
                }
            }
         */
        Class.forName("com.mysql.jdbc.Driver");
    
    
        // 创建 url 和 user 和 password
        String url = "jdbc:mysql://localhost:3306/db02";
        String  user = "root";
        String password = "root";
    
        Connection connect = DriverManager.getConnection(url, user, password);
        System.out.println("方式四:" + connect);
    }
    

    注意:

    1. mysqL驱动5.1.6可以无需CLass.forName(“com.mysql.jdbc.Driver”);
    2. 从jdk1.5以后使用了jdbc4,不再需要显示调用class.forName()注册驱动而是自动调用驱动jar包下META-INF\services\java.sql.Driver文本中的类名称去注册
    3. 建议还是写上CLass.forName(“com.mysql.jdbc.Driver”);更加明确
  5. 使用配置文件,连接数据库更灵活(在方式4的基础上进行改进)

    // 方式5
    @Test
    public void connect05() throws Exception {
        // 通过 properties 对象获取配置文件的信息
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\mysql.properties"));
        // 获取相关的值
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driver = properties.getProperty("driver");
    
        Class.forName(driver);
    
        Connection connect = DriverManager.getConnection(url, user, password);
        System.out.println("方式五:" + connect);
    }
    

    配置文件mysql.properties:

    user=root
    password=root
    url=jdbc:mysql://localhost:3306/db02
    driver=com.mysql.jdbc.Driver
    

ReultSet

  • 基本介绍

    1. 表示数据库结果集的数据表,通常通过执行查询数据库的语句生成
    2. ResultSet对象保持一个光标指向其当前的数据行。最初,光标位于第一行之前
    3. next方法将光标移动到下一行,并且由于在ResultSet对象中没有更多行时返回false,因此可以在while循环中使用循环来遍历结果集
  • 代码示例

    public class ResultSet_ {
        public static void main(String[] args) throws Exception {
            // 通过 properties 对象获取配置文件的信息
            Properties properties = new Properties();
            properties.load(new FileInputStream("src\\mysql.properties"));
            // 获取相关的值
            String user = properties.getProperty("user");
            String password = properties.getProperty("password");
            String url = properties.getProperty("url");
            String driver = properties.getProperty("driver");
    
            // 1. 注册驱动
            Class.forName(driver);
            // 2. 得到连接
            Connection connect = DriverManager.getConnection(url, user, password);
            // 3. 得到 Statement
            Statement statement = connect.createStatement();
            // 组织 sql
            String sql = "select id, name, sex, borndate from actor";
            // 执行给定的SQL语句,该语句返回单个 ResultSet 对象
            ResultSet resultSet = statement.executeQuery(sql);
            // 5. 使用while循环取出数据
            System.out.println("id\t" + "name\t" + "sex \t" + "borndate");
            while (resultSet.next()) {  // 让光标向后移动,如果没有更多行,则返回false
                int id = resultSet.getInt(1);  // 获取该行第1列
                String name = resultSet.getString(2);  // 获取该行第2列
                String sex = resultSet.getString(3);
                Date borndate = resultSet.getDate(4);
                System.out.println(id + "\t" + name + "\t" + sex + "\t" + borndate);
            }
            // 6. 关闭连接
            resultSet.close();
            statement.close();
        }
    }
    

    image-20231216122257666

Statement

Statement

  • 基本介绍

    1. Statement对象用于执行静态SQL语句并返回其生成的结果的对象
    2. 在连接建立后,需要对数据库进行访问,执行命名或是SQL语句,可以通过
    • Statement[存在SQL注入]
    • PreparedStatement[预处理]
    • CallableStatement[存储过程]
    1. Statement对象执行SQL语句,存在SQL注入风险
    2. SQL注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的SQL语句段或命令,恶意攻击数据库。
    3. 要防范SQL注入,只要用PreparedStatement(从Statement扩展而来)取代Statement就可以了
  • SQL 注入示例:

    -- 创建admin表,存储用户名和密码
    CREATE TABLE admin (
    	`name` VARCHAR(32) NOT NULL UNIQUE,
    	pwd VARCHAR(32) NOT NULL DEFAULT '');
    
    INSERT INTO admin VALUES('tom', '123');
    
    -- 正常查询,只有当`name` = 'tom', `pwd` = '123'都正确的时候才能查询到
    SELECT * FROM admin 
    	WHERE `name` = 'tom' AND `pwd` = '123';
    	
    -- 使用sql注入,按照如下方式输入也可以查询到
    -- 输入用户名:1' or
    -- 输入密码:or '1' = '1
    SELECT * FROM admin 
    	WHERE `name` = '1' OR' AND `pwd` = 'OR '1' = '1';
    
  • statement 注入:

    @SuppressWarnings("all")
    public class Statement_ {
        public static void main(String[] args) throws Exception {
    
            Scanner scanner = new Scanner(System.in);
            // 让用户输入管理员名和密码
            System.out.println("请输入管理员名:");
            String admin_name = scanner.nextLine();
            System.out.println("请输入管理员密码:");
            String admin_pwd = scanner.nextLine();
            // 通过 properties 对象获取配置文件的信息
            Properties properties = new Properties();
            properties.load(new FileInputStream("src\\mysql.properties"));
            // 获取相关的值
            String user = properties.getProperty("user");
            String password = properties.getProperty("password");
            String url = properties.getProperty("url");
            String driver = properties.getProperty("driver");
    
            // 1. 注册驱动
            Class.forName(driver);
            // 2. 得到连接
            Connection connect = DriverManager.getConnection(url, user, password);
            // 3. 得到 Statement
            Statement statement = connect.createStatement();
            // 组织 sql
            String sql = "select name, pwd from admin where name = '" + admin_name + "' and pwd = '" + admin_pwd + "'";
            ResultSet resultSet = statement.executeQuery(sql);
            if (resultSet.next()) {  // 如果查询到一条记录说明该管理员存在
                System.out.println("管理员存在");
            } else {
                System.out.println("管理员不存在");
            }
    
            // 关闭连接
            resultSet.close();
            statement.close();
            connect.close();
        }
    }
    

    image-20231217102903618

PreparedStatement

String sql = "select name, pwd from admin where name = ? and pwd = ?";
  • 基本介绍

    1. PreparedStatement 执行的SQL语句中的参数用问号(?)来表示,调用 PreparedStatement 对象的 setXxx() 方法来设置这些参数。
    2. setXxx() 方法有两个参数,第一个参数是要设置的SQL语句中的参数的索引(从1开始),第二个是设置的SQL语句中的参数的值
    3. 调用executeQuery(),返回ResultSet对象
    4. 调用executeUpdate():执行更新,包括增、删、修改
  • 预处理的好处

    1. 不再使用+拼接sgl语句,减少语法错误
    2. 有效的解决了sgl注入问题!
    3. 大大减少了编译次数,效率较高
  • select语句示例

    @SuppressWarnings("all")
    public class PreparedStatement_ {
        public static void main(String[] args) throws Exception {
            Scanner scanner = new Scanner(System.in);
            // 让用户输入管理员名和密码
            System.out.println("请输入管理员名:");
            String admin_name = scanner.nextLine();
            System.out.println("请输入管理员密码:");
            String admin_pwd = scanner.nextLine();
            // 通过 properties 对象获取配置文件的信息
            Properties properties = new Properties();
            properties.load(new FileInputStream("src\\mysql.properties"));
            // 获取相关的值
            String user = properties.getProperty("user");
            String password = properties.getProperty("password");
            String url = properties.getProperty("url");
            String driver = properties.getProperty("driver");
    
            // 1. 注册驱动
            Class.forName(driver);
            // 2. 得到连接
            Connection connect = DriverManager.getConnection(url, user, password);
            // 3. 得到 PreparedStatement
            // 3.1 组织 sql,sql语句的参数使用 ? 占位符
            // select 语句
    		String sql = "select name, pwd from admin where name = ? and pwd = ?";
            // 3.2 preparedStatement 对象实现了 PreparedStatement 接口的实现类的对象
            PreparedStatement preparedStatement = connect.prepareStatement(sql);
            // 3.3 给 ? 赋值
            preparedStatement.setString(1, admin_name);  // 第一个?
            preparedStatement.setString(2, admin_pwd);  // 第二个?
            // 4. 执行 select 语句使用 executeQuery
            //    如果执行的是 dml 语句,则使用 executeUpdate
            //    (sql)中的sql可以不用再写sql(),如果填写了sql,则会将上面的sql语句放入()中执行,name = ? and pwd = ?,会报错
            ResultSet resultSet = preparedStatement.executeQuery();
    
            if (resultSet.next()) {  // 如果查询到一条记录说明该管理员存在
                System.out.println("管理员存在");
            } else {
                System.out.println("管理员不存在");
            }
    
            // 关闭连接
            resultSet.close();
            preparedStatement.close();
            connect.close();
        }
    }
    
  • dml语句示例

    @SuppressWarnings("all")
    public class PreparedStatementDML {
        public static void main(String[] args) throws Exception {
            Scanner scanner = new Scanner(System.in);
            // 让用户输入管理员名和密码
            System.out.println("请输入管理员名:");
            String admin_name = scanner.nextLine();
    //        System.out.println("请输入管理员密码:");
    //        String admin_pwd = scanner.nextLine();
            // 通过 properties 对象获取配置文件的信息
            Properties properties = new Properties();
            properties.load(new FileInputStream("src\\mysql.properties"));
            // 获取相关的值
            String user = properties.getProperty("user");
            String password = properties.getProperty("password");
            String url = properties.getProperty("url");
            String driver = properties.getProperty("driver");
    
            // 1. 注册驱动
            Class.forName(driver);
            // 2. 得到连接
            Connection connect = DriverManager.getConnection(url, user, password);
            // 3. 得到 PreparedStatement
            // 3.1 组织 sql,sql语句的参数使用 ? 占位符
            // dml 语句
            // 添加记录
    //        String sql = "insert into admin(name, pwd) values(?, ?)";
            // 修改记录
    //        String sql = "update admin set pwd = ? where name = ?";
            // 删除记录
            String sql = "delete from admin where name = ?";
            // 3.2 preparedStatement 对象实现了 PreparedStatement 接口的实现类的对象
            PreparedStatement preparedStatement = connect.prepareStatement(sql);
            // 3.3 给 ? 赋值
            preparedStatement.setString(1, admin_name);  // 第一个?
    //        preparedStatement.setString(2, admin_pwd);  // 第二个?
            // 4. 执行 dml 语句使用 executeUpdate
            int rows = preparedStatement.executeUpdate();
            System.out.println(rows > 0 ? "执行成功" : "执行失败");
            // 关闭连接
            preparedStatement.close();
            connect.close();
        }
    }
    

JDBC API

JDBC API

JDBCUtils

说明:在jdbc操作中,获取连接和释放资源是经常使用到的(连接和关闭)可以将其封装JDBC连接的工具类JDBCUtils。

public class JDBCUtils {
    // 定义相关的属性(4个),因为只需要一份,设置为static
    private static String url;  // url
    private static String user;  // 用户名
    private static String password;  // 密码
    private static String driver;  // 驱动名

    // 在static代码块中初始化
    static {
        try {
            Properties properties = new Properties();
            properties.load(new FileInputStream("src\\mysql.properties"));
            // 读取相关的属性
            url = properties.getProperty("url");
            user = properties.getProperty("user");
            password = properties.getProperty("password");
            driver = properties.getProperty("driver");

        } catch (IOException e) {
            // 在实际开发中,这样处理
            // 1. 将编译异常转成运行异常
            // 2. 调用者可以选择捕获异常,也可以选择默认处理该异常,比较方便
            throw new RuntimeException(e);
        }
    }

    // 连接数据库,返回Connection
    public static Connection getConnection() {
        try {
            return DriverManager.getConnection(url,user, password);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    // 关闭相关资源
    /*
        1. ResultSet 结果集
        2. Statement 或者 PreparedStatement
        3. Connection
        4. 如果需要关闭资源,就传入对象,否则传入 null
     */
    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) {
            throw new RuntimeException(e);
        }
    }
}
  • JDBCUtils 测试案例——dml

    @Test
    public class JDBCUtils_Use {
        @Test
        public void testDML() {  // insert, update, delete
            // 1. 得到连接
            Connection connection = null;
            // 2. 组织一个sql
            String sql = "update actor set name = ? where id = ?";
            PreparedStatement preparedStatement = null;
            // 3, 创建 PreparedStatement 对象
            try {
                connection = JDBCUtils.getConnection();
                preparedStatement = connection.prepareStatement(sql);
                // 给占位符赋值
                preparedStatement.setString(1, "周星驰");
                preparedStatement.setInt(2, 3);
                // 执行sql
                int rows = preparedStatement.executeUpdate();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            } finally {
                // 关闭资源
                JDBCUtils.close(null, preparedStatement, connection);
            }
        }
    }
    
  • JDBCUtils 测试案例——select

    @Test
    public void testSelect() {
        // 1. 得到连接
        Connection connection = null;
        // 2. 组织一个sql
        String sql = "select * from actor";
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        // 3, 创建 PreparedStatement 对象
        try {
            connection = JDBCUtils.getConnection();
            preparedStatement = connection.prepareStatement(sql);
    
            // 执行sql,得到
            resultSet = preparedStatement.executeQuery();
            // 遍历结果集
            while (resultSet.next()) {
                System.out.println(
                        resultSet.getString("id") + "\t" +
                        resultSet.getString("name") + "\t" +
                        resultSet.getString("sex") + "\t" +
                        resultSet.getDate("borndate") + "\t" +
                        resultSet.getString("phone")
                );
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            // 关闭资源
            JDBCUtils.close(resultSet, preparedStatement, connection);
        }
    }
    

    image-20231217120027063

事务

  • 基本介绍

    1. JDBC程序中当一个Connection对象创建时,默认情况下是自动提交事务:每次执行一个SQL语句时,如果执行成功,就会向数据库自动提交,而不能回滚。
    2. JDBC程序中为了让多个SQL语句作为一个整体执行,需要使用事务
    3. 调用 Connection 的 setAutoCommit(false),可以取消自动提交事务
    4. 在所有的SQL语句都成功执行后,调用Connection 的 commit():方法提交事务
    5. 在其中某个操作失败或出现异常时,调用Connection 的 rollback():方法回滚事务
  • 不适用事务和使用事务的两个案例

    public class Transaction_ {
    
        // 没有使用事务
        @Test
        public void noTransaction() {
            // 1. 得到连接
            Connection connection = null;
            // 2. 组织一个sql
            String sql = "update account set balance = balance - 100 where id = 1";
            String sql2 = "update account set balance = balance + 100 where id = 2";
            PreparedStatement preparedStatement = null;
            // 3, 创建 PreparedStatement 对象
            try {
                connection = JDBCUtils.getConnection();  // 默认情况下,connection是默认自动提交的
                preparedStatement = connection.prepareStatement(sql);
                preparedStatement.executeUpdate();  // 执行第一条sql语句
    
                int i = 1 / 0;  // 抛出异常
    
                preparedStatement = connection.prepareStatement(sql2);
                preparedStatement.executeUpdate();  // 执行第二条sql语句
            } catch (SQLException e) {
                throw new RuntimeException(e);
            } finally {
                // 关闭资源
                JDBCUtils.close(null, preparedStatement, connection);
            }
        }
    
        // 事务解决
        @Test
        public void useTransaction() {
            // 1. 得到连接
            Connection connection = null;
            // 2. 组织一个sql
            String sql = "update account set balance = balance - 100 where id = 1";
            String sql2 = "update account set balance = balance + 100 where id = 2";
            PreparedStatement preparedStatement = null;
            // 3, 创建 PreparedStatement 对象
            try {
                connection = JDBCUtils.getConnection();  // 默认情况下,connection是默认自动提交的
                // 将 connection 设置为不自动提交
                connection.setAutoCommit(false);  // 表示开始了一个事务(事务开始点)
                preparedStatement = connection.prepareStatement(sql);
                preparedStatement.executeUpdate();  // 执行第一条sql语句
    
    //            int i = 1 / 0;  // 抛出异常
    
                preparedStatement = connection.prepareStatement(sql2);
                preparedStatement.executeUpdate();  // 执行第二条sql语句
    
                // 这里提交事务
                connection.commit();
            } catch (SQLException e) {
                // 这里可以进行回滚,即撤销执行的sql
                // 默认回滚到事务开始的状态
                System.out.println("事务回滚");
                try {
                    connection.rollback();
                } catch (SQLException ex) {
                    throw new RuntimeException(ex);
                }
                throw new RuntimeException(e);
            } finally {
                // 关闭资源
                JDBCUtils.close(null, preparedStatement, connection);
            }
        }
    }
    

批处理

  • 基本介绍

    1. 当需要成批插入或者更新记录时。可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率

    2. JDBC的批量处理语句包括下面方法:

      1. addBatch():添加需要批量处理的SQL语句或参数
      2. executeBatch():执行批量处理语句
      3. clearBatch():清空批处理包的语句
    3. JDBC连接MySQL时,如果要使用批处理功能,请在mysql.properties配置文件url中加参数:

      ?rewriteBatchedStatements=true ==> ``url=jdbc:mysql://localhost:3306/db02?rewriteBatchedStatements=true`

    4. 批处理往往和PreparedStatement一起搭配使用,可以既减少编译次数,又减少运行次数,效率大大提高

  • 不适用批处理和使用批处理使用案例

    public class Batch_ {
        // 传统方法,添加5000条数据到admin2
        @Test
        public void noBatch() throws SQLException {
            Connection connection = JDBCUtils.getConnection();
            String sql = "insert into admin2 values(null, ?, ?)";
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            System.out.println("开始执行");
            long start = System.currentTimeMillis();  // 开始时间
            for (int i = 0; i < 5000; i++) {
                preparedStatement.setString(1, "jack" + i);
                preparedStatement.setString(2, "666");
                preparedStatement.executeUpdate();
            }
            long end = System.currentTimeMillis();  // 结束时间
            System.out.println("传统方法耗时:" + (end - start));  // 传统方法耗时:136786
    
            JDBCUtils.close(null, preparedStatement, connection);
        }
    
        // 批量方法,添加5000条数据到admin2
        @Test
        public void useBatch() throws SQLException {
            Connection connection = JDBCUtils.getConnection();
            String sql = "insert into admin2 values(null, ?, ?)";
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            System.out.println("开始执行");
            long start = System.currentTimeMillis();  // 开始时间
            for (int i = 0; i < 5000; i++) {
                preparedStatement.setString(1, "jack" + i);
                preparedStatement.setString(2, "666");
    //            preparedStatement.executeUpdate();
                // 将sql语句加入到批处理包中
                preparedStatement.addBatch();
                // 当有1000条记录时,再批量执行
                if ((i + 1) % 1000 == 0) {
                    preparedStatement.executeBatch();
                    // 清空批处理队列
                    preparedStatement.clearBatch();
                }
            }
            long end = System.currentTimeMillis();  // 结束时间
            System.out.println("批量方法耗时:" + (end - start));  // 批量方法耗时:435
    
            JDBCUtils.close(null, preparedStatement, connection);
        }
    }
    
  • 批处理指令

    1. preparedStatement.addBatch();:将sql语句加入到批处理包中
    2. preparedStatement.executeBatch();:批量执行
    3. preparedStatement.clearBatch();:清空批处理队列
  • 批处理会减少发送sql语句的网络开销,而且减少编译次数,因此效率提高

数据库连接池

  • 传统获取Connection问题分析

    1. 传统的JDBC数据库连接使用DriverManager来获取,每次向数据库建立连接的时候都要将Connection加载到内存中,再验证IP地址,用户名和密码(0.05s~1s时间)。需要数据库连接的时候,就向数据库要求一个,频繁的进行数据库连接操作将占用很多的系统资源,容易造成服务器崩溃。
    2. 每一次数据库连接,使用完后都得断开,如果程序出现异常而未能关闭,将导致数据库内存泄漏,最终将导致重启数据库。
    3. 传统获取连接的方式,不能控制创建的连接数量,如连接过多,也可能导致内存泄漏,MySQL崩溃。
    4. 解决传统开发中的数据库连接问题,可以采用数据库连接池技术(connection pool)。
  • 数据库连接池基本介绍

    1. 预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。
    2. 2数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
    3. 当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。
  • 画图理解

    image-20231217164032525

  • 数据库连接池种类

    1. JDBC的数据库连接池使用javax.sql.DataSource来表示,DataSource只是一个接口,该接口通常由第三方提供实现
    2. C3P0数据库连接池,速度相对较慢,稳定性不错(hibernate,spring)
    3. DBCP数据库连接池,速度相对c3p0较快,但不稳定
    4. Proxool数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点
    5. BoneCP数据库连接池,速度快
    6. Druid(德鲁伊)是阿里提供的数据库连接池,集DBCP、C3PO、Proxool优点于一身的数据库连接池

C3P0数据库连接池

两种连接方式【优选方式2】

public class C3P0_ {

    // 方式1:相关参数在程序中指定user, url, password
    @Test
    public void testC3P0_01() throws Exception {
        // 1. 创建一个数据源对象
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
        // 2. 通知配置文件mysql.properties 获取相关连接信息
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\mysql.properties"));
        String url = properties.getProperty("url");
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String driver = properties.getProperty("driver");

        // 给数据源 comboPooledDataSource 设置相关的参数
        // 注意:连接管理是由 comboPooledDataSource 来管理
        comboPooledDataSource.setJdbcUrl(url);
        comboPooledDataSource.setUser(user);
        comboPooledDataSource.setPassword(password);
        comboPooledDataSource.setDriverClass(driver);

        // 设置初始连接数、最大连接数
        comboPooledDataSource.setInitialPoolSize(10);
        comboPooledDataSource.setMaxPoolSize(50);

        // 连接 测试对mysql 5000次操作
        long start = System.currentTimeMillis();
        for (int i = 0; i < 5000; i++) {
            Connection connection = comboPooledDataSource.getConnection();  // 这个方法就是从 DataSource 接口实现的
//            System.out.println("连接成功");
            connection.close();
        }
        long end = System.currentTimeMillis();
        System.out.println("c3p0 5000次连接数据库花费时间:" + (end - start) + "ms");  // c3p0 5000次连接数据库花费时间:383ms
    }

    // 方式2:使用配置文件模板来完成
    // 1. 将c3p0提供的c3p0-config.xml拷贝到src目录下
    // 2. 该文件制定了连接数据库和连接池的相关参数
    @Test
    public void testC3P0_02() throws Exception {
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("hsp_java");  // 填入c3p0-config.xml文件中配置的named-config
        // 连接 测试对mysql 5000次操作
        long start = System.currentTimeMillis();
        for (int i = 0; i < 5000; i++) {
            Connection connection = comboPooledDataSource.getConnection();  // 这个方法就是从 DataSource 接口实现的
//            System.out.println("连接成功");
            connection.close();
        }
        long end = System.currentTimeMillis();
        System.out.println("c3p0方式2配置文件 5000次连接数据库花费时间:" + (end - start) + "ms");  // c3p0方式2配置文件 5000次连接数据库花费时间:374ms
    }
}

c3p0-config.xml文件:

<c3p0-config>
    <!-- 数据源名称代表连接池 -->
    <named-config name="hsp_java">
        <!-- 驱动类 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <!-- url-->
        <property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/db02</property>
        <!-- 用户名 -->
        <property name="user">root</property>
        <!-- 密码 -->
        <property name="password">root</property>
        <!-- 每次增长的连接数-->
        <property name="acquireIncrement">5</property>
        <!-- 初始的连接数 -->
        <property name="initialPoolSize">10</property>
        <!-- 最小连接数 -->
        <property name="minPoolSize">5</property>
        <!-- 最大连接数 -->
        <property name="maxPoolSize">50</property>

        <!-- 可连接的最多的命令对象数 -->
        <property name="maxStatements">5</property>

        <!-- 每个连接对象可连接的最多的命令对象数 -->
        <property name="maxStatementsPerConnection">2</property>
    </named-config>
</c3p0-config>

Druid数据库连接池

Druid连接方式

public class Druid_ {
    @Test
    public void testDruid() throws Exception {
        // 1. 加入 Druid jar包
        // 2. 加入配置文件druid.properties,将文件加入到项目的src目录
        // 3. 创建 Properties对象,读取配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\druid.properties"));

        // 4. 创建一个指定参数的数据库连接池
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);

        // 连接 测试对mysql 500000次操作
        long start = System.currentTimeMillis();
        for (int i = 0; i < 500000; i++) {
            Connection connection = dataSource.getConnection();  // 这个方法就是从 DataSource 接口实现的
//            System.out.println("连接成功");
            connection.close();
        }
        long end = System.currentTimeMillis();
        System.out.println("druid 500000次连接数据库花费时间:" + (end - start) + "ms");  // druid 500000次连接数据库花费时间:660ms
    }
}

druid.properties配置文件

#key=value
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db02?rewriteBatchedStatements=true
username=root
password=root
#initial connection Size
initialSize=10
#min idle connecton size
minIdle=5
#max active connection size
maxActive=50
#max wait time (5000 mil seconds)
maxWait=5000
  • 使用druid工具类JDBCUtilsByDruid:

    public class JDBCUtilsByDruid {
        private static DataSource ds;
    
        // 静态代码块完成ds初始化
        static {
            Properties properties = new Properties();
            try {
                properties.load(new FileInputStream("src\\druid.properties"));
                ds = DruidDataSourceFactory.createDataSource(properties);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        // 编写getConnection方法
        public static Connection getConnection() throws SQLException {
            return ds.getConnection();
        }
    
        // 关闭连接,在数据库连接池技术中,close不是真正断掉连接,而是把使用的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) {
                throw new RuntimeException(e);
            }
        }
    }
    
  • 测试

    @Test
    public void testSelect() {
        System.out.println("使用druid方式完成");
        // 1. 得到连接
        Connection connection = null;
        // 2. 组织一个sql
        String sql = "select * from actor";
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        // 3, 创建 PreparedStatement 对象
        try {
            connection = JDBCUtilsByDruid.getConnection();
            System.out.println(connection.getClass());  //运行类型 class com.alibaba.druid.pool.DruidPooledConnection
            preparedStatement = connection.prepareStatement(sql);
    
            // 执行sql,得到
            resultSet = preparedStatement.executeQuery();
            // 遍历结果集
            while (resultSet.next()) {
                System.out.println(
                        resultSet.getString("id") + "\t" +
                                resultSet.getString("name") + "\t" +
                                resultSet.getString("sex") + "\t" +
                                resultSet.getDate("borndate") + "\t" +
                                resultSet.getString("phone")
                );
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            // 关闭资源
            JDBCUtilsByDruid.close(resultSet, preparedStatement, connection);
        }
    }
    

Apache——DBUtils

问题引出
  1. 关闭connection后,resultSet结果集无法使用
  2. resultSet不利于数据的管理,只能用一次
  3. 使用返回信息不方便(getString不明确),不知道返回的是name还是sex,若可以 getName或者getSex就好了

image-20231217174939736

使用土方法实现封装:

// 使用土方法解决 ResultSet封装到 ArrayList
@Test
public ArrayList<Actor> testSelectToArrayList() {
    System.out.println("使用druid方式完成");
    // 1. 得到连接
    Connection connection = null;
    // 2. 组织一个sql
    String sql = "select * from actor";
    PreparedStatement preparedStatement = null;
    ResultSet resultSet = null;
    ArrayList<Actor> list = new ArrayList<>();  // 创建ArrayList对象,存放actor对象
    // 3, 创建 PreparedStatement 对象
    try {
        connection = JDBCUtilsByDruid.getConnection();
        System.out.println(connection.getClass());  //运行类型 class com.alibaba.druid.pool.DruidPooledConnection
        preparedStatement = connection.prepareStatement(sql);

        // 执行sql,得到
        resultSet = preparedStatement.executeQuery();
        // 遍历结果集
        while (resultSet.next()) {
            int id = resultSet.getInt("id");
            String name = resultSet.getString("name");
            String sex = resultSet.getString("sex");
            Date borndate = resultSet.getDate("borndate");
            String phone = resultSet.getString("phone");
            // 把得到的 resultSet 记录,封装到Actor对象,放入list集合
            list.add(new Actor(id, name, sex, borndate, phone));
        }

        System.out.println("list集合数据:" + list);
        for (Actor actor : list) {
            System.out.println("演员名字:" + actor.getName());
        }
    } catch (SQLException e) {
        throw new RuntimeException(e);
    } finally {
        // 关闭资源
        JDBCUtilsByDruid.close(resultSet, preparedStatement, connection);
    }
    // 因为Arraylist和 connection 没有任何关联,所以该集合可以复用
    return list;
}

基本介绍

commons-dbutils是Apache组织提供的一个开源JDBC工具类库,它是对JDBC的封装使用 dbutils 能极大简化jdbc编码的工作量。

DbUtils类

  1. QueryRunner类:该类封装了SQL的执行,是线程安全的。可以实现增、删、改、查、批处理
  2. 使用QueryRunner类实现查询
  3. ResultSetHandler接口:该接口用于处理java.sql.ResultSet,将数据按要求转换为另一种形式
    • ArrayHandler:把结果集中的第一行数据转成对象数组。
    • ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
    • BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
    • BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
    • ColumnListHandler:将结果集中某一列的数据存放到List中。
    • KeyedHandler(name):将结果集中的每行数据都封装到Map里,再把这些map再存到一个map里,其key为指定的key。
    • MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
    • MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List。
DbUtils类查询
  1. 应用案例:使用DbUtils+数据库连接池(德鲁伊)方式完成对表actor的crud

    • 返回多行:new BeanListHandler<>(Actor.class)

      List<Actor> list = queryRunner.query(connection, sql, new BeanListHandler<>(Actor.class), 1);

      // 使用Apache-DbUtils+数据库连接池(德鲁伊)方式完成对表actor的crud
          @Test
          public void testQueryMany() throws Exception {  // 返回结果是多行的情况
              // 1. 得到连接对象
              Connection connection = JDBCUtilsByDruid.getConnection();
              // 2. 使用DbUtils类和接口,先引入相关的jar文件,再加入到本project
              // 3. 创建QueryRunner
              QueryRunner queryRunner = new QueryRunner();
              // 4. 就可以执行相关的方法,返回ArrayList结果集
      //        String sql = "select * from actor where id >= ?";
              // 注意:sql语句也可以查询部分列
              String sql = "select id, name from actor where id >= ?";
      
              /*
                  (1) query 方法就是执行sql语句,得到resultSet,然后封装到 ArrayList集合中
                  (2) 返回集合
                  (3) connection: 连接
                  (4) sql: 执行的sql语句
                  (5) new BeanListHandler<>(Actor.class): 在将resultSet取出Actor对象封装到ArrayList集合中
                      底层使用反射机制获取Actor类的属性,然后进行封装
                  (6) 1 就是给sql中的?赋值,可以由多个值,因为是可变参数 Object...params
                  (7) 底层得到的resultSet,会在query关闭,同时关闭PreparedStatement
               */
              List<Actor> list = queryRunner.query(connection, sql, new BeanListHandler<>(Actor.class), 1);
              System.out.println("输出集合信息");
              list.forEach(System.out::print);
      
              //  释放资源
              JDBCUtilsByDruid.close(null, null, connection);
          }
      
    • 返回一行:new BeanHandler<>(Actor.class)

      Actor actor = queryRunner.query(connection, sql, new BeanHandler<>(Actor.class), 2);

      // 使用Apache-DbUtils+数据库连接池(德鲁伊)方式 返回单行记录
      @Test
      public void testQuerySingle() throws Exception {
          // 1. 得到连接对象
          Connection connection = JDBCUtilsByDruid.getConnection();
          // 2. 使用DbUtils类和接口,先引入相关的jar文件,再加入到本project
          // 3. 创建QueryRunner
          QueryRunner queryRunner = new QueryRunner();
          // 4. 就可以执行相关的方法,返回ArrayList结果集
          String sql = "select * from actor where id = ?";
      
          // 因为我们返回的是单行记录<--->单个对象,使用的 Handler是 BeanHandler
          Actor actor = queryRunner.query(connection, sql, new BeanHandler<>(Actor.class), 10);
          System.out.println(actor);  // Actor{id=2, name='刘德华', sex='男', borndate=1970-01-01 00:00:00.0, phone='1500543'}
          // 若id不存在,返回null
      
          // 释放资源
          JDBCUtilsByDruid.close(null, null, connection);
      }
      
    • 返回单行单列:new ScalarHandler()

      Object obj = queryRunner.query(connection, sql, new ScalarHandler(), 2);

      // 使用Apache-DbUtils+数据库连接池(德鲁伊)方式 完成查询结果是单行单列的情况,返回的就是object
      @Test
      public void testQueryScalar() throws Exception {
          // 1. 得到连接对象
          Connection connection = JDBCUtilsByDruid.getConnection();
          // 2. 使用DbUtils类和接口,先引入相关的jar文件,再加入到本project
          // 3. 创建QueryRunner
          QueryRunner queryRunner = new QueryRunner();
          // 4. 就可以执行相关的方法,返回ArrayList结果集
          String sql = "select name from actor where id = ?";
      
          // 因为返回的是一个对象,使用的 Handler 是 ScalarHandler
          Object obj = queryRunner.query(connection, sql, new ScalarHandler(), 2);
          System.out.println(obj);  // 刘德华
          // 若id不存在,返回null
      
          // 释放资源
          JDBCUtilsByDruid.close(null, null, connection);
      }
      
DbUtils类DML操作

queryRunner.update()

// 使用Apache-DbUtils+数据库连接池(德鲁伊)方式 完成dml语句
@Test
public void testDML() throws Exception {
    // 1. 得到连接对象
    Connection connection = JDBCUtilsByDruid.getConnection();
    // 2. 使用DbUtils类和接口,先引入相关的jar文件,再加入到本project
    // 3. 创建QueryRunner
    QueryRunner queryRunner = new QueryRunner();
    // 4. 就可以执行相关的方法,返回ArrayList结果集
    // String sql = "update actor set name = ? where id = ?";
    // String sql = "insert into actor values(null, ?, ?, ?, ?)";
    String sql = "delete from actor where id = ?";

    // (1) 执行dml操作是queryRunner.update()
    // (2) 返回的是受影响的行数,若为0,则抛出异常
    // int affectedRows = queryRunner.update(connection, sql, "周华健", 4);
    // int affectedRows = queryRunner.update(connection, sql, "章子怡", "女", "1990-01-01", "12345678901");
    int affectedRows = queryRunner.update(connection, sql, 5);

    System.out.println(affectedRows > 0 ? "执行成功" : "数据库没有变化");

    // 释放资源
    JDBCUtilsByDruid.close(null, null, connection);
}

BasicDao

DAO和增删改查通用方法-BasicDao

问题引出

apache-dbutils+Druid 简化了JDBC开发,但还有不足:

  1. SQL语句是固定,不能通过参数传入,通用性不好,需要进行改进,更方便执行增删改查
  2. 对于select操作,如果有返回值,返回类型不能固定,需要使用泛型
  3. 将来的表很多,业务需求复杂,不可能只靠一个Java类完成
  4. 引出=》BasicDAO画出示意图实际开发中如何处理

image-20231217200311271

基本说明

  1. DAO:Data Access Object 数据访问对象
  2. 这样的通用类,称为BasicDao,是专门和数据库交互的,即完成对数据库(表)的crud操作
  3. 在BaiscDao的基础上,实现一张表对应一个Dao,更好的完成功能,比如Customer表-Customer.java类(javabean)-CustomerDao.java

代码示例

以actor为例:

  • utils

    package com.honvin.dao_.utils;
    
    import com.alibaba.druid.pool.DruidDataSourceFactory;
    
    import javax.sql.DataSource;
    import java.io.FileInputStream;
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.Properties;
    
    /**
     * @ClassName: JDBCUtilsByDruid
     * @Date: 2023/12/17 17:29
     * @Author: Honvin
     * @Software: IntelliJ IDEA
     * @Description: 基于Druid的JDBC工具类
     **/
    public class JDBCUtilsByDruid {
        private static DataSource ds;
    
        // 静态代码块完成ds初始化
        static {
            Properties properties = new Properties();
            try {
                properties.load(new FileInputStream("src\\druid.properties"));
                ds = DruidDataSourceFactory.createDataSource(properties);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        // 编写getConnection方法
        public static Connection getConnection() throws SQLException {
            return ds.getConnection();
        }
    
        // 关闭连接,在数据库连接池技术中,close不是真正断掉连接,而是把使用的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) {
                throw new RuntimeException(e);
            }
        }
    }
    
  • javabean/domain

    package com.honvin.dao_.domain;
    
    import java.util.Date;
    
    /**
     * @ClassName: Actor
     * @Date: 2023/12/17 17:52
     * @Author: Honvin
     * @Software: IntelliJ IDEA
     * @Description: Actor对象和actor表的记录对应
     **/
    public class Actor {  // Javabean, POJO, Domain对象
    
        private Integer id;
        private String name;
        private String sex;
        private Date borndate;
        private String phone;
    
        public Actor() {  // 一定要有一个无参构造器【反射需要】
        }
    
        public Actor(Integer id, String name, String sex, Date borndate, String phone) {
            this.id = id;
            this.name = name;
            this.sex = sex;
            this.borndate = borndate;
            this.phone = phone;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public Date getBorndate() {
            return borndate;
        }
    
        public void setBorndate(Date borndate) {
            this.borndate = borndate;
        }
    
        public String getPhone() {
            return phone;
        }
    
        public void setPhone(String phone) {
            this.phone = phone;
        }
    
        @Override
        public String toString() {
            return "Actor{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", sex='" + sex + '\'' +
                    ", borndate=" + borndate +
                    ", phone='" + phone + '\'' +
                    '}';
        }
    }
    
  • BasicDAO

    package com.honvin.dao_.dao;
    
    import com.honvin.dao_.utils.JDBCUtilsByDruid;
    import org.apache.commons.dbutils.QueryRunner;
    import org.apache.commons.dbutils.handlers.BeanHandler;
    import org.apache.commons.dbutils.handlers.BeanListHandler;
    import org.apache.commons.dbutils.handlers.ScalarHandler;
    
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.util.List;
    
    /**
     * @ClassName: BasicDAO
     * @Date: 2023/12/17 20:09
     * @Author: Honvin
     * @Software: IntelliJ IDEA
     * @Description: 开发BasicDAO,是其他DAO的父类
     **/
    public class BasicDAO<T> {  // 泛型指定具体类型
        private QueryRunner qr = new QueryRunner();
    
        /**
         * 开发通用的dml方法,针对任意的表
         * @param sql sql语句,可以有?
         * @param params 传入 ? 的具体的值,可以是多个
         * @return 返回受影响的行数
         */
        public  int update(String sql, Object... params) {
            Connection connection = null;
            try {
                connection = JDBCUtilsByDruid.getConnection();
                return qr.update(connection, sql, params);
            } catch (SQLException e) {
                throw new RuntimeException(e);
            } finally {
                JDBCUtilsByDruid.close(null, null,connection);
            }
        }
    
        /**
         * 返回多个对象(即查询的结果是多行),针对任意表
         * @param sql sql语句,可以有?
         * @param clazz 传入一个类的CLass对象,比如Actor.class
         * @param params 传入 ? 的具体的值,可以是多个
         * @return 根据Actor.class,返回对应的 ArrayList 集合
         */
        public List<T> queryMulti(String sql, Class<T> clazz, Object... params) {
            Connection connection = null;
            try {
                connection = JDBCUtilsByDruid.getConnection();
                return qr.query(connection, sql, new BeanListHandler<T>(clazz), params);
            } catch (SQLException e) {
                throw new RuntimeException(e);
            } finally {
                JDBCUtilsByDruid.close(null, null, connection);
            }
        }
    
        /**
         * 查询单行结果的通用方法
         * @param sql sql语句,可以有?
         * @param clazz 传入一个类的CLass对象,比如Actor.class
         * @param params 传入 ? 的具体的值,可以是多个
         * @return 根据Actor.class,返回 单个查询对象
         */
        public T querySingle(String sql, Class<T> clazz, Object... params) {
            Connection connection = null;
            try {
                connection = JDBCUtilsByDruid.getConnection();
                return qr.query(connection, sql, new BeanHandler<T>(clazz), params);
            } catch (SQLException e) {
                throw new RuntimeException(e);
            } finally {
                JDBCUtilsByDruid.close(null, null, connection);
            }
        }
    
        /**
         * 查询单行单列的通用方法,即返回单值的方法
         * @param sql sql语句,可以有?
         * @param params 传入 ? 的具体的值,可以是多个
         * @return 单值
         */
        public Object queryScalar(String sql, Object... params) {
            Connection connection = null;
            try {
                connection = JDBCUtilsByDruid.getConnection();
                return qr.query(connection, sql, new ScalarHandler(), params);
            } catch (SQLException e) {
                throw new RuntimeException(e);
            } finally {
                JDBCUtilsByDruid.close(null, null, connection);
            }
        }
    }
    
  • ActorDAO

    package com.honvin.dao_.dao;
    
    import com.honvin.dao_.domain.Actor;
    
    /**
     * @ClassName: ActorDAO
     * @Date: 2023/12/17 20:25
     * @Author: Honvin
     * @Software: IntelliJ IDEA
     * @Description:
     **/
    public class ActorDAO extends BasicDAO<Actor> {
        // 1. 就有 BasicDAO 所有的方法
        // 2. 根据业务需求,可以编写特有的方法
    }
    
  • TestDAO

    package com.honvin.dao_.test;
    
    import com.honvin.dao_.dao.ActorDAO;
    import com.honvin.dao_.domain.Actor;
    import org.junit.Test;
    
    import java.util.List;
    
    /**
     * @ClassName: TestDAO
     * @Date: 2023/12/17 20:27
     * @Author: Honvin
     * @Software: IntelliJ IDEA
     * @Description:
     **/
    public class TestDAO {
        // 测试 ActorDAO 对actor表的crud操作
        @Test
        public void testActorDAO() {
            ActorDAO actorDAO = new ActorDAO();
            // 1. 查询多行记录
            List<Actor> actors = actorDAO.queryMulti("select * from actor where id <= ?", Actor.class, 3);
            System.out.println("1. 查询多行记录");
            actors.forEach(System.out::println);
    
            // 2. 查询单行记录
            Actor actor = actorDAO.querySingle("select * from actor where id = ?", Actor.class, 2);
            System.out.println("2. 查询单行记录");
            System.out.println(actor);
    
            // 3. 查询单值记录
            Object o = actorDAO.queryScalar("select name from actor where id = ?", 2);
            System.out.println("3. 查询单值记录");
            System.out.println(o);
    
            // dml操作 insert, update, delete
            int update = actorDAO.update("insert into actor values(null, '章子怡', '女', '1982-09-17', '12345')");
            System.out.println("4. 更新记录");
            System.out.println(update > 0 ? "更新成功" : "执行没有影像数据表");
    
        }
    }
    

ctorDAO extends BasicDAO {
// 1. 就有 BasicDAO 所有的方法
// 2. 根据业务需求,可以编写特有的方法
}


* TestDAO

```java
package com.honvin.dao_.test;

import com.honvin.dao_.dao.ActorDAO;
import com.honvin.dao_.domain.Actor;
import org.junit.Test;

import java.util.List;

/**
 * @ClassName: TestDAO
 * @Date: 2023/12/17 20:27
 * @Author: Honvin
 * @Software: IntelliJ IDEA
 * @Description:
 **/
public class TestDAO {
    // 测试 ActorDAO 对actor表的crud操作
    @Test
    public void testActorDAO() {
        ActorDAO actorDAO = new ActorDAO();
        // 1. 查询多行记录
        List<Actor> actors = actorDAO.queryMulti("select * from actor where id <= ?", Actor.class, 3);
        System.out.println("1. 查询多行记录");
        actors.forEach(System.out::println);

        // 2. 查询单行记录
        Actor actor = actorDAO.querySingle("select * from actor where id = ?", Actor.class, 2);
        System.out.println("2. 查询单行记录");
        System.out.println(actor);

        // 3. 查询单值记录
        Object o = actorDAO.queryScalar("select name from actor where id = ?", 2);
        System.out.println("3. 查询单值记录");
        System.out.println(o);

        // dml操作 insert, update, delete
        int update = actorDAO.update("insert into actor values(null, '章子怡', '女', '1982-09-17', '12345')");
        System.out.println("4. 更新记录");
        System.out.println(update > 0 ? "更新成功" : "执行没有影像数据表");

    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值