JDBC相关知识

JDBC

简介

基本介绍

  • JDBC是为了访问不同的数据库提供了统一接口,为使用者屏蔽了细节问题。

  • Java程序员使用JDBC,可以连接任何提供了JDBC驱动程序的数据库系统,从而完成对数据库的各种操作

  • JDBC的基本原理图

JDBC相关知识_第1张图片

JDBC 好处

JDBC是Java提供一套用于数据库操作的接口API,Java长序员只需要面向这套接口编程即可。不同的数据厂商,需要针对这套接口,提供不同的实现。
JDBC API是一系列的接口,它统一和规范了应用程序与数据库的连接、执行SQL语句,并得到返回结果等各类操作,相关类的接口在java.sql与javax.sql中

JDBC程序编写步骤

  1. 注册驱动-加载Driver类
  2. 获取连接-得到Connection,java程序和数据库之间的连接。
  3. 执行增删改查-发送相关的sql命令给mysql执行。
  4. 释放资源-关闭相关的连接

连接数据库的五种方式

示例

通过JDBC对表actor进行添加、删除和修改操作。

(一):静态加载
package khan.louis;

import com.mysql.cj.jdbc.Driver;

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


/**
 * @author XRY
 * @date 2023年04月13日19:46
 */
//第一个JDBC程序,完成简单的操作
public class TestJDBC01 {
    public static void main(String[] args) throws SQLException{
        //前置工作:在项目下创建一个文件夹,将mysql.jar拷贝到该目录下,点击add to libraries
        /*导入JDBC的JAR包,可以在https://dev.mysql.com/downloads/file/?id=477058下载*/
        //1.注册驱动,
        Driver driver = new Driver();//创建driver对象

        //2.得到连接
        String url = "jdbc:mysql://127.0.0.1:3306/(数据库名)?serverTimezone=GMT&useSSL=false";
       /* jdbc:mysql://是一个规定好的协议,通过jdbc的方式连接mysql
        localhost ,表示本机的ip地址,也可以是其他ip
        3306:端口号
        testjdbc是表示连接的数据库名称*/
        //mysql的连接的本质就是socket连接
        //将用户名和密码封装到Properties对象中
        Properties properties = new Properties();
        properties.setProperty("user", "username");//用户名
        properties.setProperty("password", "password");//密码

        //获取连接
       Connection connect = driver.connect(url, properties);

        //3.执行操作
//        String sql = "insert into actor(name, sex, borndate, phone) values('khan','女','1999-07-21','11111111111');";
        String sql = "delete from actor where name = 'khan' and sex = '女';";
        Statement statement = connect.createStatement();//可以执行sql语句,并返回其生成的对象
        int rows = statement.executeUpdate(sql);//如果是dml语句,返回的就是影响的行数

        System.out.println(rows > 0? "成功":"失败");
        //4.关闭连接资源
        statement.close();
        connect.close();
    }
}
(二):动态加载

使用com.mysql.jc.jdbc.Driver

package khan.louis;

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

/**
 * @author XRY
 * @date 2023年04月14日9:24
 */
//方式二:使用反射加载Driver类
public class TestJDBCTwo {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
        //使用反射,动态加载,更加灵活,减少依赖性
        Class<?> aClass = Class.forName("com.mysql.cj.jdbc.Driver");//加载的是Driver的class对象
        //我们可以转换为Driver对象
        Driver driver = (Driver)aClass.newInstance();

        String url = "jdbc:mysql://localhost:3306/testjdbc?serverTimezone=GMT&useSSL=false";
        Properties properties = new Properties();
        properties.setProperty("user", "root");
        properties.setProperty("password", "zkpk");
        Connection connect = driver.connect(url, properties);
        System.out.println("方式二" + connect);

        //执行
        String sql = "insert into actor(name, sex, borndate, phone) values('louis','男','1999-07-22','113111111');";;

        Statement statement = connect.createStatement();
        int result = statement.executeUpdate(sql);
        System.out.println(result > 0? "成功":"失败");

        //关闭
        statement.close();
        connect.close();
    }
}
(三):使用DriverManager

使用 DriverManager 替换Driver

package khan.louis.jdbc;

import java.sql.*;

/**
 * @author XRY
 * @date 2023年04月21日13:11
 */

//方式三:使用DriverManager代替Driver
public class TestJDBCThree {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
        //使用反射加载Driver
        Class<?> aClass = Class.forName("com.mysql.cj.jdbc.Driver");
        Driver driver = (Driver)aClass.newInstance();

        //创建url USER和password
        String url = "jdbc:mysql://localhost:3306/testjdbc?serverTimezone=GMT&useSSL=false";
        String user = "root";
        String password = "zkpk";


        //使用DriverManager代替Driver
        DriverManager.registerDriver(driver);//注册驱动
        //使用getConnection
        Connection connection = DriverManager.getConnection(url, user, password);
        System.out.println("第三种方式:"+ connection);
        //执行
        String sql = "insert into actor(name, sex, borndate, phone) values('louis','男','1989-07-22','113111111');";;

        Statement statement = connection.createStatement();
        int result = statement.executeUpdate(sql);
        System.out.println(result > 0? "成功":"失败");

        //关闭
        statement.close();
        connection.close();
    }
}
(四):Class.forName 自动完成驱动注册

这种方式获取连接时使用最多的,推荐使用。

package khan.louis.jdbc;

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

/**
 * @author XRY
 * @date 2023年04月21日14:11
 */

