JDBC详解

发布时间:2024年01月07日

Course:JDBC 数据库编程

Author:小温

一、JDBC介绍

JDBC (Java DataBase Contectivity) Java与数据库的连接——数据库编程

JDBC 是Java语言(JDK)为完成数据库的访问操作提供的一套统一的标准。

二、JDBC步骤

三、JDBC入门案例

JDBC 是用Java代码完成数据访问的规范

3.1 加载驱动
3.1.1 下载驱动jar包

对应数据库匹配版本的驱动jar包

  • 地址https://mvnrepository.com/

  • 搜索mysql

  • 选择版本下载

    MySQL数据库版本为5.x,驱动jar包建议使用5.1.47

    MySQL数据库版本为8.x,驱动jar包建议使用8.0.x

3.1.2 将驱动jar文件添加到Java应用
  • 在java应用中创建lib文件夹

  • 将下载好的jar文件拷贝—粘贴到lin目录

  • 将驱动jar文件设置为java库:选择lib/驱动jar---右键---Add as Library.. ---OK

3.1.3 注册驱动

通过反射机制,将驱动jar文件中提供的驱动类载入到JVM中

//1.注册驱动(驱动jar包中的驱动类(Driver)的路径:com.mysql.cj.jdbc.Driver)
Class.forName("com.mysql.cj.jdbc.Driver");
3.2 创建连接
//2.创建连接:通过数据库驱动管理器 DriverManager(java.sql.Driver)获取连接
//  JDBC JDK提供的数据库连接的规范 --- java.sql
//  java.sql.Connection接口  一个对象就表示一个数据库连接
//  url 数据库的统一资源定位器(定位数据库连接资源)
//  url参数
// ? ?  characterEncoding 设置编码
// ? ?  useSSL=true  使用SSL通信
// ? ?  useTimezone=true  设置客户端和服务端时区转换
String url = "jdbc:mysql://localhost:3306/db_test3";
Connection connection = DriverManager.getConnection(url,"root","@QFedu123");
3.3 编写SQL指令
//3.编写要执行的SQL指令:SQL指令中需要的参数可以通过字符串拼接的形式(会导致SQL注入)
String sql = "insert into books(book_id,book_name,book_author,book_price,book_stock,book_desc) values('"+id+"','"+name+"','"+author+"',"+price+","+stock+",'"+desc+"')";
3.4 加载SQL指令
//4.加载SQL指令:获取SQL指令的加载器
//  java.sql.Statement 对象 可以理解为 SQL指令的“加载器”
//  java.sql.PreparedStatement 对象 ? SQL指令的“预编译加载器”
Statement statement = connection.createStatement();
3.5 执行SQL、获取结果
//5.执行SQL获取执行结果
// a. 如果SQL指令为DQL,则  ResultSet rs = statement.executeQuery(sql);  rs中就是查询结果
// b. 如果SQL指令为DML,则  int i = statement.executeUpdate(sql); ? i就表示DML操作影响的数据行数
?
// 如果i>0,表示DML操作是成功的;如果i=0表示DML操作对数据表中的数据没有影响
int i = statement.executeUpdate(sql); ?
3.6 处理结果
//6.处理结果(业务)
// 添加操作:返回值i>0表示添加成功,否则表示添加失败
// 修改操作:返回值i>0表示修改对数据有影响,如果i=0 表示对数据库没有影响
// 删除操作:返回值i>0表示删除对数据有影响,如果i=0 表示对数据库没有影响
// 查询操作:从ResultSet rs中取出查询结果,封装到Java对象中
System.out.println(i>0?"添加成功":"添加失败");
3.7 关闭连接
//7.关闭连接
// 如果执行DML操作:需要关闭Statement和Connection
// 如果执行DQL操作:需要关闭ResultSet、Statement和Connection
// 关闭之前先判空、由大到大关闭
if(statement != null && !statement.isClosed()){
 ? ?statement.close();
}
if(connection !=null && !connection.isClosed()){
 ? ?connection.close();
}

四、JDBC增删查改操作实例

使用JDBC完成数据库的CRUD访问

4.1 insert操作
/**
 * @Author 小温
 *
 */
public class TestInsertBook {
?
 ? ?public static void main(String[] args) throws ClassNotFoundException, SQLException {
?
 ? ? ? ?String name = "Java进阶之路";
 ? ? ? ?String author = "老王";
 ? ? ? ?double price = 22.22;
 ? ? ? ?int stock = 10;
 ? ? ? ?String desc = "这本书很好";
?
 ? ? ? ?//1.注册驱动(驱动jar包中的驱动类(Driver)的路径:com.mysql.cj.jdbc.Driver)
 ? ? ? ?Class.forName("com.mysql.cj.jdbc.Driver");
?
 ? ? ? ?//2.创建连接:通过数据库驱动管理器 DriverManager(java.sql.Driver)获取连接
 ? ? ? ?String url = "jdbc:mysql://localhost:3306/db_test3";
 ? ? ? ?Connection connection = DriverManager.getConnection(url,"root","@QFedu123");
?
 ? ? ? ?//3.编写要执行的SQL指令
 ? ? ? ?String sql = "insert into books(book_name,book_author,book_price,book_stock,book_desc) values('"+name+"','"+author+"',"+price+","+stock+",'"+desc+"')";
?
 ? ? ? ?//4.加载SQL指令:获取SQL指令的加载器
 ? ? ? ?Statement statement = connection.createStatement();
?
 ? ? ? ?//5.执行SQL获取执行结果
 ? ? ? ?int i = statement.executeUpdate(sql); ?
 ? ? ? ?
 ? ? ? ?//6.处理结果(业务)
 ? ? ? ?System.out.println(i>0?"添加成功":"添加失败");
?
 ? ? ? ?//7.关闭连接
 ? ? ? ?if(statement != null && !statement.isClosed()){
 ? ? ? ? ? ?statement.close();
 ? ? ?  }
 ? ? ? ?if(connection !=null && !connection.isClosed()){
 ? ? ? ? ? ?connection.close();
 ? ? ?  }
?
 ?  }
?
}
4.2 delete操作
/**
 * @Author 小温
 */
public class TestDeleteBook {
?
 ? ?public static void main(String[] args) throws ClassNotFoundException, SQLException {
 ? ? ? ?int bid = 1012;
 ? ? ? ?//使用JDBC,根据图书编号删除图书信息
 ? ? ? ?//1.注册驱动
 ? ? ? ?Class.forName("com.mysql.cj.jdbc.Driver");
 ? ? ? ?//2.创建连接
 ? ? ? ?String url = "jdbc:mysql://localhost:3306/db_test3?characterEncoding=utf8";
 ? ? ? ?Connection connection = DriverManager.getConnection(url, "root", "@QFedu123");
 ? ? ? ?//3.编写SQL
 ? ? ? ?String sql = "delete from books where book_id="+bid;
 ? ? ? ?//4.获取SQL加载器
 ? ? ? ?Statement statement = connection.createStatement();
 ? ? ? ?//5.执行SQL
 ? ? ? ?int i = statement.executeUpdate(sql);
 ? ? ? ?//6.处理结果
 ? ? ? ?System.out.println(i>0?"删除成功":"删除失败");
 ? ? ? ?//7.关闭连接
 ? ? ? ?if(statement!=null && !statement.isClosed()){
 ? ? ? ? ? ?statement.close();
 ? ? ?  }
 ? ? ? ?if(connection!=null && !connection.isClosed()){
 ? ? ? ? ? ?connection.close();
 ? ? ?  }
 ?  }
?
}
4.3 update操作
package com.qfedu.jdbc.les1;
?
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
?
/**
 @Author 小温
 */
public class TestUpdateBook {
?
 ? ?public static void main(String[] args) throws ClassNotFoundException, SQLException {
 ? ? ? ?// 根据数据记录的主键 修改其他的字段
 ? ? ? ?// 根据图书ID,修改图书名称、作者、价格、库存、描述
 ? ? ? ?int id = 1011;
 ? ? ? ?String name = "Java进阶之路";
 ? ? ? ?String author = "亮亮";
 ? ? ? ?double price = 36.80;
 ? ? ? ?int stock = 8;
 ? ? ? ?String desc = "这本书非常好";
?
 ? ? ? ?Class.forName("com.mysql.cj.jdbc.Driver");
 ? ? ? ?String url = "jdbc:mysql://localhost:3306/db_test3?characterEncoding=utf8";
 ? ? ? ?Connection connection = DriverManager.getConnection(url, "root", "@QFedu123");
 ? ? ? ?String sql = "update books set book_name='"+name+"',book_author='"+author+"',book_price="+price+",book_stock="+stock
 ? ? ? ? ? ? ? ?+",book_desc='"+desc+"' where book_id="+id;
 ? ? ? ?Statement statement = connection.createStatement();
 ? ? ? ?int i = statement.executeUpdate(sql);
 ? ? ? ?System.out.println(i>0?"修改成功":"修改失败");
 ? ? ? ?if(statement!=null && !statement.isClosed()){
 ? ? ? ? ? ?statement.close();
 ? ? ?  }
 ? ? ? ?if(connection!=null && !connection.isClosed()){
 ? ? ? ? ? ?connection.close();
 ? ? ?  }
 ?  }
}
4.4 select操作(一条结果)
结果集处理
package com.qfedu.jdbc.les1;

import java.sql.*;

/**
 @Author 小温
 */
