Java面向对象【JDBC和数据库连接池】

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

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

在这里插入图片描述

JDBC API

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

JDBC程序编写步骤

前置工作:
下载mysql对应的驱动包【mysql-connector-java-5.1.37.jar】

https://wwc.lanzoul.com/isbbB0a577pa
密码:dia1

在项目下创建一个文件夹比如 libs
将 mysql-connector-java.jar 拷贝到该目录下, 点击 add as library 添加到项目中

  1. 注册驱动: 加载Driver类
  2. 获取连接: 得到Connection
  3. 执行增删改查:发送SQL给mysql执行
  4. 释放资源: 关闭相关连接
入门代码
package com.xiaolu.jdbc;

import com.mysql.jdbc.Driver;

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

/**
 * @author 林小鹿
 * @version 1.0
 */
public class Jdbc01 {

    public static void main(String[] args) throws SQLException {
        // 前置工作:在项目下创建一个文件夹比如 libs
        // 将 mysql.jar 拷贝到该目录下, 点击 add as library  添加到项目中
//        1. 注册驱动:   加载Driver类
        Driver driver = new Driver();

//        2. 获取连接:  得到Connection
        //(1)jdbc:mysql:// 规定好表示协议,通过jdbc的方式连接mysql
        //(2)localhost 主机,也可以是ip地址
        //(3)3306 表示mysql监听的端口
        //(4)db01 连接到mysql dbms 的哪个数据库
        // (5) mysql的连接本质就是socket连接
        String url = "jdbc:mysql://Localhost:3306/db01";
        // 将用户名和密码放入到 Properties 对象中
        Properties properties = new Properties();
        properties.setProperty("user", "root"); // 用户
        properties.setProperty("password", "123456"); // 密码
        Connection connect = driver.connect(url, properties);

//        3. 执行增删改查:发送SQL给mysql执行
        String sql = "insert into actor values(null, '林小鹿', '男', '2001-01-01', '110')";
        // Statement 用于执行静态SQL语句并返回其生成的结果的对象
        Statement statement = connect.createStatement();
        int rows = statement.executeUpdate(sql); // 如果是 dml 语句,返回的就是影响行数
        System.out.println(rows > 0 ? "成功": "失败");

//        4. 释放资源:  关闭相关连接
        statement.close();
        connect.close();
    }
}
获取数据库连接的五种方式

方式①

//获取Driver?实现类对象
Driver driver = new com.mysql.jdbc.Driver();
String url = "jdbc:mysql://localhost:3306/jdbc_db";
Properties info = new Properties();
info.setProperty("user","root");
info.setProperty("password","123");
Connection conn = driver.connect(url,info);
System.out.println(conn);

方式②

//方式1会直接使用com.mysql.jdbc.Driver(),属于静态加载,灵活性差,依赖强
//-推出–>方式2 通过反射实现动态加载

Class clazz = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver)clazz.newlnstance();
String url "jdbc:mysql://localhost:3306/jdbc_db";
Properties info = new Properties();
info.setProperty("user","root");
info.setProperty("password","abc123");
Connection conn = driver.connect(url,info);
System.out.println(conn);

方式③

// 使用**DriverManager【驱动管理类】**替换Driver

Class clazz = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver)clazz.newlnstance();
String url = "jdbc:mysql://localhost:3306/jdbc_db";
String user = "root";
String password = "123";
DriverManager.registerDriver(driver);  // 注册Driver驱动
Connection conn = DriverManager.getConnection(url,user,password);
System.out.println(conn);

方式④ - 推荐使用

// 使用Class.forName自动完成注册驱动,简化代码=>分析源码

提示:

  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"); 使之更加明确
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/jdbc_db";
String user = "root";
String password = "123";
Connection conn = DriverManager.getConnection(url,user,password);
System.out.println(conn);

方式⑤ - 推荐使用

// 在方式4的基础上改进,增加配置properties文件,使连接mysql更加灵活

在配置文件中定义和配置用户、密码、链接和驱动

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 connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);