//方式四:使用Class.forName自动完成注册驱动,简化代码
public class TestJDBCFour {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
       //在加载Driver类时, 注册驱动自动完成。
        /*
         源码:
         1.静态代码块,在类加载的时候,会执行一次DriverManager.registerDriver()
         static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }*/
        Class.forName("com.mysql.cj.jdbc.Driver");//使用反射加载
        //创建url 用户名和密码
        String url = "jdbc:mysql://localhost:3306/testjdbc?serverTimezone=GMT&useSSL=false";
        String user = "root";
        String password = "zkpk";

        Connection connection = DriverManager.getConnection(url, user, password);
        System.out.println("第四种方式" + connection);
        String sql = "insert into actor(name, sex, borndate, phone) values('louis','男','1989-07-22','113411111');";;

        Statement statement = connection.createStatement();
        int result = statement.executeUpdate(sql);
        System.out.println(result > 0? "成功":"失败");

        //关闭
        statement.close();
        connection.close();
    }
}

注:在mysql1.5以后使用了jdbc4可以无需Class.forName(“com.mysql.jdbc.Driver”);注册驱动而是自动调用驱动jar包下的META-INF\services\java.sql.Driver文本中的类名称去注册。但建议还是写入。避免版本混淆。

JDBC相关知识_第2张图片

(五):使用配置文件
package khan.louis.jdbc;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

/**
 * @author XRY
 * @date 2023年04月21日14:50
 */
//方式五:添加配置文件,使连接更灵活
public class TestJDBCFive {
    public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
        //在方式四的基础上增减配置文件,让链接mysql更加灵活


        //通过properties对象获取相关配置文件的信息
        Properties properties = new Properties();
        properties.load(new FileInputStream("JDBc\\src\\khan\\louis\\jdbc\\MySQL.properties"));
        //获取相关的值
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String driver = properties.getProperty("driver");
        String url = properties.getProperty("url");
        url = url + "?serverTimezone=GMT&useSSL=false";

        Class.forName(driver);
        Connection connection = DriverManager.getConnection(url, user, password);
        System.out.println("方式五" + connection);

    }
}

//配置文件
user=root
password=root
url=jdbc:mysql://localhost:3306/testjdbc
driver=com.mysql.cj.jdbc.Driver
练习(五)

示例:使用方式五:
1、创建student
2、使用jdbc添加五条数据
3、修改id = 1的记录,将name改成自己的名字,
4、删除id = 3的记录

package khan.louis.self_testjdbc;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
 * @author XRY
 * @date 2023年04月21日15:37
 */
public class TestStudent {
    public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
        //引入properties文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("JDBc\\src\\khan\\louis\\self_testjdbc\\student.properties"));
        //获取文件中的内容
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driver = properties.getProperty("driver");
        url = url + "?serverTimezone=GMT&useSSL=false";

        //加载Driver类,
        Class.forName(driver);

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

        //执行操作使用jdbc添加五条数据
        String sql = "insert into student(`id`, `name`, `age`) values (1, '可汗', 12),(2, '路易斯', 18), (3, '威登', 23), (4, 'louis', 12), (5, 'khan', 18)";
        Statement statement = connection.createStatement();
        int result = statement.executeUpdate(sql);
        System.out.println(result > 0? "成功":"失败");

        //修改id = 1的记录,将name改成自己的名字
        String sql2 = "update student set `name` = 'sea' where `id` = 1;";
        int result1 = statement.executeUpdate(sql2);
        System.out.println(result1 > 0? "successful":"defeat");

        //删除id = 3的记录
        String sql3 = "delete from student where `id` = 3;";
        int result4 = statement.executeUpdate(sql3);
        System.out.println(result4 > 0?"成功":"失败");

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


    }
}


//配置文件
user=root
password=root
url=jdbc:mysql://localhost:3306/testjdbc
driver=com.mysql.cj.jdbc.Driver

//结果
在这里插入图片描述
JDBC相关知识_第3张图片

注意:在这里,我直接先创建了数据表student,下面是sql语句.

use testjdbc;
create student(
    id int primary key not null,
    `name` varchar(10),
    age int(3) not null
);

ResultSet(结果集)

基本介绍:

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

示例
package khan.louis.jdbc;

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

/**
 * @author XRY
 * @date 2023年04月23日8:41
 */

//测试select语句,返回ResultSet,并取出结果。
public class TestResultSet {
    public static void main(String[] args) throws IOException, SQLException, ClassNotFoundException {
        //获取数据
        Properties properties = new Properties();
        properties.load(new FileInputStream("JDBc\\src\\khan\\louis\\jdbc\\TestResultSet.properties"));
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driver = properties.getProperty("driver");

        //注册驱动
        Class.forName(driver);
        Connection connection = DriverManager.getConnection(url, user, password);

        //操作语句
        String sql = "select `name`, `age`,`id` from student";

        //得到statement
        Statement statement = connection.createStatement();
        //使用executeQuery会返回一个结果集
        ResultSet resultSet = statement.executeQuery(sql);
        /*+--------+-----+----+
        | name   | age | id |
        +--------+-----+----+
        | sea    |  12 |  1 |
        | 路易斯  |  18 |  2 |
        | louis  |  12 |  4 |
        | khan   |  18 |  5 |
        +--------+-----+----+
        4 rows in set (0.02 sec)*/
        //使用while循环取出数据
        while(resultSet.next()){//让光标向后移动,如果没有更多行,则返回false
            String name = resultSet.getString(1);//获取该行的第一列
            int age = resultSet.getInt(2);//获取第二列
            int id = resultSet.getInt(3);
            System.out.println(name + "\t" + age + "\t" + id);
            //结果
            /*sea   12  1
            路易斯 18  2
            louis   12  4
            khan    18  5*/
        }
        //关闭连接
        resultSet.close();
        statement.close();
        connection.close();
    }
}

