JDBC和数据库连接池,带源码和工具文件(在源码的libs文件夹里)!!!!

JDBC和数据库连接池

1、JDBC概述

(1)基本介绍

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

在这里插入图片描述

(2)模拟JDBC

先规定一个JDBCInterface接口。

package 模拟JDBC;

/**
 * @author D
 * @version 1.0
 * @project JDBC
 * @description 我们规定的JDBC接口(方法)
 * @date 2024/5/14 15:11:46
 */
public interface JDBCInterface {
    // 连接
    public Object getConnection();
    //crud -> 增删改查
    public void crud();
    //断开连接
    public void close();
}

写一个MySQLJDBCImpl类用来完成MySQL实现JDBCInterface接口。

package 模拟JDBC;

/**
 * @author D
 * @version 1.0
 * @project JDBC
 * @description MySQL实现JDBCInterface接口
 * @date 2024/5/14 15:15:10
 */
public class MySQLJDBCImpl implements JDBCInterface{
    @Override
    public Object getConnection() {
        System.out.println("与MySQL数据库建立连接");
        return null;
    }

    @Override
    public void crud() {
        System.out.println("实现了增删改查功能");
    }

    @Override
    public void close() {
        System.out.println("与MySQL数据库断开连接");
    }
}

写一个TestJDBC类用来测试模拟的JDBC。

package 模拟JDBC;

/**
 * @author D
 * @version 1.0
 * @project JDBC
 * @description 测试模拟的JDBC
 * @date 2024/5/14 15:18:31
 */
public class TestJDBC {
    public static void main(String[] args) {
        JDBCInterface jdbcInterface=new MySQLJDBCImpl();
        jdbcInterface.getConnection();//通过接口来调用方法(动态绑定)
        jdbcInterface.crud();
        jdbcInterface.close();
    }
}

(3)JDBC带来的好处

在这里插入图片描述

如果Java直接访问数据库后期将会维护困难,所以Java厂商就优化了此问题

在这里插入图片描述

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

(4)JDBC API

在这里插入图片描述

2、JDBC快速入门

(1)JDBC程序编写步骤

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

(2)JDBC第一个程序

通过jdbc对表actor进行添加,删除和修改操作

先在要创建表的数据库中创建actor表:

-- 这里我选择我自己的数据库djt_db01
CREATE table actor(
	id INT PRIMARY KEY auto_increment,
	`name` VARCHAR(32) NOT NULL DEFAULT '',
	sex CHAR(1) NOT NULL DEFAULT '女',
	borndate datetime,
	phone VARCHAR(12));

然后就用idea软件编写第一个jdbc程序!

创建一个类jdbc01:(我的MySQL是8.0版本的我会将相应的jar包放在资源里,以供使用)

package jdbc;

import com.mysql.cj.jdbc.Driver;

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

/**
 * @author D
 * @version 1.0
 * @project JDBC
 * @description jdbc快速入门
 * @date 2024/5/14 16:07:59
 */
public class jdbc01 {
    public static void main(String[] args) throws SQLException {
        //前提:引入mysql.jar包,这里需要注意的是根据自己装的MySQL版本引入对应版本的jar包。下面我会介绍如何引入jar包
        //1、注册驱动
        Driver driver = new Driver();

        //2、得到连接
        //(1)jdbc:mysql:// 规定好表示协议,通过jdbc的方式连接MySQL
        //(2)localhost 主机,可以是ip地址
        //(3)3306 表示MySQL监听的端口
        //(4)djt_db01 连接到MySQL dbms 的哪个数据库
        //(5)MySQL的连接本质其实就是前面学过的socket连接
        String url="jdbc:mysql://localhost:3306/djt_db01";
        //将用户名的密码放入到Properties对象
        //说明:user和password 是规定好的,后面的值根据自己的用户密码进行修改
        Properties properties = new Properties();
        properties.setProperty("user","root");//用户
        properties.setProperty("password","123456");//密码
        Connection connect = driver.connect(url, properties);

        //3、执行sql
        String sql="INSERT INTO actor VALUES(NULL,'Ghost-D','男','2001-02-25','120')";
        //statement 用于执行静态SQL语句并返回其生成的结果的对象
        Statement statement = connect.createStatement();
        int rows = statement.executeUpdate(sql);//如果是dml语句,返回的就是影响的行数
        System.out.println(rows>0?"成功":"失败");
        //4、关闭连接资源
        statement.close();
        connect.close();
    }
}

点击运行会显示成功:

在这里插入图片描述

接下来,进入MySQL查看actor表看看刚刚插入的数据是否存在:

在这里插入图片描述

拓展:引入jar包的步骤

第一步:在项目包下创建一个目录libs,将对应的MySQL的jar包复制到目录libs里。

在这里插入图片描述

在这里插入图片描述

右击刚刚复制的jar包点击添加为库…,点击确定,这样就把MySQL的jar包导入成功了。

在这里插入图片描述

在这里插入图片描述

3、获取数据库连接的5种方式

(1)方式一

public void connection01() throws SQLException {
        //方式一:获取Driver实现类对象
        Driver driver = new Driver();
        String url="jdbc:mysql://localhost:3306/jdbc_db";
        Properties info = new Properties();
        info.setProperty("user","root");
        info.setProperty("password","123456");
        Connection connect = driver.connect(url, info);
        System.out.println("方式一:"+connect);
        //方式一:com.mysql.cj.jdbc.ConnectionImpl@56a6d5a6
    }

(2)方式二

public void connection02() throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
        //通过反射加载Driver类,动态加载,更加的灵活,减少依赖性
        Class<?> cls = Class.forName("com.mysql.cj.jdbc.Driver");
        Driver driver =(Driver) cls.newInstance();
        String url="jdbc:mysql://localhost:3306/jdbc_db";
        Properties info = new Properties();
        info.setProperty("user","root");
        info.setProperty("password","123456");
        Connection connect = driver.connect(url, info);
        System.out.println("方式二:"+connect);
        //方式二:com.mysql.cj.jdbc.ConnectionImpl@56a6d5a6
    }