public class TestSelectBook {

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //根据图书ID,查询一本图书信息
        int bid = 1011;
        Class.forName("com.mysql.cj.jdbc.Driver");
        String url = "jdbc:mysql://localhost:3306/db_test3?characterEncoding=utf8";
        Connection connection = DriverManager.getConnection(url, "root", "@QFedu123");
        String sql = "select book_id,book_name,book_author,book_price,book_stock,book_desc from books where book_id="+bid;
        Statement statement = connection.createStatement();
        //通过executeQuery方法执行查询语句,并且将查询的结果存放到一个ResultSet对象中(结果集)
        ResultSet rs = statement.executeQuery(sql);
        //处理结果:从rs中获取查询结果
        if(rs.next()){
            int bookId = rs.getInt("book_id");
            String bookName = rs.getString("book_name");
            String bookAuthor = rs.getString("book_author");
            double bookPrice = rs.getDouble("book_price");
            int bookStock = rs.getInt("book_stock");
            String bookDesc = rs.getString("book_desc");
            System.out.println(bookId+"\t"+bookName+"\t"+bookAuthor+"\t"+bookPrice+"\t"+bookStock+"\t"+bookDesc);
        }
        //关闭连接  结果集也需要关闭
        if(rs!=null && !rs.isClosed()){
            rs.close();
        }
        if(statement!=null && !statement.isClosed()){
            statement.close();
        }
        if(connection!=null && !connection.isClosed()){
            connection.close();
        }

    }
}
4.5 select操作(多条结果)
package com.qfedu.jdbc.les1;

import java.sql.*;

/**
 @Author 小温
 */
public class TestSelectBooks {

    public static void main(String[] args) throws ClassNotFoundException, SQLException {

        //查询所有图书信息
        Class.forName("com.mysql.cj.jdbc.Driver");
        String url = "jdbc:mysql://localhost:3306/db_test3?characterEncoding=utf8";
        Connection connection = DriverManager.getConnection(url, "root", "@QFedu123");
        String sql = "select book_id,book_name,book_author,book_price,book_stock,book_desc from books";
        Statement statement = connection.createStatement();
        ResultSet rs = statement.executeQuery(sql);
        while(rs.next()){
            int bookId = rs.getInt("book_id");
            String bookName = rs.getString("book_name");
            String bookAuthor = rs.getString("book_author");
            double bookPrice = rs.getDouble("book_price");
            int bookStock = rs.getInt("book_stock");
            String bookDesc = rs.getString("book_desc");
            System.out.println(bookId+"\t"+bookName+"\t"+bookAuthor+"\t"+bookPrice+"\t"+bookStock+"\t"+bookDesc);
        }
        if(rs!=null && !rs.isClosed()){
            rs.close();
        }
        if(statement!=null && !statement.isClosed()){
            statement.close();
        }
        if(connection!=null && !connection.isClosed()){
            connection.close();
        }
    }
}

五、JDBC的核心类与接口

java.sql.DriverManager类 驱动管理器

java.sql.Connection接口 数据库连接

java.sql.Statement接口 SQL指令的“加载/执行器”

java.sql.ResultSet接口 结果集

5.1 DriverManager类
  • 注册驱动

  • 创建数据库连接

  • 注册驱动

    // 在Driver类中的静态初始化块中,注册驱动:DriverManager.registerDriver(new Driver());
    Class.forName("com.mysql.cj.jdbc.Driver");
    
    // 在我们的应用程序中手动注册驱动的代码也可以省略 【Class.forName("com.mysql.cj.jdbc.Driver");】
    // 如果我们没有手动注册驱动,驱动管理器在获取连接的时候发现没有注册驱动则读取 驱动jar/META-INF/servicesjava.sql.Driver文件中配置的驱动类路径进行注册
  • 获取连接

    // url  数据库服务器的地址
    // username  数据库连接用户名
    // password  数据库连接密码
    Connection connection = DriverManager.getConnection(url, "root", "@QFedu123");
5.2 Connection接口

Connection对象表示Java应用程序与数据库之间的连接

  • 通过Connection接口对象,获取执行SQL语句的Statement对象

  • 完成数据的事务管理

5.2.1 获取Statement对象
  • Statement接口: 编译执行静态SQL指令

    Statement statement = connection.createStatement();
  • PreparedStatement接口:继承了Statement接口,预编译动态SQL指令(解决SQL注入问题)

    PreparedStatement preparedStatement = connection.prepareStatement(sql);
  • CallableStatement接口:继承了PreparedStatement接口,可以调用存储过程

     CallableStatement callableStatement = connection.prepareCall(sql);
5.2.2 事务管理
//开启事务(关闭事务自动提交)
connection.setAutoCommit(false);

//事务回滚
connection.rollback();

//提交事务
connection.commit();
5.3 Statement接口

用于编译、执行SQL指令的

// 执行DML操作的SQL指令
int i = statement.executeUpdate(sql);

// 执行DQL操作的SQL指令
ResultSet rs = statement.executeQuery(sql);
5.4 ResultSet接口

ResultSet接口对象,表示查询操作返回的结果集,提供了便利的方法用于获取结果集中的数据

  • 判断结果集中是否还有数据未取出

    ResultSet rs = statement.executeQuery(select_statement);
    
    // 判断rs中是否还有数据
    rs.next();
  • 获取rs指向的结果集中某行的数据

    int id = rs.getInt(String columnLable);  //获取rs指向的行的  列 数据(字段名)
    int id = rs.getInt(int columnIndex);
    
    rs.getString(""); //获取String类型数据
    rs.getDouble(""); //获取Double类型数据
    ....
    rs.getDate(""); //获取日期类型数据

六、SQL注入问题

6.1 什么是SQL注入问题?

在JDBC操作SQL指令编写过程中,如果SQL指令中需要数据,我们可以通过字符串拼接的形式将参数拼接到SQL指令中,如String sql = "delete from books where book_id="+s;(s就是拼接到SQL中的变量)

使用字符串拼接变量的形式来设置SQL语句中的数据,可能会导致因变量值的改变引起SQL指令的原意发生改变,这就被称为SQL注入。SQL注入问题是需要避免的。

例如:

  • 如果s的值为1,SQL指令 : delete from books where book_id=1

  • 如果s的值为1 or 1=1,SQL指令:delete from books where book_id=1 or 1=1

6.2 如何解决SQL注入问题?

使用PreparedStatement进行SQL预编译解决SQL注入问题:

  • 在编写SQL指令时,如果SQL指令中需要参数,一律使用?参数占位符

  • 如果SQL指令中有?,在JDBC操作步骤中不再使用Statement,而是从Conection对象获取PreparedStatement对SQL指令进行预编译 PreparedStatement preparedStatement = connection.prepareStatement(sql);

  • 预编译完成之后,通过PreparedStatement对象给预编译后的SQL指令的?复制

    • prepareadStatement.setInt(参数占位符序号,值);

    • prepareadStatement.setString(参数占位符序号,值);

  • SQL指令中的所有?完成赋值之后,通过PreparedStatement执行SQL执行SQL时不再加载SQL

    • int i = prepareadStatement.executeUpdate();

    • ResultSet rs = preparedStatement.executeQuery();

6.3 使用PreparedStatement预编译案例
  • 删除操作

package com.qfedu.jdbc.les1;

import com.mysql.cj.jdbc.Driver;

import java.sql.*;
import java.util.Scanner;

/**
 @Author 小温
 */
public class TestDeleteBook {

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入你要删除的图书的ID:");
        String s = scanner.nextLine();
        int bid = Integer.parseInt(s);

        //1.注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2.创建连接
        String url = "jdbc:mysql://localhost:3306/db_test3?characterEncoding=utf8";
        Connection connection = DriverManager.getConnection(url, "root", "@QFedu123");
        //3.编写SQL:SQL指令中的参数使用?表示
        String sql = "delete from books where book_id=?";
        //4.如果SQL指令有参数占位符?,则从Connection获取PreparedStatement预编译SQL指令
        //  预编译:在SQL指令中的参数赋值之前对SQL执行的语法结构进行编译
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //  SQL指令预编译之后,给SQL中的?赋值
        preparedStatement.setInt(1,bid);
        //5.执行SQL
        int i = preparedStatement.executeUpdate();
        //6.处理结果
        System.out.println(i>0?"删除成功":"删除失败");
        //7.关闭连接
        if(preparedStatement!=null && !preparedStatement.isClosed()){
            preparedStatement.close();
        }
        if(connection!=null && !connection.isClosed()){
            connection.close();
        }
    }

}
  • 修改操作

package com.qfedu.jdbc.les1;

import java.sql.*;

/**
 @Author 小温
 */
public class TestInsertBook {

    public static void main(String[] args) throws ClassNotFoundException, SQLException {

        String name = "Java进阶之路";
        String author = "老王";
        double price = 22.22;
        int stock = 10;
        String desc = "这本书很好";


        //1.注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");

        //2.创建连接
        String url = "jdbc:mysql://localhost:3306/db_test3";
        Connection connection = DriverManager.getConnection(url,"root","@QFedu123");

        //3.编写要执行的SQL指令:SQL指令中所需的参数一律使用?
        String sql = "insert into books(book_name,book_author,book_price,book_stock,book_desc) values(?,?,?,?,?)";

        //4.SQL中有? 使用PreparedStatement预编译SQL
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        
        // 给SQL指令中的?复制
        preparedStatement.setString(1,name);
        preparedStatement.setString(2,author);
        preparedStatement.setDouble(3,price);
        preparedStatement.setInt(4,stock);
        preparedStatement.setString(5,desc);

        //5.执行SQL获取执行结果
        int i = preparedStatement.executeUpdate(); 

        //6.处理结果(业务)
        System.out.println(i>0?"添加成功":"添加失败");

        //7.关闭连接
        if(preparedStatement != null && !preparedStatement.isClosed()){
            preparedStatement.close();
        }
        if(connection !=null && !connection.isClosed()){
            connection.close();
        }

    }

}
  • 练习1:修改操作