Statement

基本介绍:

1、Statement对象用于执行静态SQL语句并返回其生成的结果的对象
2、在连接建立后,需要对数据库进行访问时、执行命令或是SQL语句,可以通过
Statement(存在sql注入)
PreparedStatement(预处理)
CallableStatement(存储过程)
3、Statement对象执行SQL语句,存在SQL注入风险

JDBC相关知识_第4张图片

SQL注入:是指利用某些系统没有对用户输入的内容进行充分的调查,而在用户输入数据中注入非法的SQL语句段或命令,恶意攻击数据库。
sql_injection.sql
想要防范SQL注入,只要用PreparedStatement(从Statement扩展而来)取代Statement就可以了。

SQL注入
use testjdbc;
-- SQL注入示例
create table test(
    `name` varchar(10) not null UNIQUE,
    `pwd` varchar(18) not null default ' ')character set utf8;
    
-- 添加数据
insert into test values('louis', '121212');

-- 查询某个管理员是否存在(正常)
select * from test where name = 'louis' and pwd = '121212';

-- sql注入
-- 输入用户名为 1' OR
-- 输入密码为 or '1' = '1
select * from test where name = '1' OR' AND pwd = ' OR '1' = '1';


-- 结果
louis   121212
java 程序测试注入
package khan.louis.jdbc;

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

/**
 * @author XRY
 * @date 2023年04月23日10:37
 */

//测试statement注入问题
public class TestInjectSQL {
    public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
        //让用户输入管理员名和密码
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入管理员的名字!!");
        String test_name = scanner.nextLine();//使用next()会在空格处切断
        System.out.println("请输入密码!!");
        String test_pwd = scanner.nextLine();


        Properties properties = new Properties();
        properties.load(new FileInputStream("JDBc\\src\\khan\\louis\\jdbc\\TestResultSet.properties"));
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driver = properties.getProperty("driver");

        Class.forName(driver);
        Connection connection = DriverManager.getConnection(url, user, password);
        /*sql语句*/
        String sql = "select `name`, `pwd` from test where `name` = '"+ test_name + "' and pwd = '"+test_pwd+"';";

        Statement statement = connection.createStatement();
        ResultSet resultSet = statement.executeQuery(sql);
        if(resultSet.next()){//如果查询到记录,则说明该用户存在
            System.out.println("登录成功!");
        }else{
            System.out.println("登录失败!");
        }

        //结果
        /*1.
        * 正常输入:
        *请输入管理员的名字!!
        louis
        请输入密码!!
        121212
        登录成功!
        *
        * 2.注入
        * 请输入管理员的名字!!
        1' or
        请输入密码!!
        or '1' = '1
        登录成功!
        * */

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

为了解决Statement的注入问题,我们可以使用PreparedStatement代替。


PreparedStatement(预处理查询)

基本介绍

1、PreparedStatement执行的SQL语句中的参数用?来表示,调用PreparedStatement对象的setXXX()方法来设置这些参数。setXXX()方法有两个参数,第一个参数是要设置的SQL语句中的索引(从1开始),第二个是设置的SQL语句中的参数的值。

String sql = "select count(1) from test where name = ? and pwd = ?;"

2、调用executeQuery(),返回ResultSet对象。
3、调用executeUpdate();执行更新,包括增、删、修改。返回的是一个int,影响的行数。

预处理的好处:

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

测试preparedStatement executeQuery();查询
package khan.louis.jdbc;

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

/**
 * @author XRY
 * @date 2023年04月23日13:09
 */

//测试PreparedStatement使用
public class TestPreparedStatement {
    public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
        //让用户输入管理员名和密码
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入管理员的名字!!");
        String test_name = scanner.nextLine();//使用next()会在空格处切断
        System.out.println("请输入密码!!");
        String test_pwd = scanner.nextLine();


        Properties properties = new Properties();
        properties.load(new 			FileInputStream("JDBc\\src\\khan\\louis\\jdbc\\TestResultSet.properties"));
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driver = properties.getProperty("driver");

        Class.forName(driver);
        Connection connection = DriverManager.getConnection(url, user, password);
        /*sql语句 sql语句中的?相当于占位符*/
        String sql = "select `name`, `pwd` from test where `name` = ? and pwd = ?;";

        //preparedStatement对象实现了PreparedStatement接口的实现类的对象。
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //给?赋值
        preparedStatement.setString(1, test_name);
        preparedStatement.setString(2, test_pwd);
//        Statement statement = connection.createStatement();
        //执行select语句。
        ResultSet resultSet = preparedStatement.executeQuery();
        //在上面已经关联过了,所以在执行的时候不需要再写,要是写入就是带?的内容
        if(resultSet.next()){//如果查询到记录,则说明该用户存在
            System.out.println("登录成功!");
        }else{
            System.out.println("登录失败!");
        }
        /*
        * 1、正常输入
        * 请输入管理员的名字!!
        louis
        请输入密码!!
        121212
        登录成功!
        * 2、注入
        * 请输入管理员的名字!!
        1' or
        请输入密码!!
        or '1' = '1
        登录失败!
        * */
        
       //关闭连接
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }
}
测试preparedStatement executeUpdate();增、删、改
package khan.louis.jdbc;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;
import java.util.Scanner;