(3)方式三

public void connection03() throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
        //通过DriverManager替换Driver
        Class<?> cls = Class.forName("com.mysql.cj.jdbc.Driver");
        Driver driver = (Driver) cls.newInstance();
        String url="jdbc:mysql://localhost:3306/jdbc_db";
        String user="root";
        String password="123456";

        DriverManager.registerDriver(driver);//注册Driver驱动
        Connection connection = DriverManager.getConnection(url, user, password);
        System.out.println("方式三:"+connection);
        //方式三:com.mysql.cj.jdbc.ConnectionImpl@18ce0030
    }

(4)方式四

public void connection04() throws ClassNotFoundException, SQLException {
        //使用Class.forName 自动完成注册驱动,简化代码--> 分析源码
        //这种方法是使用最多的,强烈推荐!!!!!!
        Class.forName("com.mysql.cj.jdbc.Driver");
        /* Driver源码分析
        //
        // Source code recreated from a .class file by IntelliJ IDEA
        // (powered by FernFlower decompiler)
        //

        package com.mysql.cj.jdbc;

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

        public class Driver extends NonRegisteringDriver implements java.sql.Driver {
            public Driver() throws SQLException {
            }

            static {
                try {
                    DriverManager.registerDriver(new Driver());其实在静态代码块就已经注册Driver驱动了
                } catch (SQLException var1) {
                    throw new RuntimeException("Can't register driver!");
                }
            }
        }
         */
        String url="jdbc:mysql://localhost:3306/jdbc_db";
        String user="root";
        String password="123456";
        Connection connection = DriverManager.getConnection(url, user, password);
        System.out.println("方式四:"+connection);
        //方式四:com.mysql.cj.jdbc.ConnectionImpl@56a6d5a6
    }

(5)方式五

public void connection05() throws IOException, SQLException, ClassNotFoundException {
        //使用配置文件,连接数据库更灵活
        /*
        注意:
            1、MySQL驱动5.1.6可以无需Class.forName("com.mysql.cj.jdbc.Driver");
            2、从jdk1.5以后使用了jdbc4,不在需要显式调用Class.forName()注册驱动而是自动调用驱动
            jar包下META-INF\services\java.sql.Driver文本中的类名称去注册
            3、建议还是写上Class.forName("com.mysql.cj.jdbc.Driver"),更加明确
         */
        Class.forName("com.mysql.cj.jdbc.Driver");
        //得到相应信息
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\mysql.properties"));
        String url = properties.getProperty("url");
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        Connection connection = DriverManager.getConnection(url, user, password);
        System.out.println("方式五:"+connection);
        //方式五:com.mysql.cj.jdbc.ConnectionImpl@56a6d5a6
    }

4、ResultSet[结果集]

(1)基本介绍

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

(2)应用实例

package jdbc.resultset_;

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

/**
 * @author D
 * @version 1.0
 * @project JDBC
 * @description 演示select语句返回ResultSet,并取出结果
 * @date 2024/5/15 17:20:05
 */
public class ResultSet_ {
    public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
        //获取相应信息,这里注意url要正确
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\mysql.properties"));
        String url = properties.getProperty("url");
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection connection = DriverManager.getConnection(url, user, password);
        Statement statement = connection.createStatement();
        //组织sql语句
        String sql="select id,name,borndate from actor";
        //执行给定的sql语句,该语句返回单个的ResultSet对象
        ResultSet resultSet = statement.executeQuery(sql);
        /*
        +----+---------+---------------------+
        | id | name    | borndate            |
        +----+---------+---------------------+
        |  1 | Ghost-D | 2001-02-25 00:00:00 |
        +----+---------+---------------------+
         */
        //使用while循环取出数据
        while (resultSet.next()){//让光标向后移动,如果没有更多行,则返回false
            int id = resultSet.getInt(1);//获取改行第1列
            String name = resultSet.getString(2);//获取改行第2列
            Date date = resultSet.getDate(3);//获取改行第3列
            System.out.println(id+"\t"+name+"\t"+date);
        }
        //关闭连接
        resultSet.close();
        statement.close();
        connection.close();
    }
}

5、Statement

(1)基本介绍

  1. statement对象,用于执行静态SQL语句并返回其生成的结果的对象
  2. 在建立连接后,需要对数据库进行访问,执行命名或者是SQL语句,可以通过
  • Statement[存在SQL注入]
  • PreparedStatement[预处理]
  • CallableStatement[存储过程]
  1. Statement对象执行SQL语句,存在SQL注入风险
  2. SQL注入是利用某些系统没有对用户输入的数据进行充分的检查,而用户输入数据中注入非法的SQL语句段或命令,恶意攻击数据库。
  3. 要防范SQL注入,只需要PreparedStatement(从Statement扩展而来)取代Statement就可以了。
-- 演示SQL注入
CREATE TABLE admin ( -- 管理员表
NAME VARCHAR(32) NOT NULL UNIQUE, pwd VARCHAR(32) NOT NULL DEFAULT '') CHARACTER SET utf8;
-- 添加数据
INSERT INTO admin VALUES('tom','123456');

-- 查找某个管理员是否存在
SELECT * FROM admin WHERE name='tom' AND pwd='123456';
-- SQL注入
-- 输入用户名 为 1' or
-- 输入万能密码 为 or '1'= '1
SELECT * FROM admin WHERE name='1' OR' AND pwd='OR '1'='1';

(2)应用实例

package jdbc.statement_;

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

/**
 * @author D
 * @version 1.0
 * @project JDBC
 * @description 演示Statement的注入问题
 * @date 2024/5/16 09:59:05
 */
public class Statement_ {
    public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
        /*
        -- SQL注入
        -- 输入用户名 为 1' or
        -- 输入万能密码 为 or '1'= '1
         */
        Scanner input = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String admin_name=input.nextLine();//这里用nextLine是因为防止输入到空格或者' 就退出了
        System.out.println("请输入密码:");
        String admin_pwd=input.nextLine();

        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\mysql.properties"));
        //获取相应信息
        String url = properties.getProperty("url");
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");