package com.qfedu.jdbc.les1;

import java.sql.*;

/**
 @Author 小温
 */
public class TestUpdateBook {

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 根据数据记录的主键 修改其他的字段
        // 根据图书ID,修改图书名称、作者、价格、库存、描述
        int id = 1011;
        String name = "Java进阶之路";
        String author = "亮亮";
        double price = 36.80;
        int stock = 8;
        String desc = "这本书非常好";

        Class.forName("com.mysql.cj.jdbc.Driver");
        String url = "jdbc:mysql://localhost:3306/db_test3?characterEncoding=utf8";
        Connection connection = DriverManager.getConnection(url, "root", "@QFedu123");
        //1.
        String sql = "update books set book_name=?,book_author=?,book_price=?,book_stock=?,book_desc=? where book_id=?";
        //2.
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //3.
        preparedStatement.setString(1,name);
        preparedStatement.setString(2,author);
        preparedStatement.setDouble(3,price);
        preparedStatement.setInt(4,stock);
        preparedStatement.setString(5,desc);
        //4.
        int i = preparedStatement.executeUpdate();
        System.out.println(i>0?"修改成功":"修改失败");
        //5.
        if(preparedStatement!=null && !preparedStatement.isClosed()){
            preparedStatement.close();
        }
        if(connection!=null && !connection.isClosed()){
            connection.close();
        }
    }
}
  • 练习2:根据ID查询图书信息

package com.qfedu.jdbc.les1;

import java.sql.*;

/**
 @Author 小温
 */
public class TestSelectBook {

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //根据图书ID,查询一本图书信息
        int bid = 1013;
        Class.forName("com.mysql.cj.jdbc.Driver");
        String url = "jdbc:mysql://localhost:3306/db_test3?characterEncoding=utf8";
        Connection connection = DriverManager.getConnection(url, "root", "@QFedu123");
        String sql = "select book_id,book_name,book_author,book_price,book_stock,book_desc from books where book_id=?";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setInt(1,bid);
        //通过executeQuery方法执行查询语句,并且将查询的结果存放到一个ResultSet对象中(结果集)
        ResultSet rs = preparedStatement.executeQuery();
        //处理结果:从rs中获取查询结果
        if(rs.next()){
            int bookId = rs.getInt("book_id");
            String bookName = rs.getString("book_name");
            String bookAuthor = rs.getString("book_author");
            double bookPrice = rs.getDouble("book_price");
            int bookStock = rs.getInt("book_stock");
            String bookDesc = rs.getString("book_desc");
            System.out.println(bookId+"\t"+bookName+"\t"+bookAuthor+"\t"+bookPrice+"\t"+bookStock+"\t"+bookDesc);
        }
        //关闭连接  结果集也需要关闭
        if(rs!=null && !rs.isClosed()){
            rs.close();
        }
        if(preparedStatement!=null && !preparedStatement.isClosed()){
            preparedStatement.close();
        }
        if(connection!=null && !connection.isClosed()){
            connection.close();
        }

    }
}

备注 如果SQL指令中需要参数,则使用PreparedStatement进行SQL预编译,如果SQL指令中没有参数建议使用Statement(Statement执行效率要高于PreparedStatement)。

七、工具类封装

7.1 代码的复用性

在我们的应用程序中,如果需要完成相同的操作,相同的代码无需重复编写,我们只需一次编写多次调用即可!

JDBC数据库编程是由一个固定的步骤:

  1. 注册驱动

  2. 创建连接

  3. 编写SQL

  4. 获取Statement对象

  5. 执行SQL

  6. 处理结果

  7. 关闭连接

7.2 工具类封装

DBManager

DBUtil

JDBCUtil

DBHelper

package com.qfedu.jdbc.utils;

import java.sql.*;

/**
 * @Descript  JDBC数据库连接帮助类
 @Author 小温
 */
public class DBHelper {

    /*将创建数据库连接所需的字符串定义为常量,集中管理*/
    private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
    private static final String URL = "jdbc:mysql://localhost:3306/db_test3?characterEncoding=utf8";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "@QFedu123";

    /**
     * 注册驱动只需执行一次,因此我们放在帮助类的静态初始化块中完成
     */
    static{
        try {
            Class.forName(DRIVER);
        } catch (ClassNotFoundException e) {
            System.out.println("-----------注册驱动失败");
        }
    }

    /**
     * 创建数据库连接对象
     */
    public static Connection getConnection(){
        Connection connection = null;
        try {
            connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
        } catch (SQLException e) {
            System.out.println("-----------创建连接失败");
        }
        return connection;
    }

    /**
     * 关闭连接
     * 多态的应用:使用Statement接口做参数,既可以传递Statement接口对象,
     * 也可以传递PreparedStatement接口对象
     */
    public static void close(Statement statement, Connection connection){
        close(null,statement,connection);
    }

    /**
     * 关闭连接
     */
    public static void close(ResultSet resultSet,Statement statement, Connection connection){
        try {
            if(resultSet!=null && !resultSet.isClosed()){
                resultSet.close();
            }
            if(statement!=null && !statement.isClosed()){
                statement.close();
            }
            if(connection!=null && !connection.isClosed()){
                connection.close();
            }
        }catch (Exception e){
            System.out.println("~~~~~关闭数据库连接失败");
        }
    }

}

八、DAO与DTO的封装

DAO Data Access Object 数据访问对象

DTO Data Transfer Object 数据传输对象(实体类) 用于传输DML操作参数及DQL的查询结果

8.1 CRUD方法的封装

面向对象的特征之一——封装

我们将能够完成某个CRUD操作的代码单独定义成一个方法,当需要完成此CRUD操作时调用这个方法即可;

  • insert操作封装

  • delete操作封装

  • update操作封装

8.2 DTO实体类封装

问题:在封装CRUD方法时,对于查询操作而言,需要将查询到的数据库记录返回给调用者,但是一个查询方法只能返回一个值,而一条数据库记录有多个值,如何将一条数据库记录的多个值返回呢?

处理:在Java程序中创建一个属性与数据库表匹配的类,通过此类的对象封装查询到的数据,我们把用于传递JDBC增删查改操作的数据的对象称之为数据传输对象——DTO(实体类:带有属性,其对象可以存放数据的类)

实体类创建规则:

 * 1.类中属性的个数和类型,与对应的数据表保持一致
 * 2.提供所有属性的get和set方法:Alt+Insert --- Getter And Setter --- 选择所有属性 --- OK
 * 3.提供全参构造器:Alt+Insert --- Constructor --- 选择所有属性 --- OK
 * 4.提供无参构造器:Alt+Insert --- Constructor --- SelectNone
 * 5.重写toString方法:Alt+Insert --- toString --- OK
 * 6.[暂略] 重写hashcode和equals
 * 7.[暂略]实现序列化Serializable接口

实体类实例:

package com.qfedu.jdbc.dto;

import java.io.Serializable;

/**
 * @Descript 存放图书信息的类
 @Author 小温
 */
public class Book implements Serializable {

    private int bookId;
    private String bookName;
    private String bookAuthor;
    private double bookPrice;
    private int bookStock;
    private String bookDesc;

    @Override
    public String toString() {
        return "Book{" +
                "bookId=" + bookId +
                ", bookName='" + bookName + '\'' +
                ", bookAuthor='" + bookAuthor + '\'' +
                ", bookPrice=" + bookPrice +
                ", bookStock=" + bookStock +
                ", bookDesc='" + bookDesc + '\'' +
                '}';
    }

    public Book() {
    }

    public Book(int bookId, String bookName, String bookAuthor, double bookPrice, int bookStock, String bookDesc) {
        this.bookId = bookId;
        this.bookName = bookName;
        this.bookAuthor = bookAuthor;
        this.bookPrice = bookPrice;
        this.bookStock = bookStock;
        this.bookDesc = bookDesc;
    }

    //get和set方法
}

使用实体类封装查询操作返回的结果:

/**
* 根据图书ID查询一条图书记录
*/
public Book queryBook(int bid) throws SQLException{
    Book book = null;
    Connection connection = DBHelper.getConnection();
    String sql = "select book_id,book_name,book_author,book_price,book_stock,book_desc from books where book_id=?";
    PreparedStatement preparedStatement = connection.prepareStatement(sql);
    preparedStatement.setInt(1,bid);
    //通过executeQuery方法执行查询语句,并且将查询的结果存放到一个ResultSet对象中(结果集)
    ResultSet rs = preparedStatement.executeQuery();
    //处理结果:从rs中获取查询结果
    if(rs.next()){
        int id = rs.getInt("book_id");
        String name = rs.getString("book_name");
        String author = rs.getString("book_author");
        double price = rs.getDouble("book_price");
        int stock = rs.getInt("book_stock");
        String desc = rs.getString("book_desc");
        //我们需要将查询到的一条数据库图书记录的6个值返回
        book = new Book(id,name,author,price,stock,desc);
    }
    //关闭连接  结果集也需要关闭
    DBHelper.close(rs,preparedStatement,connection);
    return book;
}
8.3 实体类封装查询结果

查询的每条结果分别存放到一个DTO对象中,再将多个DTO对象存放到一个List集合中,返回这个List集合

package com.qfedu.jdbc.les1;

import com.qfedu.jdbc.dto.Book;
import com.qfedu.jdbc.utils.DBHelper;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @Description
 @Author 小温
 */
public class TestSelectBooks {

    public static void main(String[] args) throws ClassNotFoundException, SQLException {

        List<Book> list = new TestSelectBooks().listBooks();
        for(Book b:list){
            System.out.println(b.getBookName()+"\t"+b.getBookAuthor());
        }
    }