ResultSet[结果集]

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

import com.mysql.jdbc.Driver;
import org.junit.jupiter.api.Test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;

/**
 * @author 林小鹿
 * @version 1.0
 * 通过配置文件连接mysql
 */
public class Jdbc02 {

    @Test
    public void connect() throws IOException, ClassNotFoundException, SQLException {
        // 通过配置文件连接mysql
        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 connection = DriverManager.getConnection(url, user, password);

        // 3、得到 Statement
        Statement statement = connection.createStatement();

        // 4.组织sql 语句
        String sql = "select id, name, sex, borndate from actor";
        // 执行给定的SQL语句,该语句返回单个 ResultSet 对象
        ResultSet resultSet = statement.executeQuery(sql);

        // 5.使用 while取出数据
        /* 原表:
         +----+-----------+-----+---------------------+--------+
         | id | name      | sex | borndate            | phone  |
        +----+-----------+-----+---------------------+--------+
        |  1 | 林小鹿    | 男  | 2001-01-01 00:00:00 | 110    |
        |  2 | 张三      | 男  | 2000-05-28 00:00:00 | 133192 |
        |  3 | 小李      | 女  | 2100-05-29 00:00:00 | 192    |
        +----+-----------+-----+---------------------+--------+
         */
        while (resultSet.next()) {
            int id = resultSet.getInt(1);
            // int id = resultSet.getInt("id"); // 通过列名来获取
            String name = resultSet.getString(2);
            String sex = resultSet.getString(3);
            String date = resultSet.getString(4);
            System.out.println(id + "\t"+name + "\t" + sex + "\t" + date);
        }

        // 6.关闭连接
        resultSet.close();
        statement.close();
        connection.close();
    }
}

Statement

Statement对象 用于执行静态SQL语句并返回其生成的结果对象

  • 在连接建立后,需要对数据库进行访问,执行 命令或是SQL语句,可以通过

    • Statement 【存在SQL注入】
    • PreparedStatement 【预处理】
    • CallableStatement 【存储过程】
  • Statement对象执行SQL 语句,存在SQL注入风险

  • SQL注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的SQL语句段或命令,恶意攻击数据库。

    • -- SQL
      -- 输入用户名为1' or
      -- 输入万能密码为 or '1'= '1
      SELECT
      FROM admin
      WHERE NAME ='1' OR'AND pwd = 'OR '1'= '1'
      
    • package com.xiaolu.jdbc;
      
      import java.io.FileInputStream;
      import java.sql.Connection;
      import java.sql.DriverManager;
      import java.sql.ResultSet;
      import java.sql.Statement;
      import java.util.Properties;
      import java.util.Scanner;
      
      /**
       * @author 林小鹿
       * @version 1.0
       * SQL注入问题
       */
      public class Jdbc03 {
          /*
          -- SQL
          -- 输入用户名为1' or
          -- 输入万能密码为or '1'= '1
           */
          public static void main(String[] args) throws Exception {
      
              Scanner scanner = new Scanner(System.in);
              System.out.print("请输入管理员的名字:"); // next():当接收到 空格或者'时,表示结束
              String admin_name = scanner.nextLine(); // 如果希望看到SQL注入,这里需要用nextLine,此时回车表示结束
              System.out.print("请输入管理员的密码:");
              String admin_pwd = scanner.nextLine();
      
              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<?> aClass = Class.forName(driver);
              // 2、得到连接
              Connection connection = DriverManager.getConnection(url, user, password);
      
              // 3、执行sql
              Statement statement = connection.createStatement();
              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("登录失败");
              }
      
              // 4、关闭
              resultSet.close();
              statement.close();
              connection.close();
      
          }
      }
      
  • 要防范SQL注入,只要用 PreparedStatement(从Statement扩展而来)取代Statement就可以了