        Class.forName("com.mysql.cj.jdbc.Driver");//最好写上
        //得到连接
        Connection connection = DriverManager.getConnection(url, user, password);
        //得到Statement
        Statement statement = connection.createStatement();
        //组织SQL语句
        String sql="select name,pwd from admin where name='"+admin_name+"' and pwd='"+admin_pwd+"'";
        ResultSet resultSet = statement.executeQuery(sql);
        if (resultSet.next()){
            System.out.println("恭喜,登录成功~");
        }else {
            System.out.println("对不起,登录失败~");
        }
        //关闭连接
        resultSet.close();
        statement.close();
        connection.close();
    }
}

6、PreparedStatement

(1)基本介绍

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

(2)预处理好处

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

(3)应用案例

/**
 * 1、创建admin表 在MySQL中创建
 * 2、使用PreparedStatement添加5条数据
 * 3、修改tom的记录,将name改成king
 * 4、删除一条记录
 * 5、查询全部记录,并显示在控制台
 */
主方法:
static Scanner input = new Scanner(System.in);
public static void main(String[] args) throws SQLException, IOException, ClassNotFoundException {
//添加5条记录
//        for (int i = 0; i < 5; i++) {
//            insert();
//        }
    //修改tom的记录,将name改成king
//        update();
    //删除一条记录
//        delete();
    //查询全部记录,并显示在控制台
    select();
}
使用PreparedStatement添加5条数据
public static void insert() throws IOException, ClassNotFoundException, SQLException {
        System.out.println("请输入添加的用户名:");
        String admin_name=input.nextLine();
        System.out.println("请输入用户名密码:");
        String admin_pwd=input.nextLine();

        //获取mysql账户信息
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\mysql.properties"));
        String url = properties.getProperty("url");
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");

        Class.forName("com.mysql.cj.jdbc.Driver");//最好写上(加载驱动)
        //获取连接
        Connection connection = DriverManager.getConnection(url, user, password);
        //写sql语句
        String sql="insert into admin values (?,?)";
        //得到preparedStatement,并完成赋值
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
    	//给?赋值
        preparedStatement.setString(1,admin_name);
        preparedStatement.setString(2,admin_pwd);

        //执行添加
        int rows = preparedStatement.executeUpdate();
        System.out.println(rows>0?"添加成功":"添加失败");
        //关闭连接
        preparedStatement.close();
        connection.close();
    }
修改tom的记录,将name改成king
public static void update() throws IOException, ClassNotFoundException, SQLException {
        System.out.println("请输入要修改的用户名:");
        String admin_beforename=input.nextLine();
        System.out.println("请输入修改后的用户名:");
        String admin_aftername=input.nextLine();

        //获取mysql账户信息
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\mysql.properties"));
        String url = properties.getProperty("url");
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");

        Class.forName("com.mysql.cj.jdbc.Driver");//最好写上(加载驱动)
        //获取连接
        Connection connection = DriverManager.getConnection(url, user, password);
        //写sql语句
        String sql="update admin set name=? where name=?";
        //得到preparedStatement,并完成赋值
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setString(1,admin_aftername);
        preparedStatement.setString(2,admin_beforename);

        //执行修改
        int rows = preparedStatement.executeUpdate();
        System.out.println(rows>0?"修改成功":"修改失败");
        //关闭连接
        preparedStatement.close();
        connection.close();
    }
删除记录
public static void delete() throws IOException, ClassNotFoundException, SQLException {
        System.out.println("请输入要删除的用户名:");
        String admin_name=input.nextLine();

        //获取mysql账户信息
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\mysql.properties"));
        String url = properties.getProperty("url");
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");

        Class.forName("com.mysql.cj.jdbc.Driver");//最好写上(加载驱动)
        //获取连接
        Connection connection = DriverManager.getConnection(url, user, password);
        //写sql语句
        String sql="delete from admin where name=?";
        //得到preparedStatement,并完成赋值
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setString(1,admin_name);

        //执行删除
        int rows = preparedStatement.executeUpdate();
        System.out.println(rows>0?"删除成功":"删除失败");
        //关闭连接
        preparedStatement.close();
        connection.close();
    }
查询记录
public static void select() throws IOException, ClassNotFoundException, SQLException {

        //获取mysql账户信息
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\mysql.properties"));
        String url = properties.getProperty("url");
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");

        Class.forName("com.mysql.cj.jdbc.Driver");//最好写上(加载驱动)
        //获取连接
        Connection connection = DriverManager.getConnection(url, user, password);
        //写sql语句
        String sql="select * from admin";
        //得到preparedStatement,并完成赋值
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        //执行查询
        ResultSet resultSet = preparedStatement.executeQuery();
        while(resultSet.next()){
            String name = resultSet.getString(1);
            String pwd = resultSet.getString(2);
            System.out.println("用户名:"+name+"\t"+"密码:"+pwd);
        }
        //关闭连接
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }

7、JDBC的相关API小结

在这里插入图片描述

8、封装Utils[关闭连接,释放资源]

(1)说明

在jdbc操作中,取得连接和释放资源是经常使用到的,可以将其封装成JDBC连接的工具类Utils。

下面我们将实现这个功能

(2)代码实现

创建Utils类实现jdbc的关闭连接和释放资源功能

package jdbc.JDBCUtils;

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

/**
 * @author D
 * @version 1.0
 * @project JDBC
 * @description 实现获取连接和关闭资源功能
 * @date 2024/5/17 14:45:45
 */
public class Utils {
    private static String user;//用户名
    private static String password;//密码
    private static String url;//url
    private static String driver;//驱动名
    //在静态代码块去初始化
    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);
        }
    }

    //关闭相应资源
    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);
        }
    }
}

(3)使用刚刚创建的Utils类完成dml和查询操作

1、dml操作
package jdbc.JDBCUtils;

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

/**
 * @author D
 * @version 1.0
 * @project JDBC
 * @description 使用Utils工具类完成dml操作
 * @date 2024/5/17 14:58:24
 */