    public List<Book> listBooks() throws SQLException{
        List<Book> bookList = new ArrayList<>();
        //查询所有图书信息
        Connection connection = DBHelper.getConnection();
        String sql = "select book_id,book_name,book_author,book_price,book_stock,book_desc from books";
        Statement statement = connection.createStatement();
        ResultSet rs = statement.executeQuery(sql);
        while(rs.next()){
            int bookId = rs.getInt("book_id");
            String bookName = rs.getString("book_name");
            String bookAuthor = rs.getString("book_author");
            double bookPrice = rs.getDouble("book_price");
            int bookStock = rs.getInt("book_stock");
            String bookDesc = rs.getString("book_desc");
            Book book = new Book(bookId, bookName, bookAuthor, bookPrice, bookStock, bookDesc);
            bookList.add(book);
        }
        DBHelper.close(rs,statement,connection);
        return bookList;
    }

}
8.4 实体类传递添加、修改操作参数

在JDBC的添加、修改等方法中需要多个数据,我们可以通过实体类来进行参数传递

8.4.1 添加操作
public boolean insertBook(Book book) throws SQLException {
    boolean flag = false;
    //调用工具类,获取数据库连接对象
    Connection connection = DBHelper.getConnection();
    String sql = "insert into books(book_name,book_author,book_price,book_stock,book_desc) values(?,?,?,?,?)";
    PreparedStatement preparedStatement = connection.prepareStatement(sql);
    preparedStatement.setString(1,book.getBookName());
    preparedStatement.setString(2,book.getBookAuthor());
    preparedStatement.setDouble(3,book.getBookPrice());
    preparedStatement.setInt(4,book.getBookStock());
    preparedStatement.setString(5,book.getBookDesc());
    int i = preparedStatement.executeUpdate();  
    flag = i>0;
    //关闭连接
    DBHelper.close(preparedStatement,connection);
    return flag;
}
8.4.2 修改操作
 public boolean updateBook(Book book) throws SQLException{
     boolean flag = false;
     Connection connection = DBHelper.getConnection();
     String sql = "update books set book_name=?,book_author=?,book_price=?,book_stock=?,book_desc=? where book_id=?";
     PreparedStatement preparedStatement = connection.prepareStatement(sql);
     preparedStatement.setString(1,book.getBookName());
     preparedStatement.setString(2,book.getBookAuthor());
     preparedStatement.setDouble(3,book.getBookPrice());
     preparedStatement.setInt(4,book.getBookStock());
     preparedStatement.setString(5,book.getBookDesc());
     preparedStatement.setInt(6,book.getBookId());
     int i = preparedStatement.executeUpdate();
     flag = i>0;
     DBHelper.close(preparedStatement,connection);
     return flag;
 }
8.5 DAO类封装

DAO封装:将对数据库中同一张数据表的JDBC操作方法封装到同一个Java类中,这个类就是访问此数据表的数据访问对象

import java.util.ArrayList;
import java.util.List;

/**
 * @Descript DAO Data Access Object 数据访问对象
 @Author 小温
 */
public class BookDAO {

    public boolean deleteBook(int bid) throws SQLException {
        boolean flag = false;
        //使用JDBC,根据图书编号删除图书信息
        //1.注册驱动 创建连接
        Connection connection = DBHelper.getConnection();
        //3.编写SQL
        String sql = "delete from books where book_id=?";
        //4.如果SQL指令有参数占位符?,则从Connection获取PreparedStatement预编译SQL指令
        //  预编译:在SQL指令中的参数赋值之前对SQL执行的语法结构进行编译
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //  SQL指令预编译之后,给SQL中的?赋值
        preparedStatement.setInt(1,bid);
        //5.执行SQL
        int i = preparedStatement.executeUpdate();
        //6.处理结果
        flag = i>0;
        //7.关闭连接
        DBHelper.close(preparedStatement,connection);
        return true;
    }

    /**
     * 添加图书
     * @return 如果添加成功返回true,如果添加失败则返回false
     * @throws SQLException
     */
    public boolean insertBook(Book book) throws SQLException {
        boolean flag = false;
        //调用工具类,获取数据库连接对象
        Connection connection = DBHelper.getConnection();
        String sql = "insert into books(book_name,book_author,book_price,book_stock,book_desc) values(?,?,?,?,?)";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setString(1,book.getBookName());
        preparedStatement.setString(2,book.getBookAuthor());
        preparedStatement.setDouble(3,book.getBookPrice());
        preparedStatement.setInt(4,book.getBookStock());
        preparedStatement.setString(5,book.getBookDesc());
        int i = preparedStatement.executeUpdate();  // 如果i>0,表示DML操作是成功的;如果i=0表示DML操作对数据表中的数据没有影响
        flag = i>0;
        //关闭连接
        DBHelper.close(preparedStatement,connection);
        return flag;
    }

    /**
     * 根据图书ID查询一条图书记录
     */
    public Book queryBook(int bid) throws SQLException{
        Book book = null;
        Connection connection = DBHelper.getConnection();
        String sql = "select book_id,book_name,book_author,book_price,book_stock,book_desc from books where book_id=?";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setInt(1,bid);
        //通过executeQuery方法执行查询语句,并且将查询的结果存放到一个ResultSet对象中(结果集)
        ResultSet rs = preparedStatement.executeQuery();
        //处理结果:从rs中获取查询结果
        if(rs.next()){
            int id = rs.getInt("book_id");
            String name = rs.getString("book_name");
            String author = rs.getString("book_author");
            double price = rs.getDouble("book_price");
            int stock = rs.getInt("book_stock");
            String desc = rs.getString("book_desc");
            //我们需要将查询到的一条数据库图书记录的6个值返回
            book = new Book(id,name,author,price,stock,desc);
        }
        //关闭连接  结果集也需要关闭
        DBHelper.close(rs,preparedStatement,connection);
        return book;
    }

    public List<Book> listBooks() throws SQLException{
        List<Book> bookList = new ArrayList<>();
        //查询所有图书信息
        Connection connection = DBHelper.getConnection();
        String sql = "select book_id,book_name,book_author,book_price,book_stock,book_desc from books";
        Statement statement = connection.createStatement();
        ResultSet rs = statement.executeQuery(sql);
        while(rs.next()){
            int bookId = rs.getInt("book_id");
            String bookName = rs.getString("book_name");
            String bookAuthor = rs.getString("book_author");
            double bookPrice = rs.getDouble("book_price");
            int bookStock = rs.getInt("book_stock");
            String bookDesc = rs.getString("book_desc");
            Book book = new Book(bookId, bookName, bookAuthor, bookPrice, bookStock, bookDesc);
            bookList.add(book);
        }
        DBHelper.close(rs,statement,connection);
        return bookList;
    }

    public boolean updateBook(Book book) throws SQLException{
        boolean flag = false;
        Connection connection = DBHelper.getConnection();
        String sql = "update books set book_name=?,book_author=?,book_price=?,book_stock=?,book_desc=? where book_id=?";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setString(1,book.getBookName());
        preparedStatement.setString(2,book.getBookAuthor());
        preparedStatement.setDouble(3,book.getBookPrice());
        preparedStatement.setInt(4,book.getBookStock());
        preparedStatement.setString(5,book.getBookDesc());
        preparedStatement.setInt(6,book.getBookId());
        int i = preparedStatement.executeUpdate();
        flag = i>0;
        DBHelper.close(preparedStatement,connection);
        return flag;
    }

}
8.6 DAO类代码优化

1.在应用程序开发中,如果方法中抛出异常且自己可以处理,则直接通过try/catch进行捕获处理;

2.JDBC操作方法的连接需要放在finally中进行关闭;

3.将数据库连接Connection、Statement、ResultSet等需要关闭的数据库对象定义在try之前;

4.因为所有的JDBC操作都需要Conection、Statement对象,查询方法都需要ResultSet对象,因此在DAO中可以将这些对象定义成类的成员变量

package com.qfedu.jdbc.dao;

import com.qfedu.jdbc.dto.Book;
import com.qfedu.jdbc.utils.DBHelper;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @Descript DAO Data Access Object 数据访问对象
 @Author 小温
 */
public class BookDAO {

    private Connection connection;
    private Statement statement;
    private PreparedStatement preparedStatement;
    private ResultSet rs;

    public boolean deleteBook(int bid) {
        boolean flag = false;
        try{
            connection = DBHelper.getConnection();
            String sql = "delete from books where book_id=?";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setInt(1,bid);
            int i = preparedStatement.executeUpdate();
            flag = i>0;
        }catch(SQLException e){
            e.printStackTrace();
        }finally {
            DBHelper.close(preparedStatement,connection);
        }
        return flag;
    }