/**
 * @author XRY
 * @date 2023年04月23日18:16
 */
public class TestExecuteUpdate {
    public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
        //让用户输入管理员名和密码
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入管理员的名字!!");
        String test_name = scanner.nextLine();//使用next()会在空格处切断
        System.out.println("请输入密码!!");
        String test_pwd = scanner.nextLine();

        Properties properties = new Properties();
        properties.load(new FileInputStream("JDBc\\src\\khan\\louis\\jdbc\\TestResultSet.properties"));
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driver = properties.getProperty("driver");

        Class.forName(driver);
        Connection connection = DriverManager.getConnection(url, user, password);

        /*添加记录*/
        String sql = "insert into test values(?, ?)";

        //preparedStatement对象实现了PreparedStatement接口的实现类的对象。
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //给?赋值
        preparedStatement.setString(1, test_name);
        preparedStatement.setString(2, test_pwd);
//        Statement statement = connection.createStatement();
        //执行select语句。
        int result = preparedStatement.executeUpdate();
        System.out.println(result>0?"成功":"失败");
        /*请输入管理员的名字!!
        khan
        请输入密码!!
        1234
        成功*/

        //关闭连接
        preparedStatement.close();
        connection.close();

    }
}
练习

1、创建demo表使用PreparedStatement添加5条数据
2、 修改Alex的记录,将name 改为king
3、删除一条记录
4、查询全部记录,并显示在控制台

  • (一)
package khan.louis.self_testjdbc;

import javax.swing.text.AbstractDocument;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;
import java.util.Scanner;

/**
 * @author XRY
 * @date 2023年04月23日18:30
 */

public class TestPreparedStatement {
    public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
        content c = new content();
        Scanner scanner = new Scanner(System.in);

        //1、读取数据
        Properties properties = new Properties();
        properties.load(new FileInputStream("JDBc\\src\\khan\\louis\\self_testjdbc\\TestPreparedStatement.properties"));
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driver = properties.getProperty("driver");
        //2、注册驱动
        Class.forName(driver);
        //3、获取连接
        Connection connection = DriverManager.getConnection(url, user, password);
        //4、sql语句
        String sql = "insert into demo values(?,?,?)";

        //5、创建preparedStatement对象
        PreparedStatement preparedStatement = null;
        int result = 0;
        for (int i = 0; i < 3; i++) {
            preparedStatement = connection.prepareStatement(sql);
            System.out.println("请输入id");
            c.setId(scanner.nextInt());
            System.out.println("请输入姓名");
            c.setName(scanner.next());
            System.out.println("请输入性别");
            c.setGender(scanner.next());
            preparedStatement.setInt(1, c.getId());
            preparedStatement.setString(2, c.getName());
            preparedStatement.setString(3, c.getGender());
            result = preparedStatement.executeUpdate();
        }
        //查看结果
        System.out.println(result>0?"成功":"失败");
        //断开连接
        preparedStatement.close();
        connection.close();
    }
}

//定义输入内容
class content{
    private int id;
    private String name;
    private String gender;

    public content() {
    }

    public content(int id, String name, String gender) {
        this.id = id;
        this.name = name;
        this.gender = gender;
    }

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }
}
//sql语句
use testjdbc;

create table demo(
    id int primary key not null,
    `name` varchar(10),
    `age` varchar(3),
    `gender` char(1)
);

alter table demo drop column age;
select * from demo;

结果

在这里插入图片描述

  • (二)
package khan.louis.self_testjdbc;

import javax.swing.text.AbstractDocument;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;
import java.util.Scanner;

/**
 * @author XRY
 * @date 2023年04月23日18:30
 */

public class TestPreparedStatement {
    public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要修改姓名");
        String name1 = scanner.next();
        System.out.println("请输入改为姓名");
        String name2 = scanner.next();

        //1、读取数据
        Properties properties = new Properties();
        properties.load(new FileInputStream("JDBc\\src\\khan\\louis\\self_testjdbc\\TestPreparedStatement.properties"));
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driver = properties.getProperty("driver");
        //2、注册驱动
        Class.forName(driver);
        //3、获取连接
        Connection connection = DriverManager.getConnection(url, user, password);
        //4、sql语句
        String sql = "update demo set `name` = ? where `name` = ?;";

        //5、创建preparedStatement对象
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, name2);
            preparedStatement.setString(2, name1);
            int result = preparedStatement.executeUpdate();


        //查看结果
        System.out.println(result>0?"成功":"失败");
        //断开连接
        preparedStatement.close();
        connection.close();
    }
}

结果

在这里插入图片描述

JDBC相关知识_第5张图片
JDBC相关知识_第6张图片

  • (三)
package khan.louis.self_testjdbc;

import javax.swing.text.AbstractDocument;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;
import java.util.Scanner;

/**
 * @author XRY
 * @date 2023年04月23日18:30
 */

public class TestPreparedStatement {
    public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要修改姓名");
        String name1 = scanner.next();

        //1、读取数据
        Properties properties = new Properties();
        properties.load(new FileInputStream("JDBc\\src\\khan\\louis\\self_testjdbc\\TestPreparedStatement.properties"));
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driver = properties.getProperty("driver");
        //2、注册驱动
        Class.forName(driver);
        //3、获取连接
        Connection connection = DriverManager.getConnection(url, user, password);
        //4、sql语句
        String sql = "delete from demo where `name` = ?";