public class JDBCUtils_UseDml {
    public static void main(String[] args) {
        //1、得到连接
        Connection connection = null;
        PreparedStatement preparedStatement=null;
        //2、组织一个sql语句
        String sql="update actor set name=? where id=?";
        //3、创建PreparedStatement对象
        try {
            connection=Utils.getConnection();
            preparedStatement=connection.prepareStatement(sql);
            preparedStatement.setString(1,"djt");
            preparedStatement.setInt(2,1);
            int rows = preparedStatement.executeUpdate();
            System.out.println(rows>0?"执行成功":"执行失败");
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            //关闭资源
            Utils.close(null,preparedStatement,connection);
        }
    }
}
2、查询操作
package jdbc.JDBCUtils;

import java.sql.*;

/**
 * @author D
 * @version 1.0
 * @project JDBC
 * @description 使用Utils工具完成查询操作
 * @date 2024/5/17 15:11:08
 */
public class JDBCUtils_UseSelect {
    public static void main(String[] args) {
        //1、得到连接
        Connection connection = null;
        PreparedStatement preparedStatement=null;
        ResultSet set=null;
        //2、组织一个sql语句
        String sql="select * from actor";
        //3、创建PreparedStatement对象
        try {
            connection=Utils.getConnection();
            preparedStatement=connection.prepareStatement(sql);
            set= preparedStatement.executeQuery();
            while(set.next()){
                int id = set.getInt("id");
                String name = set.getString("name");
                String sex = set.getString("sex");
                Date date = set.getDate("borndate");
                String phone = set.getString("phone");
                System.out.println(id+"\t"+name+"\t"+sex+"\t"+date+"\t"+phone);
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            //关闭资源
            Utils.close(set,preparedStatement,connection);
        }
    }
}

9、事务

(1)基本介绍

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

(2)应用实例

模拟经典的转账任务。

先创建以下表:

在这里插入图片描述

1、不使用事务时
public void noTransaction_(){
        //操作转账的业务
        //1、得到连接
        Connection connection = null;
        PreparedStatement preparedStatement=null;
        //2、组织一个sql语句
        String sql1="update account set balance=balance-100 where id=1";
        String sql2="update account set balance=balance+100 where id=2";
        //3、创建PreparedStatement对象
        try {
            connection= Utils.getConnection();//在默认情况下,connection是自动提交的
            preparedStatement=connection.prepareStatement(sql1);
            //执行第一条语句
            preparedStatement.executeUpdate();
            //当执行完第一条语句后由异常第二条语句就不会执行这时就会出现金额错误问题
            int n1=1/0;//执行到这里会报错,这时第二条语句就不会执行了
            preparedStatement=connection.prepareStatement(sql2);
            //执行第二条语句
            preparedStatement.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            //关闭资源
            Utils.close(null,preparedStatement,connection);
        }
    }
/**
*会发现马云的余额少了100但是马化腾的余额没变。马云的100元不翼而飞了!这很致命
**/
2、使用事务时
public void Transaction_(){
        //操作转账的业务
        //1、得到连接
        Connection connection = null;
        PreparedStatement preparedStatement=null;
        //2、组织一个sql语句
        String sql1="update account set balance=balance-100 where id=1";
        String sql2="update account set balance=balance+100 where id=2";
        //3、创建PreparedStatement对象
        try {
            connection= Utils.getConnection();//在默认情况下,connection是自动提交的
            connection.setAutoCommit(false);//设置为不自动提交,也相当于开启了事务
            preparedStatement=connection.prepareStatement(sql1);
            //执行第一条语句
            preparedStatement.executeUpdate();
            //当执行完第一条语句后由异常第二条语句就不会执行这时就会出现金额错误问题
            int n1=1/0;
            preparedStatement=connection.prepareStatement(sql2);
            //执行第二条语句
            preparedStatement.executeUpdate();
            //提交事务
            connection.commit();
        } catch (SQLException e) {
            try {
                System.out.println("执行异常,撤销刚刚执行的sql语句");
                connection.rollback();//这里可以进行回滚,也可以回滚到保存点
            } catch (SQLException ex) {
                throw new RuntimeException(ex);
            }
            throw new RuntimeException(e);
        } finally {
            //关闭资源
            Utils.close(null,preparedStatement,connection);
        }
    }
/**
*会发现马云的余额和马化腾的余额都没有变,是因为事务回滚了
**/

(3)课后练习

package jdbc.transaction_;

import jdbc.JDBCUtils.Utils;

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

/**
 * @author D
 * @version 1.0
 * @project JDBC
 * @description 课堂练习
 * @date 2024/5/18 11:11:22
 */
public class homework {
    /**
     * 1、创建account表
     * 2、在表中先添加两条记录tom余额100,king余额200
     * 3、使用事务完成,tom给king转10元
     */
    public static void main(String[] args) {
        //得到连接
        Connection connection = Utils.getConnection();
        PreparedStatement preparedStatement=null;
        //组织sql语句
        String sql1="update account set balance=balance-10 where id=3";
        String sql2="update account set balance=balance+10 where id=4";
        //创建PreparedStatement对象
        try {
            connection=Utils.getConnection();
            connection.setAutoCommit(false);//设置不自动提交,等同于开启了事务
            //执行第一条语句
            preparedStatement=connection.prepareStatement(sql1);
            preparedStatement.executeUpdate();
            //执行第二条语句
            preparedStatement=connection.prepareStatement(sql2);
            preparedStatement.executeUpdate();
            //提交事务
            connection.commit();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            Utils.close(null,preparedStatement,connection);
        }
    }
}

10、批处理

(1)基本介绍