    public boolean insertBook(Book book) {
        boolean flag = false;
        try{
            //调用工具类,获取数据库连接对象
            connection = DBHelper.getConnection();
            String sql = "insert into books(book_name,book_author,book_price,book_stock,book_desc) values(?,?,?,?,?)";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1,book.getBookName());
            preparedStatement.setString(2,book.getBookAuthor());
            preparedStatement.setDouble(3,book.getBookPrice());
            preparedStatement.setInt(4,book.getBookStock());
            preparedStatement.setString(5,book.getBookDesc());
            int i = preparedStatement.executeUpdate();  // 如果i>0,表示DML操作是成功的;如果i=0表示DML操作对数据表中的数据没有影响
            flag = i>0;
        }catch (SQLException e){
            e.printStackTrace();
        }finally {
            DBHelper.close(preparedStatement,connection);
        }
        return flag;
    }

    public Book queryBook(int bid) {
        Book book = null;
        try {
            connection = DBHelper.getConnection();
            String sql = "select book_id,book_name,book_author,book_price,book_stock,book_desc from books where book_id=?";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setInt(1, bid);
            //通过executeQuery方法执行查询语句,并且将查询的结果存放到一个ResultSet对象中(结果集)
            rs = preparedStatement.executeQuery();
            //处理结果:从rs中获取查询结果
            if (rs.next()) {
                int id = rs.getInt("book_id");
                String name = rs.getString("book_name");
                String author = rs.getString("book_author");
                double price = rs.getDouble("book_price");
                int stock = rs.getInt("book_stock");
                String desc = rs.getString("book_desc");
                //我们需要将查询到的一条数据库图书记录的6个值返回
                book = new Book(id, name, author, price, stock, desc);
            }
        }catch (SQLException e){
            e.printStackTrace();
        } finally {
            DBHelper.close(rs, preparedStatement, connection);
        }
        return book;
    }

    public List<Book> listBooks(){
        List<Book> bookList = new ArrayList<>();
        try {
            connection = DBHelper.getConnection();
            String sql = "select book_id,book_name,book_author,book_price,book_stock,book_desc from books";
            statement = connection.createStatement();
            rs = statement.executeQuery(sql);
            while (rs.next()) {
                int bookId = rs.getInt("book_id");
                String bookName = rs.getString("book_name");
                String bookAuthor = rs.getString("book_author");
                double bookPrice = rs.getDouble("book_price");
                int bookStock = rs.getInt("book_stock");
                String bookDesc = rs.getString("book_desc");
                Book book = new Book(bookId, bookName, bookAuthor, bookPrice, bookStock, bookDesc);
                bookList.add(book);
            }
        }catch (SQLException e){
            e.printStackTrace();
        }finally {
            DBHelper.close(rs, statement, connection);
        }
        return bookList;
    }

    public boolean updateBook(Book book)  {
        boolean flag = false;
        try {
            connection = DBHelper.getConnection();
            String sql = "update books set book_name=?,book_author=?,book_price=?,book_stock=?,book_desc=? where book_id=?";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, book.getBookName());
            preparedStatement.setString(2, book.getBookAuthor());
            preparedStatement.setDouble(3, book.getBookPrice());
            preparedStatement.setInt(4, book.getBookStock());
            preparedStatement.setString(5, book.getBookDesc());
            preparedStatement.setInt(6, book.getBookId());
            int i = preparedStatement.executeUpdate();
            flag = i > 0;
        }catch (SQLException e){
            e.printStackTrace();
        } finally {
            DBHelper.close(preparedStatement, connection);
        }
        return flag;
    }

}

九、JDBC综合案例

完成学生信息的CRUD操作

9.1 JDBC数据库编程的流程
  • 创建数据库、数据表

    create table students(
    	stu_num char(8) primary key,
    	stu_name varchar(20) not null,
    	stu_gender char(2) not null,
    	stu_age int not null
    );
  • 创建新的Java工程

  • 创建JDBC的工具类:DBHelper

  • 创建DTO类(用于封装参数及查询结果)

  • 创建DAO类(用于完成CRUD操作)

9.2 创建JDBC工具类
  • 在Java工程中创建package :com.qfedu.jdbc.utils

  • com.qfedu.jdbc.utils包中创建工具类DBHelper

  • 编写DBHelper工具类:

    • 添加驱动jar文件

    • 编写DBHelper代码

9.3 创建DTO类
  • 在Java工程中创建package:com.qfedu.jdbc.dto

  • com.qfedu.jdbc.dto创建数据表对应的实体类Student

  • 编写实体类

    package com.qfedu.jdbc.dto;
    
    /**
     * @Descript 用于传递学生信息的实体类
     @Author 小温
     */
    public class Student {
    
        private String stuNum;
        private String stuName;
        private String stuGender;
        private int stuAge;
    
        @Override
        public String toString() {
            return "Student{" +
                    "stuNum='" + stuNum + '\'' +
                    ", stuName='" + stuName + '\'' +
                    ", stuGender='" + stuGender + '\'' +
                    ", stuAge=" + stuAge +
                    '}';
        }
    
        public Student() {
        }
    
        public Student(String stuNum, String stuName, String stuGender, int stuAge) {
            this.stuNum = stuNum;
            this.stuName = stuName;
            this.stuGender = stuGender;
            this.stuAge = stuAge;
        }
        //get  set
    }
9.4 创建DAO类

创建DAO类,完成JDBC操作

  • 在Java项目中创建package:com.qfedu.jdbc.dao

  • com.qfedu.jdbc.dao包中创建类StudentDAO

package com.qfedu.jdbc.dao;

import com.qfedu.jdbc.dto.Student;
import com.qfedu.jdbc.utils.DBHelper;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @Descript students表的数据访问类
 @Author 小温
 */
public class StudentDAO {

    private Connection connection;
    private Statement statement;
    private PreparedStatement preparedStatement;
    private ResultSet resultSet;

    //1、添加学生信息
    public boolean insert(Student student){
        boolean flag = false;
        try {
            connection = DBHelper.getConnectin();
            String sql = "insert into students(stu_num,stu_name,stu_gender,stu_age) values(?,?,?,?)";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1,student.getStuNum());
            preparedStatement.setString(2,student.getStuName());
            preparedStatement.setString(3,student.getStuGender());
            preparedStatement.setInt(4,student.getStuAge());
            int i = preparedStatement.executeUpdate();
            flag = i>0;
        }catch (SQLException e){
            e.printStackTrace();
        }finally {
            DBHelper.close(preparedStatement,connection);
        }
        return flag;
    }


    //2、根据学号查询一个学生记录
    public Student queryStudent(String snum){
        Student student = null;
        try{
            connection = DBHelper.getConnectin();
            String sql = "select stu_num,stu_name,stu_gender,stu_age from students where stu_num=?";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1,snum);
            resultSet = preparedStatement.executeQuery();
            if(resultSet.next()){
                String stuNum = resultSet.getString("stu_num");
                String stuName = resultSet.getString("stu_name");
                String stuGender = resultSet.getString("stu_gender");
                int stuAge = resultSet.getInt("stu_age");
                student = new Student(stuNum,stuName,stuGender,stuAge);
            }
        }catch (SQLException e){
            e.printStackTrace();
        }finally {
            DBHelper.close(resultSet,preparedStatement,connection);
        }
        return student;
    }


    //3、查询多个学生信息
    public List<Student> listStudents(){
        List<Student> studentList = new ArrayList<>();
        try{
            connection = DBHelper.getConnectin();
            String sql = "select stu_num,stu_name,stu_gender,stu_age from students";
            statement = connection.createStatement();
            resultSet = statement.executeQuery(sql);
            while(resultSet.next()){
                String stuNum = resultSet.getString("stu_num");
                String stuName = resultSet.getString("stu_name");
                String stuGender = resultSet.getString("stu_gender");
                int stuAge = resultSet.getInt("stu_age");
                Student s = new Student(stuNum,stuName,stuGender,stuAge);
                studentList.add(s);
            }
        }catch (SQLException e){
            e.printStackTrace();
        } finally {
            DBHelper.close(resultSet,statement,connection);
        }
        return studentList;
    }

}
9.5 测试DAO类中的方法

使用junit对DAO中创建的JDBC方法进行单元测试

9.5.1 下载导入junit依赖到项目中
  • 下载:https://mvnrepository.com/ (如果使用junit 4.10 + 版本单元测试,需要以下两个jar文件)

    • junit-4.12.jar

    • hamcrest-core-1.3.jar

  • junit-4.12.jarhamcrest-core-1.3.jar拷贝项目的lib文件夹

  • 选择拷贝到项目中的jar文件---右键---Add as Library

9.5.2 创建单元测试类

如果我们需要对某个类中的方法进行单元测试,我们需要创建这个类的测试类

StudentDAO ---------> StudentDAOTest(测试类:类名=被测试类+Test)

package com.qfedu.jdbc.test;

import com.qfedu.jdbc.dao.StudentDAO;
import com.qfedu.jdbc.dto.Student;
import org.junit.Test;

import java.util.List;

import static org.junit.Assert.*;

/**
 * @Descript 此类是StudentDAO的单元测试类
  @Author 小温
 */
public class StudentDAOTest {

    //1.在测试类中定义成员变量:创建被测试类的对象
    private StudentDAO studentDAO = new StudentDAO();

    //2创建测试方法 : 用来测试StudentDAO类中的insertStudent方法
    //a.测试方法名=test+被测试方法名
    //b.测试方法无参数无返回值
    @Test
    public void testInsertStudent(){
        //准备被测试方法所需的参数
        Student stu = new Student("1008","Tom","男",20);
        //调用被测试方法,获取结果
        boolean b = studentDAO.insertStudent(stu);
        //断言返回结果(成立 | 不成立)
        assertTrue(b);
    }

    @Test
    public void testQueryStudent(){
        String snum = "1008";
        Student student = studentDAO.queryStudent(snum);
        assertEquals("Tom2",student.getStuName());
    }

    @Test
    public void testListStudents(){
        List<Student> studentList = studentDAO.listStudents();
        assertEquals(8,studentList.size());
    }
}

十、JDBC事务管理

  • 什么是事务?

  • 事务的四大特性---ACID

  • 事务的隔离级别

  • MySQL事务管理:

    • start transaction

    • rollback

    • commit

10.1 JDBC实现借书操作
  1. records表添加借书记录

  2. 修改books表中的库存

package com.qfedu.jdbc.dao;

import com.qfedu.jdbc.utils.DBHelper;