        //5、创建preparedStatement对象
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, name1);
            int result = preparedStatement.executeUpdate();


        //查看结果
        System.out.println(result>0?"成功":"失败");
        //断开连接
        preparedStatement.close();
        connection.close();
    }
}
/*
请输入要修改姓名
Jet
成功*/
  • (四)
package khan.louis.self_testjdbc;

import javax.swing.text.AbstractDocument;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
import java.util.Scanner;

/**
 * @author XRY
 * @date 2023年04月23日18:30
 */

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

        //1、读取数据
        Properties properties = new Properties();
        properties.load(new FileInputStream("JDBc\\src\\khan\\louis\\self_testjdbc\\TestPreparedStatement.properties"));
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driver = properties.getProperty("driver");
        
        //2、注册驱动
        Class.forName(driver);
        
        //3、获取连接
        Connection connection = DriverManager.getConnection(url, user, password);
        
        //4、sql语句
        String sql = "select * from demo;";

        //5、创建preparedStatement对象
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        ResultSet resultSet = preparedStatement.executeQuery();

        //查看结果
        while(resultSet.next()){
            int id = resultSet.getInt(1);
            String name = resultSet.getString(2);
            String gender = resultSet.getString(3);
            System.out.println(id + "\t" + name + "\t" + gender);

        }
        
        //断开连接
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }
}

//结果:
/*1 khan    男
2   Louis   男
3   king    中
4   KhanLouis   女*/

封装JDBCUtils

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

package khan.louis.jdbc.jdbcutils;

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

/**
 * @author XRY
 * @date 2023年04月24日7:33
 */