  1. 当需要成批插入或者更新记录时。可以采用Java的批处理更新机制,这一机制允许多条语句一次性提交数据库批量处理。通常情况下比单独提交处理更有效率。
  2. JDBC的批量处理语句包括以下方法:
  • addBatch():添加需要批量处理的SQL语句或参数
  • executeBatch():执行批量处理语句
  • clearBatch():清空批处理包的语句
  1. JDBC连接MySQL时,如果要使用批处理功能,请在mysql.properties配置文件的url中添加参数 ?rewriteBatchedStatements=true
  2. 批处理往往和PreparedStatement一起搭配使用,可以既减少编译次数,有减少运行次数,效率大大提高

(2)应用案例

向admin2表中添加5000条数据,看看使用和不使用批处理各消耗多少时间。

admin2表创建:

CREATE TABLE admin2(
	id INT PRIMARY KEY auto_increment,
	username VARCHAR(32) NOT NULL,
	password VARCHAR(32) NOT NULL);

不使用批处理:

public void noBatch() throws Exception {
    //获取连接
    Connection connection = Utils.getConnection();
    String sql="insert into admin2 values (null,?,?)";
    PreparedStatement preparedStatement = connection.prepareStatement(sql);
    System.out.println("开始执行");
    long start = System.currentTimeMillis();//开始时间
    for (int i = 0; i < 5000; i++) {
        preparedStatement.setString(1,"jack"+i);
        preparedStatement.setString(2,"666");
        preparedStatement.executeUpdate();
    }
    long end = System.currentTimeMillis();//结束时间
    System.out.println("传统的方式 耗时="+(end-start));//传统的方式 耗时=12676
    //关闭连接
    Utils.close(null,preparedStatement,connection);
}

使用批处理:

public void Batch_() throws Exception {
    //获取连接
    Connection connection = Utils.getConnection();
    String sql="insert into admin2 values (null,?,?)";
    PreparedStatement preparedStatement = connection.prepareStatement(sql);
    System.out.println("开始执行");
    long start = System.currentTimeMillis();//开始时间
    for (int i = 0; i < 5000; i++) {
        preparedStatement.setString(1,"jack"+i);
        preparedStatement.setString(2,"666");
        //将 sql 语句加入到批处理包中 -> 看源码
        /*
        //1. //第一就创建 ArrayList - elementData => Object[]
        //2. elementData => Object[] 就会存放我们预处理的 sql 语句
        //3. 当 elementData 满后,就按照 1.5 扩容
        //4. 当添加到指定的值后,就 executeBatch
        //5. 批量处理会减少我们发送 sql 语句的网络开销,而且减少编译次数,因此效率提高
        public void addBatch() throws SQLException {
        synchronized(this.checkClosed().getConnectionMutex()) {
        if (this.batchedArgs == null) {
        this.batchedArgs = new ArrayList();
        }
        for(int i = 0; i < this.parameterValues.length; ++i) {
        this.checkAllParametersSet(this.parameterValues[i], this.parameterStreams[i], i);
        }
        this.batchedArgs.add(new PreparedStatement.BatchParams(this.parameterValues, this.parameterStreams, this.isStream, this.streamLengths, this.isNull));
        }
        }
        */
        preparedStatement.addBatch();
        if ((i+1)%1000==0){
            preparedStatement.executeBatch();
            //清空一下
            preparedStatement.clearBatch();
        }
    }
    long end = System.currentTimeMillis();//结束时间
    System.out.println("预处理的方式 耗时="+(end-start));//预处理的方式 耗时=129
    //关闭连接
    Utils.close(null,preparedStatement,connection);
}

11、数据库连接池

(1)引入:5K次连接数据库问题

package jdbc.datasource;

import jdbc.JDBCUtils.Utils;
import org.junit.Test;

import java.sql.Connection;

/**
 * @author D
 * @version 1.0
 * @project JDBC
 * @description 传统连接数据库问题
 * @date 2024/5/18 17:14:20
 */
public class ConQuestion {
    //连接MySQL5000次
    @Test
    public void testCon(){
        long start = System.currentTimeMillis();
        System.out.println("开始连接");
        for (int i = 0; i < 5000; i++) {
            //使用传统方法连接
            Connection connection = Utils.getConnection();
            //做一些工作,比如得到PreparedStatement,发送sql
            //....
            //关闭
            Utils.close(null,null,connection);
        }
        long end = System.currentTimeMillis();
        System.out.println("耗时="+(end-start));//耗时=32865
    }
}

(2)传统获取Connection问题分析

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

(3)数据库连接池基本介绍

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

在这里插入图片描述

数据库连接池示意图

在这里插入图片描述

(4)数据库连接池种类

在这里插入图片描述

(5)C3P0

前置工作:在libs目录下导入c3p0的jar包,与MySQL的jar包导入方法一致

方法一:相关参数,在程序中指定user,url,password等

public void testC3P0_01() throws IOException, PropertyVetoException, SQLException {
    //获取一个数据源对象
    ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
    //通过配置文件(mysql.properties)获取相关连接信息
    Properties properties = new Properties();
    properties.load(new FileInputStream("src\\mysql.properties"));
    //读取相关的属性值
    String user = properties.getProperty("user");
    String password = properties.getProperty("password");
    String url = properties.getProperty("url");
    String driver = properties.getProperty("driver");

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

    //设置初始化连接数
    comboPooledDataSource.setInitialPoolSize(10);
    //设置最大连接数
    comboPooledDataSource.setMaxPoolSize(50);
    //测试用连接池连接MySQL5000次的效率
    long start = System.currentTimeMillis();
    for (int i = 0; i < 5000; i++) {
        Connection connection = comboPooledDataSource.getConnection();//这个方法就是从DateSource接口实现
//            System.out.println("连接成功");
        connection.close();
    }
    long end = System.currentTimeMillis();
    System.out.println("C3P0连接池耗时="+(end-start));//C3P0连接池耗时=664
}

方法二:使用配置文件模拟完成(前提在src目录下导入c3p0-config.xml文件)

<c3p0-config>
    <!-- 数据源名称代表连接池 -->
  <named-config name="Ghost-D"><!-- 这里自己起名字就可以了 -->
<!-- 驱动类 -->
  <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
  <!-- url-->
  	<property name="jdbcUrl">jdbc:mysql://localhost:3306/djt_db01</property>
  <!-- 用户名 -->
  		<property name="user">root</property>
  		<!-- 密码 -->
  	<property name="password">123456</property>
  	<!-- 每次增长的连接数-->
    <property name="acquireIncrement">5</property>
    <!-- 初始的连接数 -->
    <property name="initialPoolSize">10</property>
    <!-- 最小连接数 -->
    <property name="minPoolSize">5</property>
   <!-- 最大连接数 -->
    <property name="maxPoolSize">50</property>