import java.sql.*;

/**
 * @Descript
 @Author 小温
 */
public class BookDAO {

    /**
     * 借书:
     * @param stuNum 借书的学生学号
     * @param bookId 借书的图书编号
     * @param num   借书的数量
     * @return
     */
    public boolean borrowBook2(String stuNum,int bookId,int num){
        boolean flag = false;
        try {

            //1.向`records`表添加借书记录
            Connection connection = DBHelper.getConnectin();
            String sql1 = "insert into records(snum,bid,borrow_num,is_return,borrow_date) values(?,?,?,0,sysdate())";
            PreparedStatement preparedStatement = connection.prepareStatement(sql1);
            preparedStatement.setString(1,stuNum);
            preparedStatement.setInt(2,bookId);
            preparedStatement.setInt(3,num);
            int i = preparedStatement.executeUpdate();

            int k = 10/0;  //造异常:算术异常

            //2.修改`books`表中的库存
            Connection connection2 = DBHelper.getConnectin();
            String sql2 = "update books set book_stock=book_stock-? where book_id=?";
            PreparedStatement preparedStatement2 = connection2.prepareStatement(sql2);
            preparedStatement2.setInt(1,num);
            preparedStatement2.setInt(2,bookId);
            int j = preparedStatement2.executeUpdate();
            flag = i>0 && j>0;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            // 关闭连接
        }
        return flag;
    }
}

分析:

  • 借书业务由两个数据库操作完成,这两个操作要么同时成功,要么同时失败,构成一个数据库事务;

  • JDBC的DML操作默认是自定提交的,因此当第一个DML操作(添加借书记录)完成后,无论第二个操作(修改库存)是否成功,借书记录都会添加到数据库,而且是永久的

  • JDBC中该如何做事务管理呢?

10.2 JDBC事务管理
  1. 一个事务中的多个DML操作需要基于同一个数据库连接;

  2. 创建连接之后,设置事务手动提交(关闭自动提交);connection.setAutoCommit(false);

  3. 当当前事务中的所有DML操作完成之后手动提交;connection.commit();

  4. 当事务中的任何一个步骤出现异常,在catch代码块中执行事务回滚。connection.rollback();

package com.qfedu.jdbc.dao;

import com.qfedu.jdbc.utils.DBHelper;

import java.sql.*;

/**
 * @Descript
 @Author 小温
 */
public class BookDAO {

    /**
     * 借书:
     * @param stuNum 借书的学生学号
     * @param bookId 借书的图书编号
     * @param num   借书的数量
     * @return
     */
    public boolean borrowBook(String stuNum,int bookId,int num){
        boolean flag = false;
        Connection connection = null;
        PreparedStatement preparedStatement1 = null;
        PreparedStatement preparedStatement2 = null;
        try {
            connection = DBHelper.getConnectin();
            //设置使用当前Connection连接操作数据库自动提交关闭
            connection.setAutoCommit(false);

            //1.向`records`表添加借书记录
            String sql1 = "insert into records(snum,bid,borrow_num,is_return,borrow_date) values(?,?,?,0,sysdate())";
            preparedStatement1 = connection.prepareStatement(sql1);
            preparedStatement1.setString(1,stuNum);
            preparedStatement1.setInt(2,bookId);
            preparedStatement1.setInt(3,num);
            int i = preparedStatement1.executeUpdate();

            int k = 10/0;  //造异常:算术异常

            //2.修改`books`表中的库存
            String sql2 = "update books set book_stock=book_stock-? where book_id=?";
            preparedStatement2 = connection.prepareStatement(sql2);
            preparedStatement2.setInt(1,num);
            preparedStatement2.setInt(2,bookId);
            int j = preparedStatement2.executeUpdate();

            // 提交事务
            connection.commit();
            flag = i>0 && j>0;
        }catch (Exception e){
            try {
                //一旦事务执行过程中出现异常,执行回滚
                connection.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }finally {
            DBHelper.close(preparedStatement1,null);
            DBHelper.close(preparedStatement2,connection);
        }
        return flag;
    }
   
}
10.3 Service层的事务管理

DAO只负责数据库的操作,业务由service层进行管理

10.3.1 Service分层介绍
  • DAO负责特定的数据库操作

  • Servcie进行业务处理,Service业务处理过程如果需要数据库操作,则调用DAO完成

10.3.2 Service分层实现
  • 创建RecordDAO,完成insertRecord方法

package com.qfedu.jdbc.dao;

import com.qfedu.jdbc.utils.DBHelper;

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

/**
 * @Descript 完成对借书记录表的JDBC操作
 @Author 小温
 */
public class RecordDAO {

    /**
     *  添加借书记录
     * @param snum 借书的学生的学号
     * @param bid 借书的图书编号
     * @param num 借书的数量
     * @return 添加借书记录成功返回true,失败返回false
     */
    public boolean insertRecord(String snum,int bid,int num){
        boolean flag = false;
        Connection connection = null;
        PreparedStatement preparedStatement1 = null;
        try{
            //1.向`records`表添加借书记录
            connection = DBHelper.getConnectin();
            String sql1 = "insert into records(snum,bid,borrow_num,is_return,borrow_date) values(?,?,?,0,sysdate())";
            preparedStatement1 = connection.prepareStatement(sql1);
            preparedStatement1.setString(1,snum);
            preparedStatement1.setInt(2,bid);
            preparedStatement1.setInt(3,num);
            int i = preparedStatement1.executeUpdate();
            flag = i>0;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            DBHelper.close(preparedStatement1,connection);
        }
        return flag;
    }

}
  • BookDAO中定义updateStock修改库存方法

package com.qfedu.jdbc.dao;

import com.qfedu.jdbc.utils.DBHelper;

import java.sql.*;

/**
 * @Descript
  @Author 小温
 */
public class BookDAO {
    
    /**
     * 修改库存
     * @param bid 要修改库存的图书编号
     * @param num  book_stock=book_stock-num
     * @return
     */
    public boolean updateStock(int bid,int num){
        boolean flag = false;
        Connection connection = null;
        PreparedStatement preparedStatement2 = null;
        try{
            //2.修改`books`表中的库存
            connection = DBHelper.getConnectin();
            String sql2 = "update books set book_stock=book_stock-? where book_id=?";
            preparedStatement2 = connection.prepareStatement(sql2);
            preparedStatement2.setInt(1,num);
            preparedStatement2.setInt(2,bid);
            int j = preparedStatement2.executeUpdate();
            flag = j>0;
        }catch (SQLException e){
            e.printStackTrace();
        }finally {
            DBHelper.close(preparedStatement2,connection);
        }
        return flag;
    }

}
  • 创建BookService完成借书业务

package com.qfedu.jdbc.service;

import com.qfedu.jdbc.dao.BookDAO;
import com.qfedu.jdbc.dao.RecordDAO;

/**
 * @Descript 图书管理的业务类
 @Author 小温
 */
public class BookService {

    private RecordDAO recordDAO = new RecordDAO();
    private BookDAO bookDAO = new BookDAO();

    /**
     * 借书业务,包含两个步骤,这两个步骤要么同时成功,要么同时失败——事务
     */
    public boolean borrowBook(String stuNum,int bookId,int num){
        //1.添加借书记录
        boolean b1 = recordDAO.insertRecord(stuNum, bookId, num);
        //2.修改图书库存
        boolean b2 = bookDAO.updateStock(bookId, num);
        // 结果处理
        boolean r = b1 && b2;
        return r;
    }

}
10.3.3 Service层的事务管理

Servcie层事务中多个数据库的DML操作是相互独立的,如何保证所有DML要么同时成功,要么同时失败呢?

事务管理要满足以下条件:

  • 多个DML操作需使用同一个数据库连接

  • 第一个DML操作之前设置事务手动提交

  • 所有DML操作执行完成之后提交事务

  • 出现异常则进行事务回滚

如何让Service事务中的多个DML使用同一个数据库连接?

  • 在Service获取连接对象,将连接对象传递到DAO中

    分析:DAO类中的Connection对象需要通过Service传递给进来,这种对象传递本来也无可厚非,但是当我们通过面向接口开发时(面向接口,是为了能够灵活的定义实现类),容易造成接口的冗余(接口污染)

  • 使用ThreadLocal容器,实现多个DML操作使用相同的连接

    存储Connection的容器可以使用List集合

    • 使用List集合做容器,在多线程并发编程中会出现资源竞争问题——多个并发的线程使用的是同一个数据库连接对象(我们的要求是同一个事务中使用同一个连接,而并非多个线程共享连接)

    • 为了解决并发编程的连接对象共享问题,我们可以使用ThreadLocal作为数据库连接对象的容器

    package com.qfedu.jdbc.utils;
    
    import java.sql.*;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @Descript
     @Author 小温
     */
    public class DBHelper {
        //1.定义数据库连接信息
        private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
        private static final String URL = "jdbc:mysql://localhost:3306/db_test3?characterEncoding=utf8";
        private static final String USERNAME = "root";
        private static final String PASSWORD = "@QFedu123";
    
        //1??定义ThreadLocal容器
        private static final ThreadLocal<Connection> local = new ThreadLocal<>();
    