PreparedStatement【接口】

  • PreparedStatement执行的SQL语句中的参数用问号(?)来表示,调用PreparedStatement对象的setXxx()方法来设置这些参数。setXxx()方法有两个参数,第一个参数是要设置的SQL语句中的参数的索引(从1开始),第二个是设置的SQL语句中的参数的值
  • 调用executeQuery(),返回ResultSet对象
  • 调用executeUpdate():执行更新,包括增、删、修改
  • 预处理好处
    • 不再使用 + 拼接sql语句,减少语法错误
    • 有效的解决了sql注入问题
    • 大大减少了编译次数,效率较高
代码
package com.xiaolu.jdbc.preparedstatement_;

import java.io.FileInputStream;
import java.sql.*;
import java.util.Properties;
import java.util.Scanner;

/**
 * @author 林小鹿
 * @version 1.0
 */
public class PreState {

    public static void main(String[] args) throws Exception{
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入管理员的名字:"); // next():当接收到 空格或者'时,表示结束
        String admin_name = scanner.nextLine(); // 如果希望看到SQL注入,这里需要用nextLine,此时回车表示结束
        System.out.print("请输入管理员的密码:");
        String admin_pwd = scanner.nextLine();

        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<?> aClass = Class.forName(driver);
        // 2、得到连接
        Connection connection = DriverManager.getConnection(url, user, password);

        // 3、执行sql
        // 这里的 ? 相当于占位符
        String sql = "select name, pwd from admin where name = ? and pwd = ?";
        // preparedStatement 对象实现了 PreparedStatement 接口的实现类的对象
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        // 给 ? 赋值
        preparedStatement.setString(1,admin_name);
        preparedStatement.setString(2,admin_pwd);

        // 执行 select 语句使用 executeQuery
        // 如果 执行 dml(update,insert,delete) 语句使用 executeUpdate
        // 这里在执行executeQuery时,不用再把sql语句传入
        ResultSet resultSet = preparedStatement.executeQuery();
        if (resultSet.next()) {
            System.out.println("登录成功");
        } else {
            System.out.println("登录失败");
        }

        // 4、关闭
        resultSet.close();
        preparedStatement.close();
        connection.close();

    }
}

JDBC API总结

在这里插入图片描述

封装JDBCUtils

在jdbc 操作中,获取连接和释放资源 是经常使用到,因此可以将其封装JDBC连接的工具类JDBCUtils

代码

properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mvc
user=xxx
password=xxx

JDBCUtils工具类1

package com.xiaolu.tools;

import java.sql.*;
import java.util.ResourceBundle;

/**
 * @author 林小鹿
 * @version 1.0
 * @create 2023/2/5 15:21
 * @Description
 */
public class JDBCUtils {

    private static ResourceBundle bundle = ResourceBundle.getBundle("resources/jdbc"); // 主目录下resources目录下的jdbc.properties文件
    private static String user = bundle.getString("user");
    private static String password = bundle.getString("password");
    private static String url = bundle.getString("url");
    private static String driver = bundle.getString("driver");

    private JDBCUtils() {
    }