	<!-- 可连接的最多的命令对象数 -->
    <property name="maxStatements">5</property> 
    
    <!-- 每个连接对象可连接的最多的命令对象数 -->
    <property name="maxStatementsPerConnection">2</property>
  </named-config>
</c3p0-config>
//方式二:使用配置文件模板来完成
//将c3p0提供的c3p0-config.xml 拷贝到src目录下
//该文件制定了连接数据库和连接池的相关参数
@Test
public void testC3P0_02() throws SQLException {
    ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("Ghost-D");
    测试用连接池连接MySQL5000次的效率
    long start = System.currentTimeMillis();
    for (int i = 0; i < 5000; i++) {
        Connection connection = comboPooledDataSource.getConnection();
//            System.out.println("连接成功");
        connection.close();
    }
    long end = System.currentTimeMillis();
    System.out.println("C3P0连接池耗时="+(end-start));//C3P0连接池耗时=667
}

(6)Druid(德鲁伊)

package jdbc.datasource;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.junit.Test;

import javax.sql.DataSource;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.util.Properties;

/**
 * @author D
 * @version 1.0
 * @project JDBC
 * @description 演示德鲁伊连接池
 * @date 2024/5/19 12:50:55
 */
public class Druid_ {
    @Test
    public void testDruid() throws Exception {
        //1、导入Druid的jar包
        //2、加入配置文件druid.properties到src目录下
        //3、创建properties对象,读取配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\druid.properties"));

        //4、创建一个指定参数的数据库连接池,Druid连接池
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        //测试用连接池连接MySQL5000次的效率
        long start = System.currentTimeMillis();
        for (int i = 0; i < 5000; i++) {
            Connection connection = dataSource.getConnection();
//            System.out.println("连接成功");
            connection.close();
        }
        long end = System.currentTimeMillis();
        System.out.println("Druid连接池耗时="+(end-start));//Druid连接池耗时=861
    }
}

12、Apache—DBUtils

(1)引入

先分析一个问题:当关闭connection后,resultset结果集无法使用而且resultset不利于数据的管理

在这里插入图片描述

(2)自己实现一下(土办法)

/**
 * 使用土方法将ResultSet结果集 =封装=>到ArrayList
 */

public ArrayList<Actor> SelectToArrayList(){
    //1、得到连接
    Connection connection = null;
    PreparedStatement preparedStatement=null;
    ResultSet set=null;
    ArrayList<Actor> list=new ArrayList<>();//存放结果集的集合
    //2、组织一个sql语句
    String sql="select * from actor";
    //3、创建PreparedStatement对象
    try {
        connection=JDBCUtilsByDruid.getConnection();
        preparedStatement=connection.prepareStatement(sql);
        set= preparedStatement.executeQuery();
        while(set.next()){
            int id = set.getInt("id");
            String name = set.getString("name");
            String sex = set.getString("sex");
            LocalDateTime date = set.getDate("borndate").toInstant().atZone(ZoneId.of("UTC")).toLocalDateTime();//这里是因为我的MySQL版本不兼容getDate("borndate")所以就百度查找到的解决方法
            String phone = set.getString("phone");
//                System.out.println(id+"\t"+name+"\t"+sex+"\t"+date+"\t"+phone);
            list.add(new Actor(id,name,sex,date,phone));
        }
//            System.out.println(list);
        //也可以遍历,输出想让客户看到的信息
        for (Actor actor :list) {
            System.out.println("id="+actor.getId()+"\t"+"name="+actor.getName());
        }

    } catch (SQLException e) {
        throw new RuntimeException(e);
    } finally {
        //关闭资源
        JDBCUtilsByDruid.close(set,preparedStatement,connection);
    }
    //因为ArrayList和Connection没有关系,所以该集合可以复用
    return list;
}

(3)基本介绍

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

  • DBUtils类
  1. QueryRunner类:该类封装了SQL的执行,是线程安全的。可以实现增、删、改、查、批处理
  2. 使用QueryRunner类实现查询
  3. ResultSetHandler接口:该接口用于处理java.sql.ResultSet,将数据按要求转换为另一种形式

在这里插入图片描述

(4)应用实例

1、演示DBUtils+数据库连接池(德鲁伊)方式,完成对表actor的crud
package jdbc.datasource;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.junit.Test;

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

/**
 * @author D
 * @version 1.0
 * @project JDBC
 * @description 使用Apache-DBUtils和德鲁伊数据库连接池完成对actor的crud
 * @date 2024/5/19 16:25:34
 */
public class DBUtils_Use {
    @Test
    public void testDBUtils() throws SQLException {
        //得到连接(Druid连接池)
        Connection connection = JDBCUtilsByDruid.getConnection();
        //使用DBUtils的类和接口(导入jar包)
        //创建QueryRunner
        QueryRunner queryRunner = new QueryRunner();
        String sql="select * from actor where id >=?";
        /*
            (1)query方法就是执行sql语句,得到resultset ---封装到---> ArrayList集合中
            (2)返回集合
            (3)connection:连接
            (4)sql:要执行的sql语句
            (5)new BeanListHandler<>(Actor.class):将resultset->Actor对象->封装到ArrayList
            底层使用了反射机制(因为并不知到Actor的属性有哪些,所以通过反射就可以知道Actor的属性),去获取Actor的属性,然后进行封装
            (6)1 就是给sql语句中的?赋值的,可以有多个值,因为是可变参数Object... params
            (7)底层得到的resultset,会在query关闭,关闭PreparedStatement
         */
        List<Actor> list = queryRunner.query(connection, sql, new BeanListHandler<>(Actor.class), 1);
        for (Actor actor :list) {
            System.out.println(actor);
        }
        //释放资源
        JDBCUtilsByDruid.close(null,null,connection);
    }
}

queryRunner.query的底层源码:

public <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
    PreparedStatement stmt = null;//定义PreparedStatement
    ResultSet rs = null;//接受返回的结果集
    T result = null;//返回ArrayList