//这是一个工具类,完成mysql的连接和关闭资源
public class JDBCUtils {
    //定义相关的属性(4个),因为只需要一份,因此我们做出static
private static String user;//用户名
private static String password;//密码
private static String url;//url
private static String driver;//驱动名
//在static代码块去初始化
static{
    Properties properties = new Properties();
    try {
        properties.load(new FileInputStream("JDBc\\khan\\louis\\jdbc\\jdbcutils\\JDBCUtils.properties"));
        ///读取相关的属性值
        user = properties.getProperty("user");
        password = properties.getProperty("password");
        url = properties.getProperty("url");
        driver = properties.getProperty("driver");
    } catch (IOException e) {
        throw new RuntimeException(e);
        }
    }
    //连接数据库, 返回Connection
    public static Connection getConnection(){

        try {
            return DriverManager.getConnection(url, user, password);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    //关闭资源
    /*
    * 1.ResultSet 结果集
    * 2、Statement或者PreparedStatement
    * 3.Connection
    * 4.如果需要关闭资源,就传入对象,否则就传入null*/
    public static void close(ResultSet set, Statement statement, Connection connection){
        //判断是否为空
        try {
            if(set != null){
                set.close();
            }
            if(statement != null){
                statement.close();
            }
            if(connection != null){
                connection.close();
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }

    }
}

测试JDBCUtils

package khan.louis.jdbc.jdbcutils;

import org.junit.Test;

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

/**
 * @author XRY
 * @date 2023年04月24日7:57
 */
//测试JDBCUtils工具类,完成DML 和 SELECT
public class TestJDBCUtils {
    public static void main(String[] args) {
        TestJDBCUtils testJDBCUtils = new TestJDBCUtils();
        testJDBCUtils.testDML();
    }
    public void testDML(){
        //DML主要是insert, update, delete

        //1、得到链接
        Connection connection = null;
        
        //2、组织sql
        String sql = "update demo set `name` = ? where id = ?";
        
        //3、创建PreparedStatement对象
        PreparedStatement preparedStatement = null;
        try {
            connection = JDBCUtils.getConnection();
            preparedStatement = connection.prepareStatement(sql);
            //给占位符赋值
            preparedStatement.setString(1,"Alex");
            preparedStatement.setInt(2, 4);
            //执行
            int result = preparedStatement.executeUpdate();
            System.out.println(result>0? "成功":"失败");
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            JDBCUtils.close(null, preparedStatement, connection);
        }
    }
}
//结果
/*
成功*/

事务

介绍

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

银行转账案例

没有使用事务
package khan.louis.jdbc.transaction;

import khan.louis.jdbc.jdbcutils.JDBCUtils;

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

/**
 * @author XRY
 * @date 2023年04月24日13:08
 */
public class NoTransaction {

    public void testDML(){
        //获得连接
        Connection connection = JDBCUtils.getConnection();
        PreparedStatement preparedStatement = null;
        //创建sql语句
        String sql = "update account set money = money - 100 where `name` ='khan'";
        String sql2 = "update account set money = money + 100 where `name` ='louis'";
        try {
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.executeUpdate();//执行第一条sql
            int i = 1 /0;//抛出异常导致数据不一致
            preparedStatement = connection.prepareStatement(sql2);
            preparedStatement.executeUpdate();

        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            JDBCUtils.close(null, preparedStatement, connection);
        }
    }
}
事务使用
public void testDMLTran(){
        //获得连接
        Connection connection = null;

        PreparedStatement preparedStatement = null;
        //创建sql语句
        String sql = "update `account` set money = money - 100 where `name` ='khan'";
        String sql2 = "update account set money = money + 100 where `name` ='louis'";
        try {
            connection = JDBCUtils.getConnection();
            connection.setAutoCommit(false);
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.executeUpdate();//执行第一条sql
            int i = 1 /0;//抛出异常导致数据不一致
            preparedStatement = connection.prepareStatement(sql2);
            preparedStatement.executeUpdate();

            connection.commit();
        } catch (Exception e) {
            //这里我们可以进行回滚,即撤销执行的sql,默认回滚到事务开始的地方
            System.out.println("执行发生了异常,撤销执行的sql");
            try {
                connection.rollback();
            } catch (SQLException ex) {
                throw new RuntimeException(ex);
            }
            throw new RuntimeException(e);
        } finally {
            JDBCUtils.close(null, preparedStatement, connection);
        }
    }

批处理

基本介绍

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

方法 说明
addBatch() 添加需要批量处理的SQL语句或参数
executeBatch() 执行批量处理语句
clearBatch() 清空批处理包的语句

3、JDBC连接MySQL时,如果需要使用批处理功能,请再url中加参数:

?rewriteBatchedStatements = true

在这里插入图片描述

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

示例

向student表中添加5000条数据,对比使用批处理和正常处理耗时分别多久

package khan.louis.jdbc.batch;

import khan.louis.jdbc.jdbcutils.JDBCUtils;
import org.junit.Test;

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

/**
 * @author XRY
 * @date 2023年04月24日20:22
 */
//演示java的批处理
public class Batch_ {

    //传统方法,添加5000条数据到student中
    @Test
    public void noBatch() throws Exception {
        Connection connection = JDBCUtils.getConnection();
        String sql = "insert into student(id,  name, age) values(?,?,?)";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        System.out.println("开始执行");
        long start = System.currentTimeMillis();
        for (int i = 0; i < 5000; i++) {
            preparedStatement.setInt(1,i+10);
            preparedStatement.setString(2,"Louis" + i);
            preparedStatement.setString(3,"1688");
            preparedStatement.executeUpdate();
        }
        System.out.println("结束执行");
        long end = System.currentTimeMillis();
        System.out.println("传统方法耗时+"+ (end-start));
       //关闭连接
       JDBCUtils.close(null, preparedStatement, connection);
    }
    //结果:
    /*开始执行
    结束执行
    传统方法耗时+4003*/

    //使用批量方式添加数据
    @Test
    public void batch() throws Exception {
        Connection connection = JDBCUtils.getConnection();
        String sql = "insert into student(id,  name, age) values(?,?,?)";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        System.out.println("开始执行");
        long start = System.currentTimeMillis();
        for (int i = 0; i < 5000; i++) {
            preparedStatement.setInt(1,i+10);
            preparedStatement.setString(2,"Louis" + i);
            preparedStatement.setString(3,"1688");
            //将sql语句加入到批处理包中
            preparedStatement.addBatch();
            //当有有1000条记录时,批量执行
            if((i + 1) %1000 == 0){//满1000条数据
                preparedStatement.executeBatch();
                //清空
                preparedStatement.clearBatch();
            }
        }
        System.out.println("结束执行");
        long end = System.currentTimeMillis();
        System.out.println("批量方式耗时+"+ (end-start));
        //关闭连接
        JDBCUtils.close(null, preparedStatement, connection);
        }
        //结果
        /*开始执行
        结束执行
        批量方式耗时+138*/
}

注意:需要在url后加入?rewriteBatchedStatements = true

数据库连接池

传统获取Connectio的问题

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

使用数据库连接池可以解决上述存在的问题。

基本介绍

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

JDBC相关知识_第7张图片

数据库连接池的种类

JDBC的数据库连接池使用javax.sql.DataSource来表示,DataSource只是一个接口,该接口通常由第三方提供实现[提供相应的jar包]。

名称 特点
C3P0数据库连接池 速度相对较慢,稳定性不错(hibernate,spring)
DBCP数据库连接池 速度相对C3P0较快,但不稳定
Proxool数据库连接池 有监控连接池的功能,稳定性较C3P0差一点
BoneCP数据库连接池 速度快
Druid(德鲁伊)数据库连接池 它是阿里提供的,集DBCP,C3P0,Proxool优点于一身的数据库连接池。

C3P0数据库连接池

package khan.louis.jdbc.c3p0;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.sql.Connection;
import java.util.Properties;

/**
 * @author XRY
 * @date 2023年04月25日16:16
 */
//演示c3p0的使用
public class TestC {
    //方式1:相关参数,在程序中指定user url password等
    @Test
    public void testC3P0_01() throws Exception {

        //1.创建一个数据源对象
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
       
        //2.通过配置文件mysql.properties获取相关的连接信息
        Properties properties = new Properties();
        properties.load(new FileInputStream("JDBCUtils.properties"));
       
        ///读取相关的属性值
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driver = properties.getProperty("driver");

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


        //设置数据源的连接数
        //初始化连接数
        comboPooledDataSource.setInitialPoolSize(10);
        //最大连接数
        comboPooledDataSource.setMaxPoolSize(50);
        Connection connection = comboPooledDataSource.getConnection();//这个方法就是从DataSource接口实现
        System.out.println("连接成功!!");
        connection.close();
    }
}

此外还可以使用配置文件的方式来连接 。

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

package khan.louis.jdbc.druid;

import com.alibaba.druid.pool.DruidAbstractDataSource;
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 XRY
 * @date 2023年04月25日18:19
 */
public class TestDruid {

    @Test
    public void testDruid() throws Exception {
        //1、加入Druid jar包
        //2、加入配置文件,将该文件拷贝到项目的src目录
        //3、创建properties对象用来读取配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\druid.properties"));
        
        //4、创建一个指定参数的数据库连接池, Druid数据库连接池
//        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//        Connection connection = dataSource.getConnection();
//        System.out.println("连接成功");
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        
        //5、测试效率
        System.out.println("开始测试");
        long start = System.currentTimeMillis();
        for (int i = 0; i < 5000; i++) {
            Connection connection = dataSource.getConnection();
            connection.close();
//            System.out.println("连接成功");
        }
        long end = System.currentTimeMillis();
        System.out.println("Druid连接池 耗时==》" + (end - start));

            /*开始测试
            4月 25, 2023 6:51:42 下午 com.alibaba.druid.pool.DruidDataSource info
            信息: {dataSource-1} inited
            Druid连接池 耗时==》365*/
    }
}
德鲁伊工具类
package khan.louis.jdbc.druid;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

/**
 * @author XRY
 * @date 2023年04月25日19:16
 */
//基于Druid数据库连接池的工具类
public class DruidUtils {

    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, PreparedStatement preparedStatement, Connection connection){
        try {
            if(resultSet != null){
                resultSet.close();
            }
            if(preparedStatement != null){
                preparedStatement.close();
            }
            if(connection != null){
                connection.close();
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

Apache-DBUtils

在以前的使用方法中关闭connection后,resultSet结果集无法使用,resultSet不利于数据的管理(只能使用一次), 使用返回的信息也不方便。

基本介绍

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

DBUtils类

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

包含的功能对象
对象 作用
ArrayHandler 把结果集中的第一行数据转成对象数组
ArrayListHandler 把结果集中的没一行数据都转成一个数组,再存到List中
BeanHandler 将结果集中的第一行数据封装到一个对应的JavaBean实例中
BeanListHandler 将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放放到List里
ColumnListHandler 将结果集中某一列的数据存放到List中
KeyedHandler(name) 将结果集中的每行数据都封装到Map里,再把这些Map再存到一个map里,其key为指定的key
MapHandler 将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值
MapListHandler 将结果集中的每一行数据都封装到一个Map里,然会再存放到List
BeanListHandler
package khan.louis.jdbc.druid;

import khan.louis.jdbc.jdbcutils.JDBCUtils;
import khan.louis.jdbc.jdbcutils.TestJDBCUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.junit.Test;

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

/**
 * @author XRY
 * @date 2023年04月25日20:29
 */
public class TestDBUtils {

    //使用apache-DBUtils 工具类 + Druid完成对表的crud操作
    @Test
    public void testQueryMany() throws SQLException {
        //返回结果是多行的情况

        //1、得到连接
        Connection connection = DruidUtils.getConnection();
        //2、使用DBUtils的类和接口
        //2.1、引入DBUtils相关的jar文件
        //2.2、创建QueryRunner
        QueryRunner queryRunner = new QueryRunner();
        //3、就可以执行相关的方法,返回ArrayList结果集
        //3.1、定义查询语句
        String sql = "select * from student where id = ?";

        /*query方法就是执行sql语句,得到resultset --- 封装到ArrayList集合中
        * 返回集合
        * connection:连接
        * 执行sql语句
        * new BeanListHandler<>(Student.class):在将resultset取出弄成一个Student对象,封装到ArrayList
        * (使用Student.class的原因是,以上的对象底层会使用到反射机制,查看获取Student类的属性,然后进行封装)
        * 1 就是给sql语句中的?赋值的,可以有多个值,因为是可变参数*/

        //底层得到的resultset会在query关闭, 还会关闭preparedStatement
        List<Student> list = queryRunner.query(connection, sql, new BeanListHandler<>(Student.class), 400);

        System.out.println("输出结合的信息");
        for(Student student:list){
            System.out.println(student);
        }
        //释放资源
        DruidUtils.close(null,null, connection);

    }
}
        //结果:
        /*4月 25, 2023 9:01:16 下午 com.alibaba.druid.pool.DruidDataSource info
        信息: {dataSource-1} inited
        输出结合的信息
        Student{id=400, name='Louis390', age=1688}*/
使用DBUtils + 数据库连接池(德鲁伊)方式,完成对Student的crud
@Test
public void testQuerySingle() throws SQLException {
    //1、得到连接
    Connection connection = DruidUtils.getConnection();
    QueryRunner queryRunner = new QueryRunner();
    String sql = "select * from Student where id = ?";
    //返回单个对象,使用的Handler是BeanHandler
    Student query = queryRunner.query(connection, sql, new BeanHandler<>(Student.class), 100);
    System.out.println(query);

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

    //结果
    /*4月 26, 2023 8:54:51 上午 com.alibaba.druid.pool.DruidDataSource info
    信息: {dataSource-1} inited
    Student{id=100, name='Louis90', age=1688}*/
}
使用apache DBUtils + Druid完成DML(增删改操作)
@Test
public void testDML() throws SQLException {
    //1、得到连接
    Connection connection = DruidUtils.getConnection();
    QueryRunner queryRunner = new QueryRunner();

    String sql = "update student set name = ? where id = ?";


    //返回单个对象,使用的Handler是BeanHandler
    int row = queryRunner.update(connection, sql, "louis300", 300);
    //执行DML操作执行的方法是update,返回值是受影响的行数
    System.out.println(row > 0?"执行成功":"执行没有影响到行数");

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

    //结果
    /*4月 26, 2023 9:07:24 上午 com.alibaba.druid.pool.DruidDataSource info
    信息: {dataSource-1} inited
    执行成功*/
}

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

apache-DBUtils + Druid 简化了开发,但还有不足

1、SQL语句是固定的,不能够通过参数传入,通用性不好,需要进行改进,更方便执行增删改查
2、对于select操作,若果有返回值,返回类型不能固定,需要使用泛型
3、将来的表很多,业务需求复杂,不可能只靠一个Java类完成。

DAO:data access object(数据访问对象)

示例

以Student表为例
我们新建如下的目录结构:

JDBC相关知识_第8张图片

首先我们需要先创建DruidUtils
package khan.louis.jdbc.dao.utils;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

/**
 * @author XRY
 * @date 2023年04月25日19:16
 */
//基于Druid数据库连接池的工具类
public class DruidUtils {

    private static DataSource ds;
    //在静态代码块完成ds的初始化
    static{
        Properties properties = new Properties();
        try {
            properties.load(new FileInputStream("src\\druid.properties"));
            ds = DruidDataSourceFactory.createDataSource(properties);//DataSource类型
        } 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, PreparedStatement preparedStatement, Connection connection){
        try {
            if(resultSet != null){
                resultSet.close();
            }
            if(preparedStatement != null){
                preparedStatement.close();
            }
            if(connection != null){
                connection.close();
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

}

//注意src\\druid.properties包含的是你的连接信息附件,可将德鲁伊的附件导入到文件中,修改相关参数即可
创建domain下面的student类(注意,该类需要和数据库种的student表对应)
package khan.louis.jdbc.dao.domain;

/**
 * @author XRY
 * @date 2023年04月25日20:42
 */
public class Student {
    private Integer id;
    private String name;
    private Integer age;

    public Student(){

    }
    public Student(Integer id, String name, Integer age){
        this.id = id;
        this.name = name;
        this.age = age;
    }

    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 Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
student表
//studdent表
create table student(
    id int primary key,
    `name` varchar(10) not null,
    age int(10) not null
);
创建basicDao和studentDao
basicDao
package khan.louis.jdbc.dao.basicdao;

import khan.louis.jdbc.druid.DruidUtils;
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;
import java.util.Objects;

/**
 * @author XRY
 * @date 2023年04月26日18:51
 */

//开发BasicDao是其他DAO的父类
    //BasicDao表示一个泛型,之后在使用的时候就可以确定那个类操作这个泛型, 泛型指定具体的类型
public class BasicDao<T> {
   private QueryRunner qr = new QueryRunner();
   //开发通用的dml方法,针对任意的表
    public int update(String sql, Object... parameters){
        //拿到了sql语句,所以我们需要一个连接
        Connection connection = null;

        //通过Druid找到连接
        try {
            connection = DruidUtils.getConnection();

            int update = qr.update(connection, sql, parameters);
            return update;//返回受影响的行数
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            DruidUtils.close(null, null, connection);
        }
    }

    //返回多个对象,即查询的结果是多行,针对任意表

    /**
     *
     * @param sql--->sql语句, 可以有?
     * @param clazz---->传入一个Class对象 比如Student.class
     * @param parameters--->传入?的具体值,可以是多个
     * @return --->更具class 返回对应的ArrayList结合
     */

    public List<T> queryMulti(String sql, Class<T> clazz, Object...parameters){
        Connection connection = null;

        try {
            connection = DruidUtils.getConnection();
            List<T> query = qr.query(connection, sql, new BeanListHandler<T>(clazz), parameters);
            return query;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            DruidUtils.close(null, null, connection);
        }

    }


    //查询单行的通用方法
    public T querySingle(String sql, Class<T> clazz, Object... parameters){
        Connection connection = null;
        try {
            connection = DruidUtils.getConnection();
            T query = qr.query(connection, sql, new BeanHandler<T>(clazz), parameters);
            return query;

        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            DruidUtils.close(null, null, connection);
        }
    }

    //查询单行单例的方法,即返回单值的方法
    public Object queryScalar(String sql, Object... parameters){
        Connection connection = null;

        try {
            connection = DruidUtils.getConnection();
            return qr.query(connection, sql, new ScalarHandler(), parameters);

        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            DruidUtils.close(null, null, connection);
        }
    }
}
StudentDao
package khan.louis.jdbc.dao.basicdao;

import khan.louis.jdbc.dao.basicdao.BasicDao;
import khan.louis.jdbc.dao.domain.Student;

/**
 * @author XRY
 * @date 2023年04月26日19:30
 */
public class StudentDao extends BasicDao<Student> {
    //1.Student继承了BasicDao的方法
    //2.根据业务需求,可以编写特有的方法
}
最后就是使用环节TestStudentDao
package khan.louis.jdbc.dao.test;

import khan.louis.jdbc.dao.basicdao.StudentDao;
import khan.louis.jdbc.dao.domain.Student;
import org.junit.Test;

import java.util.List;

/**
 * @author XRY
 * @date 2023年04月26日19:33
 */

//测试StudentDao对student的crud操作
public class TestStudentDao {
    @Test
    public void testStudentDao(){
        StudentDao studentDao = new StudentDao();
        //1.查询
        List<Student> students = studentDao.queryMulti("select * from student where id > ? and id < ?", Student.class, 200, 205);
        for(Student stu:students){
            System.out.println(stu);
        }
        //2.查询单条记录
        Student student = studentDao.querySingle("select * from student where id = ?", Student.class, 100);
        System.out.println(student);

        //3.查询单行单列
        Object result = studentDao.queryScalar("select name from student where id = ?", 500);
        System.out.println(result);

        //4.dml操作
        int Rresult = studentDao.update("insert into student values(?,?,?)", 1, "louis", 35);
        System.out.println(Rresult > 0?"执行成功":"执行没有影响表");
    }
}

你可能感兴趣的:(JavaWeb,java,数据库,mysql)