    static {
        try {
            // 注册驱动
            Class.forName("com.mysql.jdbc.Driver");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // 获取连接
    public static Connection getConnection() {
        try {
            return DriverManager.getConnection(url, user, password);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    // 关闭相关资源
    public static void close(ResultSet set, Statement statement, Connection connection) {
        try {
            if (set != null) {
                set.close();
            }
            if (statement != null) {
                statement.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

}

JDBCUtils工具类2

package com.xiaolu.jdbc.utils;

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

/**
 * @author 林小鹿
 * @version 1.0
 * 这是一个工具类,完成 mysql的连接和关闭资源
 */
public class JDBCUtils {
    // 定义相关的属性(4个),因为只需要一份,因此做成static
    private static String user; // 用户名
    private static String password; // 密码
    private static String url; // url
    private static String driver; // 驱动名

    // 构造方法私有化 => 不让创建对象
    private JDBCUtils(){}
    
    // 在static代码块中初始化
    static {
        try {
            Properties properties = new Properties();
            properties.load(new FileInputStream("src:\\mysql.properties"));
            // 读取相关的属性值
             user = properties.getProperty("user");
             password = properties.getProperty("password");
             url = properties.getProperty("url");
             driver = properties.getProperty("driver");

        } catch (IOException e) {
            // 将 编译异常 转成 运行异常
            // 这时调用者可以选择捕获该异常,也可以选择默认的处理方式,比较方便
            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
        如果需要关闭资源,就传入对象,否则传入 null 即可
     */
    public static void close(ResultSet set, Statement statement, Connection connection) {
        // 判断是否为null
        try {
            if (set != null) {
                set.close();
            }
            if (statement != null) {
                statement.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            // 将 编译异常 转成 运行异常
            throw new RuntimeException(e);
        }
    }
}

案列演示

    @Test
    public void testDML() {// insert, update, delete
        // 1、得到连接
        Connection connection = null;

        // 2、组织sql语句
        String sql = "update actor set name = ? where id = ?";
        PreparedStatement preparedStatement = null;
        try {
            // 连接
            connection = JDBCUtils.getConnection();

            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, "凯哥");
//            preparedStatement.setString(1, "小李");
            preparedStatement.setInt(2, 3);
            // 执行
            preparedStatement.executeUpdate();

        } catch (SQLException e) {
            // 捕获、打印异常
            e.printStackTrace();
        } finally {
            JDBCUtils.close(null, preparedStatement, connection);
        }
    }

事务

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

import com.xiaolu.jdbc.utils.JDBCUtils;
import org.junit.jupiter.api.Test;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * @author 林小鹿
 * @version 1.0
 * 演示jdbc 中使用mysql事务
 */
public class Transaction_ {

    @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;
        try {
            // 连接
            connection = JDBCUtils.getConnection();
            // 将connection 设置为不自动提交,即 开启事务
            connection.setAutoCommit(false);
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.executeUpdate(); // 执行第一条

            int i = 1 / 0; // 抛出异常
            preparedStatement = connection.prepareStatement(sql2);
            preparedStatement.executeUpdate(); // 执行第二条

            // 如果没有发生异常,提交事务
            connection.commit();

        } catch (SQLException e) {
            // 抛出异常后,在这里有机会进行回滚,即撤销执行的SQL
            // rollback() 默认回滚到事务开始的状态
            System.out.println("发生了异常,撤销执行的sql...");
            try {
                connection.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            JDBCUtils.close(null, preparedStatement, connection);
        }
    }
}

批处理

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

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

    • addBatch():添加需要批量处理的SQL语句或参数
    • executeBatch():执行批量处理语句
    • clearBatch0:清空批处理包的语句
  • **注意:**JDBC连接MySQL时,如果要使用批处理功能,需要在url中加参数
    ?rewriteBatchedStatements=true

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

  • for(inti=0;i<5000;1++){//5000执行
        preparedStatement.setstring(1,"jack"i);
        preparedstatement.setstring(2,"666");
        //将sqL语句加入到批处理包中->看源码
        preparedstatement.addBatch();
        //当有1000条记录时,在批量执行
        1f((i+1) % 1000 == 0){// 每满1000条sq1
            preparedstatement.executeBatch();
            //清空一把
            preparedstatement.clearBatch();
        }
    }
    

底层

  • 第一就创建ArrayList - elementData => Object[]
  • elementData => Object[] 就会存放我们预处理的sql语句
  • 当elementData满后,就按照1.5扩容
  • 当添加到指定的值后,就executeBatch
  • 批量处理会减少我们发送SqL语句的网络开销,而且减少编译次数,因此效率提高

数据库连接池

c3p0和德鲁伊下载

链接:https://pan.baidu.com/s/1VdUSzs2ZT-i8ve97iaelng
提取码:pbkb

传统的方式获取Connection连接问题分析

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

数据库连接池基本介绍

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

数据库连接池种类

在这里插入图片描述

注意:在数据库连接池技术中,close不是真的断掉连接,而是把使用的Connection对象放回连接池

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

C3P0 数据库连接池的使用

  • 下载相关的包

    • 将 c3p0-0.9.5.2.jar和mchange-commons-java-0.2.11.jar导入到项目中

    • 编写 c3p0-config.xml 配置文件,并导入到项目的 src 下

      • <?xml version="1.0" encoding="utf-8"?>
        <c3p0-config>
          <default-config>
            <!-- 驱动类 -->
            <property name="driverClass">com.mysql.jdbc.Driver</property>
            <!-- url -->
            <property name="jdbcUrl">jdbc:mysql://localhost:3306/db01</property>
            <!-- 用户名 -->
            <property name="user">root</property>
            <!-- 密码 -->
            <property name="password">123456</property>
            <!-- 每次增长的连接数 -->
            <property name="acquireIncrement">5</property>
            <!-- 初始的连接数 -->
            <property name="initialPoolSize">5</property>
            <!-- 最小连接数 -->
            <property name="minPoolSize">5</property>
            <!-- 最大连接数 -->
            <property name="maxPoolSize">50</property>
            <!-- 可连接的最多命令对象数 -->
            <property name="maxStatements">5</property>
            <!-- 每个连接对象可连接的最多的命令对象数 -->
            <property name="maxStatementsPerConnection">2</property>
            <!-- 超时等待时间 -->
            <property name="checkoutTimeout">3000</property>
          </default-config>
        
          <!--   数据源名称  代表连接池 -->
          <named-config name="xiaoLu_db">
          </named-config>
        </c3p0-config>
        
  • 使用

    • 方式一

      • public void test01() throws Exception{
              // 1.创建一个数据源对象
              ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
              // 2.通过配置文件获取相关连接的信息
              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");
        
              // 给数据源(连接池) comboPooledDataSource 设置相关的参数
              // 注意:连接管理是由 comboPooledDataSource 来管理
              comboPooledDataSource.setDriverClass(driver);
              comboPooledDataSource.setJdbcUrl(url);
              comboPooledDataSource.setUser(user);
              comboPooledDataSource.setPassword(password);
        
              // 初始化连接数
              comboPooledDataSource.setInitialPoolSize(10);
              // 最大连接数
              comboPooledDataSource.setMaxPoolSize(50);
              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次连接mysql 耗时=" + (end - start));  // 235
        
          }
        
        
    • 方式二(使用配置文件) — 推荐使用

      • public void test02() throws SQLException {
                // 使用连接池的名称创建对象
                ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("xiaoLu_db");
        
                // 连接
                Connection connection = comboPooledDataSource.getConnection();
                System.out.println("连接成功");
        
                // 关闭连接
                connection.close();
        
            }
        
        }
        

Druid(德鲁伊) 数据库连接池的使用

  • 下载相关的包

    • 加入 Druid的 jar 包

    • 加入druid.properties配置文件,将配置文件加入到项目的 src 目录下

      • driverClassName=com.mysql.jdbc.Driver
        url=jdbc:mysql://127.0.0.1:3306/db01?rewriteBatchedStatements=true
        username=root
        password=123456
        #initialSize connection Size
        initialSize=10
        #max active connection size
        maxActive=50
        #max wait time
        maxWait=5000
        #min idle connection size
        minIdle=5
        
  • 使用

    •     public void test() throws Exception {
              //1.加入Druid jar包
              //2.加入配置文件druid.properties,将该文件拷贝项目的src目录
              //3.创建Properties,对象,读取配置文件
              Properties properties = new Properties();
              properties.load(new FileInputStream("src\\druid.properties"));
      
              // 4.创建一个指定参数的数据库连接池,Druid连接池,并返回一个数据源
              DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
              Connection connection = dataSource.getConnection();
              System.out.println("连接成功");
              // 5. 关闭连接
              connection.close();
          }
      }
      
将Druid集成工具类JDBCUtils
package com.xiaolu.jdbc.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;

/**
 * @author 林小鹿
 * @version 1.0
 * 给予druid数据库连接池的工具类
 */
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) {
            e.printStackTrace();
        }
    }

    // 编写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 test() {
    System.out.println("使用 druid 的方式来完成");
    // 1、得到连接
    Connection connection = null;
    Statement statement = null;
    ResultSet resultSet = null;

    // 2、组织sql语句
    String sql = "select id, name, sex, borndate from actor";
    try {
        // 连接
        connection = JDBCUtilsByDruid.getConnection();
        statement = connection.createStatement();
        resultSet = statement.executeQuery(sql);
        while (resultSet.next()) {
            int id = resultSet.getInt(1);
            // int id = resultSet.getInt("id"); // 通过列名来获取
            String name = resultSet.getString(2);
            String sex = resultSet.getString(3);
            String date = resultSet.getString(4);
            System.out.println(id + "\t"+name + "\t" + sex + "\t" + date);
        }

    } catch (SQLException e) {
        // 捕获异常
        e.printStackTrace();
    } finally {
        JDBCUtilsByDruid.close(resultSet, statement, connection);
    }
}

Apache — DBUtils

解决resultSet的问题
  1. 结果集和connection 是关联的,即如果关闭连接,就不能使用结果集
  2. 结果集不利于数据管理【只能使用一次】
  3. 使用返回信息也不方便
土方法

resultSet → 数据封装到类 → 封装到集合

在这里插入图片描述

基本介绍
  • commons-dbutils 是 Apache 组织提供的一个开源JDBC工具类库,它是对JDBC的封装,使用dbutilsi能极大简化 jdbc 编码的工作量。
  • DbUtils类
    • QueryRunner类:该类封装了SQL的执行,是线程安全的。可以实现增、删、改、查、批处理
    • 使用QueryRunner类实现查询
    • 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 类和接口,需先引入DBUtils 相关的jar 包【此处使用的是commons-dbutils-1.7.jar】,并加入到项目中
  • 使用
    1. 得到连接 (druid)
    2. 创建QueryRunner
    3. 执行相关的方法,返回arrayList 结果集
    4. 释放资源(放回连接)
使用案例代码
package com.xiaolu.jdbc.datasource;

import com.xiaolu.jdbc.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 org.junit.jupiter.api.Test;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

/**
 * @author 林小鹿
 * @version 1.0
 * 使用apache-DBUtils 工具类 + druid 完成对表的 crud操作
 */
public class DBUtils_USE {
    // apache-dbutils + druid 完成 返回的结果是全部记录 --> 返回的结果为ArrayList
    @Test
    public void testQueryMany() throws SQLException {// 返回结果是多行的情况
        // 1、得到连接 (druid)
        Connection connection = JDBCUtilsByDruid.getConnection();
        // 2、使用DBUtils 类和接口,需先引入DBUtils 相关的jar 包,并加入到项目中
        // 3、创建QueryRunner
        QueryRunner queryRunner = new QueryRunner();
        // 4、执行相关的方法,返回arrayList 结果集
        String sql = "select * from actor where id >= ?";
        //(1)query 方法就是执行sql语句,得到resultSet ---> 封装到 --> ArrayList集合中
        //(2)返回集合
        //(3)connection:连接
        //(4)sql:执行的sgL语句
        //(5)new BeanListHandler<>(Actor.class):在将resultSet -> Actor对象 -> 封装到ArrayList
        //   底层使用反射机制去获取Actor下类的属性,然后进行封装
        //(6)此处的 2 就是给sql 语句中的? 赋值,因为是可变参数,可以有多个值
        List<Actor> list =
                queryRunner.query(connection, sql, new BeanListHandler<>(Actor.class), 2);
        System.out.println("遍历集合的信息");
        for (Actor actor : list) {
            System.out.print(actor);
        }

        //(7)释放资源(放回连接)
        //   底层会得到resultSet,会在 query 中关闭 resultSet 和 PrepareStatement
        JDBCUtilsByDruid.close(null, null, connection);
    }

    // apache-dbutils + druid 完成 返回的结果是单行记录 --> 返回的结果为 T【对应的domain类型】
    @Test
    public void testQuerySingle() throws SQLException {
        Connection connection = JDBCUtilsByDruid.getConnection();
        QueryRunner queryRunner = new QueryRunner();
        String sql = "select * from actor where id = ?";
        //因为我们返回的单行记录<-->单个对象,使用的 Handler 是BeanHandler
        Actor actor = queryRunner.query(connection, sql, new BeanHandler<>(Actor.class), 1);
        System.out.println(actor);
        // 释放资源
        JDBCUtilsByDruid.close(null, null, connection);
    }

    // apache-dbutils + druid 完成 返回的结果是单行单列 --> 返回的结果为object
    @Test
    public void testScalar() throws SQLException {
        Connection connection = JDBCUtilsByDruid.getConnection();
        QueryRunner queryRunner = new QueryRunner();
        // 执行相关的方法,返回单行单列,返回的就是object
        String sql = "select name from actor where id = ?";
        Object obj = queryRunner.query(connection, sql, new ScalarHandler<>(), 1);

        System.out.println(obj);
        JDBCUtilsByDruid.close(null, null, connection);
    }

    // apache-dbutils + druid 完成 dml (update, insert, delete)
    @Test
    public void testDML() throws SQLException {
        Connection connection = JDBCUtilsByDruid.getConnection();
        QueryRunner queryRunner = new QueryRunner();

        String sql = "update actor set name = ? where id = ?";
        //String sql = "insert into actor values(null, ?, ?, ?, ?)";
        //String sql = "delete from actor where id = ?";
        // 执行dml 语句的操作都是 queryRunner.update(),返回的值是受影响的行数
        int affectedRow = queryRunner.update(connection, sql, "小米", 2);
        System.out.println(affectedRow > 0 ? "影响行数:" + affectedRow : "未影响到表");

        JDBCUtilsByDruid.close(null, null, connection);

    }

}

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

apache-dbutils+Druid简化了JDBC开发,但还有不足:
  1. SQL语句是固定,不能通过参数传入,通用性不好,需要进行改进,更方便执行 增删改查
  2. 对于select操作,如果有返回值,返回类型不能固定,需要使用泛型
  3. 将来的表很多,业务需求复杂,不可能只靠一个Java类完成
基本说明

在这里插入图片描述

  • DAO:data access object 数据访问对象
  • 这样的通用类,称为 BasicDao,是专门和数据库交互的,即完成对数据库(表)的crud操作。
  • 在BaiscDao的基础上,实现一张表对应一个Dao,更好的完成功能,比如
    Customer表 — Customer.java(javabean) — CustomerDao.java
实现步骤
  • 设计

    • com.xiaolu.dao_
      • com.xiaolu.dao_.utils //工具类
      • com.xiaolu.dao_.domain //javabean
        • domain中的属性名一定要和表的列名保持一致
        • 底层是通过MySQL查询得到的结果所在列来查询domain中的属性名,再根据列名调用相应的set方法来赋值
      • com.xiaolu.dao_.dao //存放XxxDAO和BasicDAO
      • com.xiaolu.dao_.test //写测试类
  • BasicBAO代码

    • package com.xiaolu.dao_.dao;
      
      import com.xiaolu.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;
      
      /**
       * @author 林小鹿
       * @version 1.0
       * 其他DAO的父类
       */
      public class BasicDAO<T> { // 泛型指定具体类型
          private QueryRunner qr = new QueryRunner();
      
          // 开发通用的dml方法,针对任意的表
          public int update(String sql, Object... parameters) {
              Connection connection = null;
              try {
                  connection = JDBCUtilsByDruid.getConnection();
                  int update = qr.update(connection, sql, parameters);
                  return update;
      
              } catch (SQLException e) {
                  throw new RuntimeException(e); // 将编译异常 -> 运行异常抛出
              } finally {
                  JDBCUtilsByDruid.close(null, null, connection);
              }
      
      
          }
      
          // 返回多个对象(即查询的结果是多行),针对任意表
          /**
           *
           * @param sql sql 语句,可以有 ?
           * @param clazz 传入一个类的Class对象 比如 Actor.class
           * @param parameters 传入 ? 的具体的值,可以是多个
           * @return
           */
          public List<T> queryMulti(String sql, Class<T> clazz, Object... parameters) {
              Connection connection = null;
              try {
                  connection = JDBCUtilsByDruid.getConnection();
                  return qr.query(connection, sql, new BeanListHandler<T>(clazz), parameters);
      
              } catch (SQLException e) {
                  throw new RuntimeException(e);
              } finally {
                  JDBCUtilsByDruid.close(null, null, connection);
              }
      
          }
      
          // 查询单行结果的通用方法
          public T querySingle(String sql, Class<T> clazz, Object... parameters) {
              Connection connection = null;
              try {
                  connection = JDBCUtilsByDruid.getConnection();
                  return qr.query(connection, sql, new BeanHandler<T>(clazz), parameters);
      
              } catch (SQLException e) {
                  throw new RuntimeException(e);
              } finally {
                  JDBCUtilsByDruid.close(null, null, connection);
              }
          }
      
          // 查询单行单列的方法,即返回单值的方法
          public Object queryScalar(String sql, Object... parameters) {
              Connection connection = null;
              try {
                  connection = JDBCUtilsByDruid.getConnection();
                  return qr.query(connection, sql, new ScalarHandler<>(), parameters);
      
              } catch (SQLException e) {
                  throw new RuntimeException(e);
              } finally {
                  JDBCUtilsByDruid.close(null, null, connection);
              }
          }
      }
      
  • ActorDAO

    • package com.xiaolu.dao_.dao;
      
      import com.xiaolu.dao_.domain.Actor;
      
      /**
       * @author 林小鹿
       * @version 1.0
       * 执行Actor表的操作
       * 可以根据业务需求,编写特有的方法
       */
      public class ActorDAO extends BasicDAO<Actor>{
      }
      
  • JDBCUtilsByDruid

    • package com.xiaolu.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;
      
      /**
       * @author 林小鹿
       * @version 1.0
       * 给予druid数据库连接池的工具类
       */
      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) {
                  e.printStackTrace();
              }
          }
      
          // 编写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);
              }
          }
      }
      
  • Actor

    • package com.xiaolu.dao_.domain;
      
      import java.util.Date;
      
      /**
       * @author 林小鹿
       * @version 1.0
       * 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 "\nActor{" +
                      "id=" + id +
                      ", name='" + name + '\'' +
                      ", sex='" + sex + '\'' +
                      ", borndate=" + borndate +
                      ", phone='" + phone + '\'' +
                      '}';
          }
      }
      
  • TestDAO

    • package com.xiaolu.dao_.test;
      
      import com.xiaolu.dao_.dao.ActorDAO;
      import com.xiaolu.dao_.domain.Actor;
      import org.junit.jupiter.api.Test;
      
      import java.util.List;
      
      /**
       * @author 林小鹿
       * @version 1.0
       * 测试
       */
      public class TestDAO {
      
          // 测试ActorDAO 对actor表crud操作
          @Test
          public void testActorDAO() {
              ActorDAO actorDAO = new ActorDAO();
              // 查询
              List<Actor> actors = actorDAO.queryMulti("select * from actor where id >= ?", Actor.class, 2);
              for (Actor actor : actors) {
                  System.out.print(actor);
              }
      
              // 查询单行记录
              System.out.println("\n=====查询单行记录=====");
              Actor actor = actorDAO.querySingle("select * from actor where id = ?", Actor.class, 1);
              System.out.println(actor);
      
              // 查询单行单列
              System.out.println("====查询单行单列====");
              Object o = actorDAO.queryScalar("select name from actor where id = ?", 1);
              System.out.println(o);
      
              // dml操作
      //        int update = actorDAO.update("insert into actor values(null, ?, ?, ?, ?)", "张三", "男", "2000-11-22", "991");
              int update = actorDAO.update("delete from actor where id = ?", 5);
              System.out.println(update > 0 ? "执行成功" : "未受影响");
      
          }
      }
      
  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

進擊的小鹿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值