    try {
        stmt = this.prepareStatement(conn, sql);//创建PreparedStatement
        this.fillStatement(stmt, params);//对sql的?进行赋值
        rs = this.wrap(stmt.executeQuery());//执行sql语句,返回resultset
        result = rsh.handle(rs);//返回的resultset-->arrayList[result](使用到反射,对传入的class对象处理)
    } catch (SQLException var33) {
        this.rethrow(var33, sql, params);
    } finally {
        try {
            this.close(rs);//关闭resultset
        } finally {
            this.close((Statement)stmt);//关闭PreparedStatement
        }
    }

    return result;
}
2、演示apache-dbutils + druid 完成 返回的结果是单行记录(单个对象)
/**
 * 演示apache-dbutils + druid 完成 返回的结果是单行记录(单个对象)
 */
public void testDBUtilsSingle() throws SQLException {
    //得到连接(Druid连接池)
    Connection connection = JDBCUtilsByDruid.getConnection();
    //使用DBUtils的类和接口(导入jar包)
    //创建QueryRunner
    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);
}
3、演示 apache-dbutils + druid 完成查询结果是单行单列-返回的就是 object
public void testScalar() throws SQLException {
    //得到连接(Druid连接池)
    Connection connection = JDBCUtilsByDruid.getConnection();
    //使用DBUtils的类和接口(导入jar包)
    //创建QueryRunner
    QueryRunner queryRunner = new QueryRunner();
    String sql="select name from actor where id =?";
    //因为返回的是一个对象,使用的handler就是ScalarHandler
    Object object = queryRunner.query(connection, sql, new ScalarHandler(), 1);
    System.out.println(object);
    //关闭资源
    JDBCUtilsByDruid.close(null,null,connection);
}
4、演示apache-dbutils + druid 完成 dml (update, insert, delete)
public void testDML() throws SQLException {
    //得到连接(Druid连接池)
    Connection connection = JDBCUtilsByDruid.getConnection();
    //使用DBUtils的类和接口(导入jar包)
    //创建QueryRunner
    QueryRunner queryRunner = new QueryRunner();
    String sql="insert into actor values(null,?,?,?,?)";
    int rows = queryRunner.update(connection, sql, "djt", "男", "2001-02-25", "110");//返回的是受影响的行数
    System.out.println(rows>0?"执行成功":"执行失败");
    //关闭资源
    JDBCUtilsByDruid.close(null,null,connection);
}

(5)数据库表和JavaBean的类型映射关系

在这里插入图片描述

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

(1)引入

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

在这里插入图片描述

(2)基本说明

  1. DAO: data access object数据访问对象
  2. 这样的通用类,称为BasicDao,是专门和数据库交互的,即完成对数据库(表)的crud操作。
  3. 在BasicDao的基础上,实现一张表对应一个DAO,更好的完成功能,比如User表–>User.java类(JavaBean)–>UserDao.java

(3)BasicDao应用实例

先创建bao_包,在bao_包下创建4个包分别为utils包(工具类),domain包(javabean),dao包(存放XxxDAO和BasicBAO),test包(写测试类)。

在utils包下添加JDBCUtilsByDruid类(基于druid数据库连接池的工具类):

package 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 D
 * @version 1.0
 * @project JDBC
 * @description 基于druid数据库连接池的工具类
 * @date 2024/5/19 16:13:58
 */
public class JDBCUtilsByDruid {
    private static DataSource ds;
    //在静态代码块中完成ds的初始化
    static {
        Properties properties = new Properties();
        try {
            properties.load(new FileInputStream("src\\druid.properties"));
            ds= DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    //编写getConnection方法
    public static Connection getConnection() throws SQLException {
        return ds.getConnection();
    }
    //关闭连接,再次强调:在数据库连接池技术中,close不是真正的断掉链接,而是把使用的Connection对象放回连接池
    public static void close(ResultSet resultSet, Statement statement,Connection connection){
        try {
            if (resultSet!=null){
                resultSet.close();
            }
            if (statement!=null){
                statement.close();
            }
            if (connection!=null){
                connection.close();
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

在domain包下创建Actor类(Actor对象和actor表的记录对应):

package dao_.domain;

import java.time.LocalDateTime;

/**
 * @author D
 * @version 1.0
 * @project JDBC
 * @description Actor对象和actor表的记录对应
 * @date 2024/5/19 17:12:07
 */
public class Actor {//javabean,pojo,domain对象
    private Integer id;
    private String name;
    private String sex;
    private LocalDateTime borndate;
    private String phone;

    public Actor() {//一定要给一个无参构造器[反射需要]
    }

    public Actor(Integer id, String name, String sex, LocalDateTime 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 LocalDateTime getBorndate() {
        return borndate;
    }

    public void setBorndate(LocalDateTime borndate) {
        this.borndate = borndate;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    @Override
    public String toString() {
        return "Actor{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", borndate=" + borndate +
                ", phone='" + phone + '\'' +
                '}';
    }
}

在dao包下创建BasicBAO(开发BasicDAO,是其他DAO的父类):

package dao_.dao;

import 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 D
 * @version 1.0
 * @project JDBC
 * @description 开发BasicDAO,是其他DAO的父类
 * @date 2024/5/21 15:21:59
 */
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 rows = qr.update(connection, sql, parameters);
            return rows;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            JDBCUtilsByDruid.close(null,null,connection);
        }
    }

    //开发返回多个对象(即查询的结果是多行),针对任意表
    /**
     *
     * @param sql sql语句,可以有?
     * @param clazz 传入一个类的Class对象,比如Actor.class
     * @param parameters 传入?的具体的值,可以是多个
     * @return 根据Actor.class,返回对应的ArrayList集合
     */
    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);
        }
    }

    //开发返回一个对象(即查询的结果是d单行),针对任意表
    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);
        }
    }
}