        //2.静态初始化块注册驱动
        static{
            try {
                Class.forName(DRIVER);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    
        //3.创建数据库连接
        public static Connection getConnectin(){
            // 2??从ThreadLocal容器中获取连接
            Connection connection = local.get();
            try {
                if(connection == null){
                    //3??如果容器中没有连接,则创建连接,并将创建的连接存放到容器
                    connection = DriverManager.getConnection(URL,USERNAME,PASSWORD);
                    local.set(connection);
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return connection;
        }
    
        //4. 4??关闭连接
        // 如果使用ThreadLocal存储数据库连接,关闭连接时同时要将Connection对象从ThreadLocal中移除
        public static void closeConnection(){
            // 获取到当前线程使用的数据库连接对象
            Connection connection = local.get();
            try {
                if(connection !=null && !connection.isClosed()){
                    connection.close();
                }
                // 将关闭后的连接对象从ThreadLocal中移除
                local.remove();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    
        public static void closeStatement(Statement statement){
            closeStatement(null,statement);
        }
    
        public static void closeStatement(ResultSet resultSet, Statement statement){
            try {
                if(resultSet!=null && !resultSet.isClosed()){
                    resultSet.close();
                }
                if(statement!=null && !statement.isClosed()){
                    statement.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    
    }

十一、数据库连接池

11.1 什么是数据库连接池?

如果每个JDBC操作需要数据库连接都重新创建,使用完成之后都销毁,我们的JVM会因为频繁的创建、销毁连接而占用额外的系统资源。

数据库连接本质上是可被重用的资源(当一个JDBC操作完成之后,其创建的连接是可以被其他JDBC操作使用的),基于这个特性:

  • 我们可以创建一个存放数据库连接的容器(连接池),连接池是有最大容量的

  • 当我们要进行JDBC操作时,直接从这个容器中获取连接

    • 如果容器中没有空闲的连接且连接池中连接的个数没有达到最大值,则创建新的数据库连接存入连接池并给这个操作使用,使用完成之后无需关闭连接直接归还这个容器中即可;

    • 如果容器中没有空闲的连接且连接池中连接的个数达到最大值,当前操作就会进行等待,等待连接池中的某个连接被归还,归还之后再使用;

    • 如果容器中有空闲连接,则无需创建新的连接,直接从容器中获取这个空闲连接进行使用;

连接池:存放数据库连接对象的容器

连接池作用:对数据库连接进行管理,减少因重复创建、销毁连接导致的系统开销

11.2 常用连接池

我们可以编程实现:创建一个数组、集合来存放数据库连接;

目前市面上已经有多种实现的数据库连接池了,我们无需再手动实现,只需引用对应的数据库连接池产品,即可在我们自己的Java应用中使用连接池(站在巨人的肩上)

功能dbcpdruidc3p0HikariCP
是否支持PSCache
监控jmxjmx/log/httpjmx,logjmx
扩展性
sql拦截及解析支持
代码简单中等复杂简单
特点依赖于common-pool阿里开源,功能全面历史久远,代码逻辑复杂,且不易维护优化力度大,功能简单,起源于boneCP
连接池管理LinkedBlockingDeque数组ThreadLocal CopyOnWriteArrayList
  • 基于连接池的性能、使用的便捷性、连接监控等多方面综合情况,druid是目前企业应用中使用最广泛的

  • Hikari在SpringBoot中默认集成,性能是诸多竞品中最好的

11.3 使用Druid连接池
11.3.1 创建Java应用
  • 创建Java工程

  • 添加驱动jar文件

11.3.2 创建连接池属性配置
  • 在src中创建package:com.qfedu.jdbc.utils

  • com.qfedu.jdbc.utils中创建druid.properties文件

  • 配置druid连接池的实行

    # 数据库连接信息
    driverClassName=com.mysql.cj.jdbc.Driver
    url=jdbc:mysql://localhost:3306/db_test3?characterEncoding=utf8
    username=root
    password=@QFedu123
    
    # 连接池属性
    # 连接池的初始化连接数<创建数据库连接池时默认初始化的连接的个数>
    initialSize=10
    # 连接池的最大连接数
    maxActive=50
    # 最小空闲连接数(当数据库连接使用率很低时,连接池中的连接会被释放一部分)
    minIdle=5
    # 超时等待时间(单位:ms)
    maxWait=30000
11.3.3 创建连接池工具类
  • 下载并导入druid的jar文件druid-1.2.8.jar

  • com.qfedu.jdbc.utils创建DruidUtils工具类(工具类与属性文件druid.properties在同目录下)

    package com.qfedu.jdbc.utils;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import com.alibaba.druid.pool.DruidDataSourceFactory;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.util.Properties;
    
    /**
     * @Descript Druid数据库连接池工具类
     @Author 小温
     */
    public class DruidUtils {
    
        //1.定义DruidDataSource对象:表示Druid数据库连接池(数据源)
        private static DruidDataSource druidDataSource;
    
        //2.静态代码块初始化定义DruidDataSource对象
        static{
            try {
                //读取druid.properties文件中配置的属性
                InputStream is = DruidUtils.class.getResourceAsStream("druid.properties");
                Properties properties = new Properties();
                properties.load(is);
                //使用属性文件初始化DruidDataSource对象
                druidDataSource = 
                    (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        //3.创建静态方法,从连接池对象中获取连接
        public static Connection getConnection(){
            Connection connection = null;
            try {
                connection =  druidDataSource.getConnection();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return connection;
        }
        
    }

十二、通用JDBC操作封装

在DAO层的JDBC操作中,对数据的表增、删、改、查操作存在代码的冗余,我们可以对其公共部分进行封装,实现代码的复用。

12.1 DML操作封装

对于数据库的DML操作:

  • 参数不同

  • SQL通过

  • SQL参数复制不同

  • 通用的DML操作方法

package com.qfedu.jdbc.utils;

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

/**
 * @Descript 公共DAO,用于封装公共的JDBC操作
 @Author 小温
 */
public class CommonDAO {

    /**
     * 公共DML操作
     * @param sql 执行的SQL指令
     * @return
     */
    /**
     * sql : delete from students where stu_num=?
     * args: snum
     *
     * sql : insert into students(stu_num,stu_name,stu_gender,stu_age) values(?,?,?,?)
     * args: 1009  Lucy  女   18
     */
    public boolean update(String sql, Object... args){
        boolean b = false;
        try{
            Connection connection = DruidUtils.getConnection();
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            for (int i = 0; i < args.length ; i++) {
                preparedStatement.setObject(i+1,args[i]);
            }
            int i = preparedStatement.executeUpdate();
            b = i>0;
        }catch (Exception e){
            e.printStackTrace();
        }
        return b;
    }

}
12.2 DQL操作封装
使用泛型设置通用查询方法的返回类型
package com.qfedu.jdbc.utils;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @Descript 用于定义结果集映射的接口
 @Author 小温
 */
public interface RowMapper<T> {

    public T getRow(ResultSet resultSet) throws SQLException;

}
package com.qfedu.jdbc.utils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

/**
 * @Descript 公共DAO,用于封装公共的JDBC操作
 @Author 小温
 */
public class CommonDAO<T> {

    /**
     * 公共DML操作
     */
    public boolean update(String sql, Object... args){
        boolean b = false;
        try{
            Connection connection = DruidUtils.getConnection();
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            for (int i = 0; i < args.length ; i++) {
                preparedStatement.setObject(i+1,args[i]);
            }
            int i = preparedStatement.executeUpdate();
            b = i>0;
        }catch (Exception e){
            e.printStackTrace();
        }
        return b;
    }

    /**
     * 查询
     */
    public List<T> select(String sql, RowMapper<T> rowMapper,Object...args){
        List<T> list = new ArrayList<>();
        try{
            Connection connection = DruidUtils.getConnection();
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            for (int i = 0; i <args.length ; i++) {
                preparedStatement.setObject(i+1,args[i]);
            }
            ResultSet resultSet = preparedStatement.executeQuery();
            while(resultSet.next()){
                // 从查询结果中取出一条记录(多个值),封装到一个实体类对象中
                //  getRow就是方法调用者在调用方法时,传递进来的用于映射查询结果的方法
               T t =  rowMapper.getRow(resultSet);
               list.add(t);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return list;
    }

}
12.3 基于通用JDBC操作的案例

图书信息进行JDBC操作

  • 创建图书实体类Book

  • 创建BookDAO

12.3.1 创建实体类
/**
 * @Descript 图书信息实体类
 @Author 小温
 */
public class Book {
    
    private int bookId;
    private String bookName;
    private String bookAuthor;
    private double bookPrice;
    private int bookStock;
    private String bookDesc;
    
    //无参构造器
    //全参构造器
    //toString
    //get和set
}
12.3.2 创建DAO类

DAO中的操作都是调用CommonDAO实现的

package com.qfedu.jdbc.dao;

import com.qfedu.jdbc.dto.Book;
import com.qfedu.jdbc.utils.CommonDAO;
import com.qfedu.jdbc.utils.RowMapper;

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

/**
 * @Descript 对图书信息表进行CRUD操作
 @Author 小温
 */
public class BookDAO {

    private CommonDAO<Book> commonDAO = new CommonDAO<>();

    public  boolean insertBook(Book book){
        String sql = "insert into books(book_name,book_author,book_price,book_stock,book_desc) values(?,?,?,?,?)";
        boolean b = commonDAO.update(sql, book.getBookName(), book.getBookAuthor(), book.getBookPrice(), book.getBookStock(), book.getBookDesc());
        return b;
    }

    public boolean deleteBook(int bookId){
        String sql = "delete from books where book_id=?";
        boolean b = commonDAO.update(sql, bookId);
        return b;
    }

    public boolean updateBook(Book book){
        String sql="update books set book_name=?,book_author=?,book_price=?,book_stock=?,book_desc=? where book_id=?";
        boolean b = commonDAO.update(sql, book.getBookName(), book.getBookAuthor(), book.getBookPrice(), book.getBookStock(), book.getBookDesc(), book.getBookId());
        return b;
    }

    public Book queryBook(int bookId){
        String sql = "select book_id,book_name,book_author,book_price,book_stock,book_desc from books where book_id=?";
        RowMapper<Book> bookRowMapper = new RowMapper<Book>(){
            public Book getRow(ResultSet resultSet) throws SQLException {
                int bid = resultSet.getInt("book_id");
                String bookName = resultSet.getString("book_name");
                String bookAuthor = resultSet.getString("book_author");
                double bookPrice = resultSet.getDouble("book_price");
                int bookStock = resultSet.getInt("book_stock");
                String bookDesc = resultSet.getString("book_desc");
                return new Book(bid,bookName,bookAuthor,bookPrice,bookStock,bookDesc);
            }
        };
        List<Book> list = commonDAO.select(sql, bookRowMapper, bookId);
        return list.size()>0?list.get(0):null;
    }

    public List<Book> listBooks(){
        String sql = "select book_id,book_name,book_author,book_price,book_stock,book_desc from books";
        List<Book> list = commonDAO.select(sql, resultSet -> {
            int bid = resultSet.getInt("book_id");
            String bookName = resultSet.getString("book_name");
            String bookAuthor = resultSet.getString("book_author");
            double bookPrice = resultSet.getDouble("book_price");
            int bookStock = resultSet.getInt("book_stock");
            String bookDesc = resultSet.getString("book_desc");
            return new Book(bid, bookName, bookAuthor, bookPrice, bookStock, bookDesc);
        });
        return list;
    }

}

十三、Apache DBUtils

13.1 DBUtils介绍

Commons DBUtils是Apache组织提供的一个针对JDBC进行简单封装的开源工具类库,使用DBUtils可以极大简化JDBC应用程序开发,同时不会影响数据库访问的性能。

DBUtils是Java编程中数据库访问的轻巧、使用的工具类库

  • 提供对数据表通用的DML操作

  • 提供对数据表通用的DQL操作(可以把结果封装成对象、集合等类型)

DBUtils工具类库中的核心类:

  • QueryRunner,用于执行SQL指令(相当于我们自己封装的CommonDAO类)

    • update() , 用于执行DML指令

    • query(), 用于执行DQL指令

  • ResultSetHandler接口,结果集处理器,用于处理查询结果(相当于我们自己的封装的RowMapper接口)

    • BeanHandler实现类:当查询语句返回一条结果时,使用BeanHandler将结果转换成一个对象

    • BeanListHandler实现类:当查询语句返回多个结果时,使用BeanListHandler将结果转换成一个集合

    • ScalarHandler实现类:当查询结果返回一个值时,使用ScalarHandler将这个值转换成Java变量类型

13.2 DBUtils的使用准备
  1. 新建Java工程

  2. 添加依赖:

    • mysql-connector-java-8.0.26.jar

    • druid-1.2.8.jarApache DBUtils建立数据库连接是依赖连接池数据源的,因此我们需要使用连接池

    • commons-dbutils-1.7.jar

  3. 配置druid的属性文件

    创建com.qfedu.jdbc.utils

    在包中创建druid.properties文件,配置如下

    # 数据库连接信息
    driverClassName=com.mysql.cj.jdbc.Driver
    url=jdbc:mysql://localhost:3306/db_test3?characterEncoding=utf8
    username=root
    password=@QFedu123
    
    # 连接池属性
    # 连接池的初始化连接数<创建数据库连接池时默认初始化的连接的个数>
    initialSize=10
    # 连接池的最大连接数
    maxActive=50
    # 最小空闲连接数(当数据库连接使用率很低时,连接池中的连接会被释放一部分)
    minIdle=5
    # 超时等待时间(单位:ms)
    maxWait=30000
  4. 创建Druid连接池工具类

    com.qfedu.jdbc.utils包创建DruidUtils工具类

    package com.qfedu.jdbc.utils;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import com.alibaba.druid.pool.DruidDataSourceFactory;
    
    import javax.sql.DataSource;
    import java.io.IOException;
    import java.io.InputStream;
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.util.Properties;
    
    /**
     * @Descript
     @Author 小温
     */
    public class DruidUtils {
    
        private static DruidDataSource druidDataSource;
    
        static{
            try {
                InputStream is = DruidUtils.class.getResourceAsStream("druid.properties");
                Properties properties = new Properties();
                properties.load(is);
                druidDataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 获取数据库连接池数据源对象
         * @return
         */
        public static DataSource getDataSource(){
            return druidDataSource;
        }
    
        public static Connection getConnection(){
            Connection connection = null;
            try {
                connection = druidDataSource.getConnection();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return connection;
        }
    
    }

13.3 DBUtils使用

完成图书信息的数据库操作

  • 创建实体类

/**
 * @Descript 图书信息实体类
 @Author 小温
 */
public class Book {
    
    private int bookId;
    private String bookName;
    private String bookAuthor;
    private double bookPrice;
    private int bookStock;
    private String bookDesc;
    
}
13.3.1 添加操作
public int insertBook(Book book){
    int i= 0;
    try {
        //1.编写SQL
        String sql = "insert into books(book_name,book_author,book_price,book_stock,book_desc) values(?,?,?,?,?)";
        //2.准备参数
        Object[] params = {book.getBookName(),book.getBookAuthor(),book.getBookPrice(),book.getBookStock(),book.getBookDesc()};
        //3.调用commons-dbutils中的QueryRunner执行SQL
        QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
        i = queryRunner.update(sql, params);
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return i;
}
13.3.2 删除操作
public int deleteBook(int bookId){
    int i = 0;
    try {
        String sql = "delete from books where book_id=?";
        QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
        i = queryRunner.update(sql,bookId);
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return i;
}
13.3.3 修改操作
public int updateBook(Book book){
    int i=0;
    try {
        String sql = "update books set book_name=?,book_author=?,book_price=?,book_stock=?,book_desc=? where book_id=?";
        Object[] params = {book.getBookName(),book.getBookAuthor(),book.getBookPrice(),book.getBookStock(),book.getBookDesc(),book.getBookId()};
        QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
        i = queryRunner.update(sql, params);
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return i;
}
13.3.4 查询操作

查询一条记录

public Book queryBook(int bookId){
    Book book = null;
    try {
        String sql = "select book_id bookId,book_name bookName,book_author bookAuthor,book_price bookPrice,book_stock bookStock,book_desc bookDesc from books where book_id=?";
        QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
        //1.对于查询操作,我们需要通过QueryRunner对象调用query方法来执行
        //2.所有的query方法都需要一个ResultSetHandler的参数,通过此参数指定query方法的返回类型
        //  如果SQL指令执行之后返回的是一行记录,我们通过BeanHandler指定查询结果封装的实体类类型
        //  要求:查询结果集的字段名必须与指定的实体类的属性名匹配
        //     方案1:创建实体类的时候,实体类中属性的名字与数据表中的列名相同
        //     方案2:查询语句字段取别名,让字段别名与实体类属性名一致
        book = queryRunner.query(sql, new BeanHandler<Book>(Book.class), bookId);
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return book;
}

自定义结果集处理 自定义ResultSetHandler

public Book queryBook2(int bookId){
    Book book = null;
    try {
        String sql = "select book_id,book_name,book_author,book_price,book_stock,book_desc from books where book_id=?";
        QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
        //1.对于查询操作,我们需要通过QueryRunner对象调用query方法来执行
        //2.所有的query方法都需要一个ResultSetHandler的参数,通过此参数指定query方法的返回类型
        //  如果SQL指令执行之后返回的是一行记录,我们通过BeanHandler指定查询结果封装的实体类类型
        //  要求:查询结果集的字段名必须与指定的实体类的属性名匹配
        //     方案3:自定义ResultSetHandler结果处理
        ResultSetHandler<Book> resultSetHandler = new ResultSetHandler<Book>() {
            @Override
            public Book handle(ResultSet resultSet) throws SQLException {
                Book book = null;
                if(resultSet.next()) {
                    int id = resultSet.getInt("book_id");
                    String bookName = resultSet.getString("book_name");
                    String bookAuthor = resultSet.getString("book_author");
                    double bookPrice = resultSet.getDouble("book_price");
                    int bookStock = resultSet.getInt("book_stock");
                    String bookDesc = resultSet.getString("book_desc");
                    book = new Book(id,bookName,bookAuthor,bookPrice,bookStock,bookDesc);
                }
                return book;
            }
        };
        book = queryRunner.query(sql, resultSetHandler, bookId);
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return book;
}

查询多条记录

public List<Book> listBooks(){
    List<Book> bookList = null;
    try {
        String sql = "select  book_id bookId,book_name bookName,book_author bookAuthor,book_price bookPrice,book_stock bookStock,book_desc bookDesc  from books";
        QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
        //如果SQL指令执行之后返回的是多行记录,我们通过BeanListHandler指定查询结果封装的实体类的集合类型
        bookList = queryRunner.query(sql, new BeanListHandler<Book>(Book.class));
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return bookList;
}

查询一个值

例如在做分页的时候,我们需要查询数据的总记录数

?

public long getCount(){
    long count = 0;
    String sql = "select count(1) from books";
    QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
    // 如果SQL指令执行之后返回的是一个值时,我们通过ScalarHandler指定返回类型
    // QueryRunner在处理统计操作时,是以long类型进行操作的,因此不能直接转成Integer
    // 如果我们确定这个值在int范围内,我们可以在得到long类型之后进行强转,建议使用long处理
    ScalarHandler<Long> scalarHandler = new ScalarHandler<Long>();
    try {
        count = queryRunner.query(sql, scalarHandler);
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return count;
}
文章来源:https://blog.csdn.net/qq_53872712/article/details/135431906
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。