在bao包下创建ActorBAO(继承BasicBAO类):

package dao_.dao;

import dao_.domain.Actor;

/**
 * @author D
 * @version 1.0
 * @project JDBC
 * @description
 * @date 2024/5/21 15:47:03
 */
public class ActorDAO extends BasicDAO<Actor>{
    //就有BasicDAO的方法
    //根据业务需求,可以编写特有的方法
}

在test包下创建testDAO类(测试ActorDAO对actor表的crud操作):

package dao_.test;

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

import java.util.List;

/**
 * @author D
 * @version 1.0
 * @project JDBC
 * @description
 * @date 2024/5/21 15:50:36
 */
public class testDAO {

    //测试ActorDAO对actor表的crud操作
    @Test
    public void testActorDAO(){
        ActorDAO actorDAO = new ActorDAO();
        //1、查询多行记录
        System.out.println("查询多行记录");
        List<Actor> actors = actorDAO.queryMulti("select * from actor where id>=?", Actor.class, 1);
        for (Actor actor :actors) {
            System.out.println(actor);
        }
        //2、查询单行记录
        System.out.println("查询单行记录");
        Actor actor = actorDAO.querySingle("select * from actor where id=?", Actor.class, 1);
        System.out.println(actor);
        //3、查询单行单列
        System.out.println("查询单行单列");
        Object o = actorDAO.queryScalar("select name from actor where id=?", 1);
        System.out.println(o);
        //dml操作
        System.out.println("演示插入操作");
        int rows = actorDAO.update("insert into actor values(null,?,?,?,?)", "Dew", "男", "2001-02-25", "120");
        System.out.println(rows>0?"执行成功":"执行失败");
    }
}

这样就完成了具体操作,其他XxxBAO与上面操作一致。也可根据需求添加特殊方法。

(4)课后练习,一定要独自完成!!!

在这里插入图片描述

先创建表,然后在domain包下创建Goods类:

package dao_.domain;

/**
 * @author D
 * @version 1.0
 * @project JDBC
 * @description
 * @date 2024/5/21 17:08:56
 */
public class Goods {
    private int id;
    private String goods_name;
    private double price;

    public Goods() {
    }

    public Goods(int id, String goods_name, double price) {
        this.id = id;
        this.goods_name = goods_name;
        this.price = price;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getGoods_name() {
        return goods_name;
    }

    public void setGoods_name(String goods_name) {
        this.goods_name = goods_name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Goods{" +
                "id=" + id +
                ", goods_name='" + goods_name + '\'' +
                ", price=" + price +
                '}';
    }
}

在dao包下创建GoodsDAO类(继承BasicBAO类):

package dao_.dao;

import dao_.domain.Goods;

/**
 * @author D
 * @version 1.0
 * @project JDBC
 * @description
 * @date 2024/5/21 17:10:13
 */
public class GoodsDAO extends BasicDAO<Goods>{

}

在test包下的testDAO类下添加以下代码:

public void testGoodsDAO(){
    GoodsDAO goodsDAO = new GoodsDAO();
    //1、查询多行记录
    System.out.println("查询多行记录");
    List<Goods> goodsList = goodsDAO.queryMulti("select * from Goods_ where id>=?", Goods.class, 10);
    for (Goods goods :goodsList) {
        System.out.println(goods);
    }
    //2、查询单行记录
    System.out.println("查询单行记录");
    Goods goods = goodsDAO.querySingle("select * from Goods_ where id=?", Goods.class, 10);
    System.out.println(goods);
    //3、查询单行单列
    System.out.println("查询单行单列");
    Object o = goodsDAO.queryScalar("select goods_name from Goods_ where id=?", 10);
    System.out.println(o);
    //dml操作
    System.out.println("演示插入操作");
    int rows = goodsDAO.update("insert into Goods_ values(?,?,?)", 30, "小米手机", 3000);
    System.out.println(rows>0?"执行成功":"执行失败");
}

这样课后练习就完成了。

his.id = id;
this.goods_name = goods_name;
this.price = price;
}

public int getId() {
    return id;
}

public void setId(int id) {
    this.id = id;
}

public String getGoods_name() {
    return goods_name;
}

public void setGoods_name(String goods_name) {
    this.goods_name = goods_name;
}

public double getPrice() {
    return price;
}

public void setPrice(double price) {
    this.price = price;
}

@Override
public String toString() {
    return "Goods{" +
            "id=" + id +
            ", goods_name='" + goods_name + '\'' +
            ", price=" + price +
            '}';
}

}


在dao包下创建GoodsDAO类(继承BasicBAO类):

```java
package dao_.dao;

import dao_.domain.Goods;

/**
 * @author D
 * @version 1.0
 * @project JDBC
 * @description
 * @date 2024/5/21 17:10:13
 */
public class GoodsDAO extends BasicDAO<Goods>{

}

在test包下的testDAO类下添加以下代码:

public void testGoodsDAO(){
    GoodsDAO goodsDAO = new GoodsDAO();
    //1、查询多行记录
    System.out.println("查询多行记录");
    List<Goods> goodsList = goodsDAO.queryMulti("select * from Goods_ where id>=?", Goods.class, 10);
    for (Goods goods :goodsList) {
        System.out.println(goods);
    }
    //2、查询单行记录
    System.out.println("查询单行记录");
    Goods goods = goodsDAO.querySingle("select * from Goods_ where id=?", Goods.class, 10);
    System.out.println(goods);
    //3、查询单行单列
    System.out.println("查询单行单列");
    Object o = goodsDAO.queryScalar("select goods_name from Goods_ where id=?", 10);
    System.out.println(o);
    //dml操作
    System.out.println("演示插入操作");
    int rows = goodsDAO.update("insert into Goods_ values(?,?,?)", 30, "小米手机", 3000);
    System.out.println(rows>0?"执行成功":"执行失败");
}

这样课后练习就完成了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值