JDBC(powernode 文档)(内含源代码)

news2024/9/22 7:07:01

源代码下载地址链接:https://download.csdn.net/download/weixin_46411355/87400304

目录

JDBC概述

1.1 前言

1.2 什么是JDBC

1.3 JDBC的原理

1.4 程序员,JDBC,JDBC驱动的关系及说明

1.4.1 JDBC API

1.4.2 JDBC 驱动

1.4.3 Java程序员

1.4.4 三方关系

1.5 总结

2.JDBC操作数据库的步骤

2.1 总体步骤

2.2 详细步骤

2.2.1 官网下载驱动包

2.2.2 加载驱动

2.2.3 创建数据库连接

2.2.4 创建Statement并发送命令

2.2.5 处理ResultSet结果

2.2.6 关闭数据库资源

3.准备工作

3.1 创建数据库并创建student表

3.2 创建项目

3.3 创建lib目录并引入MYSQL驱动包

3.4 把lib包引入项目环境

4.使用JDBC完成添加操作

4.1 步骤

4.2 代码

4.3 URL详解

4.3.1 为什么要定义URL

4.4查看数据库

4.4.1 一个URL由哪些部分组成

5.使用JDBC完成更新和删除

5.1步骤

5.2 修改代码

5.3 删除代码

6.DBUtils的简单封装

6.1 为什么要封装

6.2 创建DBUtils封装代码

6.3 创建上面的工具类对象,前面的代码进行改造

7.使用JDBC完成查询

7.1 循环向student表里面插入20条数

 7.2 查询

 8.使用JDBC完成分页查询

9.常用接口详解

9.1 DriverManager

9.2 Connection

9.3 Statement

9.4 PreparedStatement

10.SQL注入问题

10.1问题引入

10.1.1 创建sys_user表并初始化数据

10.1.2 编写代码实现登陆

10.1.3 测试登陆

10.2 解决办法【使用PreparedStatement】

10.2.1 技术原理

10.2.2 创建对象

10.2.3 传递参数

10.2.4 修改登录代码

10.2.5 再次测试

11.事务处理解决转账问题

11.1 什么是事务

11.2事务的四大特性:

11.2.1 原子性

11.2.2一致性

11.2.3 隔离性

11.2.4 持久性

11.3 需求描述

11.4 准备工作

11.4.1 创建表

11.4.2 初始化数据

11.5 代码实现

 11.6 测试

12.JDBC批处理

12.1 什么是批处理

12.2需求描述及操作步骤

12.3 案例代码

13.连接池

13.1 什么是连接池

13.2 为什么使用连接池

13.3 连接池的工作原理

13.4我们如何手写【难点】

13.4.1编写说明

13.4.2 创建jdbc.properties

13.4.3 封装一个连接类

13.4.4 创建一个连接池接口

13.4.5 创建一个连接池实现类以及对应方法

13.4.6 创建一个连接池的维护类

13.4.7 案例测试

 13.5 市面上有哪些可用的连接池

14.BaseDAO的封装【难点】

14.1 概述

14.2 BaseDao代码

14.3 UserDao子类

14.4 测试类

14.5 分页封装

14.5.1 创建分页结果封装类PageInfo

14.5.2 修改BaseDao

14.5.3 测试类


                                                        【JDBC】

主要内容

  1. JDBC概述
  2. 使用JDBC完成添加操作
  3. 使用JDBC完成更新和删除
  4. DBUtils的简单封装
  5. 使用JDBC完成查询
  6. 使用JDBC完成分页查询
  7. 常用接口详解
  8. JDBC批处理
  9. SQL注入问题
  10. 事务处理解决转账问题
  11. 数据库连接池
  12. 使用反射对DBUtils再次的封装
  13. BaseDAO的封装
  14. JdbcTemplate的使用

学习目标

知识点

要求

JDBC概述

掌握

使用JDBC完成添加操作

掌握

使用JDBC完成更新和删除

掌握

DBUtils的简单封装

掌握

使用JDBC完成查询

掌握

使用JDBC完成分页查询

掌握

常用接口详解

掌握

JDBC批处理

掌握

SQL注入问题

掌握

事务处理解决转账问题

掌握

连接池

掌握

使用反射对DBUtils再次的封装

掌握

BaseDAO的封装

掌握

  1. JDBC概述

1.1 前言

学习到现在,我们是如何操作数据库的?

a、使用MySQL自带的命令行的方式进行操作;

b、使用第三方的数据库客户端工具来连接MySQL并且进行操作

但是这些都是一些单独的数据库操作工具,它没法让我们书写的程序自动执行这些工具,那么我们该如何在Java程序中来操作这些数据库呢?

1.2 什么是JDBC

JDBC(Java DataBase Connectivity)就是Java数据库连接,即一套使用Java语言来操作数据库的编程接口,也可以认为是一组规范。

1.3 JDBC的原理

早期SUN公司的天才们想编写一套可以连接天下所有数据库的API,但是当他们刚刚开始时就发现这是不可完成的任务,因为各个厂商的数据库服务器差异太大了。后来SUN开始与数据库厂商们讨论,最终得出的结论是,由SUN提供一套访问数据库的规范(就是一组接口),并提供连接数据库的协议标准,然后各个数据库厂商会遵循SUN的规范提供一套访问自己公司的数据库服务器的API出现。SUN提供规范命名为JDBC,而各个厂商提供的,遵循了JDBC规范的,可以访问自己数据库的API被称之为驱动

小结:

SUN提供一套访问数据库的规范(就是一组接口),规范命名为JDBC,并提供连接数据库的协议标准。

各个数据库厂商会遵循SUN的JDBC规范的,提供一套可以访问自己公司的数据库服务器的API,被称之为驱动。

JDBC是接口,而JDBC驱动才是接口的实现没有驱动无法完成数据库连接!每个数据库厂商都有自己的驱动,用来连接自己公司的数据库。

当然还有第三方公司专门为某一数据库提供驱动,这样的驱动往往不是开源免费的!

1.4 程序员,JDBC,JDBC驱动的关系及说明

1.4.1 JDBC API

提供者:Sun公司

内容:供程序员调用的接口与类,集成在java.sql和javax.sql包中,如

DriverManager类   作用:管理各种不同的JDBC驱动

Connection接口  

Statement接口     

ResultSet接口

1.4.2 JDBC 驱动

提供者:数据库厂商

作用:负责连接各种不同的数据库

1.4.3 Java程序员

JDBC对Java程序员而言是API对实现与数据库连接的服务提供商而言是接口模型

1.4.4 三方关系

SUN公司是规范制定者,制定了规范JDBC(连接数据库规范)

数据库厂商 微软、甲骨文等分别提供实现JDBC接口的驱动jar包

程序员学习JDBC规范来应用这些jar包里的类。

1.5 总结

简单地说,JDBC 可做三件事:与数据库建立连接发送指令操作数据库处理结果

2.JDBC操作数据库的步骤

2.1 总体步骤

  1. 官网下载驱动包
  2. 加载一个Driver驱动
  3. 创建数据库连接(Connection)
  4. 创建SQL命令发送器Statement
  5. 创建SQL
  6. 通过Statement发送SQL命令并得到结果
  7. 处理SQL结果(select语句)
  8. 关闭数据库资源:ResultSet、Statement、Connection

2.2 详细步骤

2.2.1 官网下载驱动包

http://dev.mysql.com/downloads/connector/j/

在Select Operating System:中选择Platform Independent

选择 ZIP Archive 版本 点击Download

 解压后得到 jar 库文件

 

 然后在对应的项目中导入该库文件。

2.2.2 加载驱动

加载JDBC驱动是通过调用方法java.lang.Class.forName(),下面列出常用的几种数据库驱动程序加载语句的形式 :

 注意,如果你的mysql版本是8或者以上的话,那么driver的写法需要改写成:com.mysql.cj.jdbc.Driver

2.2.3 创建数据库连接

与数据库建立连接的方法是调用

DriverManager.getConnection(String url, String user, String password )

方法

Connection conn=null;

String url="jdbc:mysql://localhost:3306/bjpowernode?charsetUnicode=UTF8&serverTimezone=UTC";
String user=“root";
String password=“root";

conn = DriverManager.getConnection(url, user, password);

2.2.4 创建Statement并发送命令

Statement对象用于将 SQL 语句发送到数据库中,或者理解为执行sql语句

有三种 Statement对象:

        Statement:用于执行不带参数的简单SQL语句;

        PreparedStatement(从 Statement 继承):用于执行带或不带参数预编译SQL语句

        CallableStatement(从PreparedStatement 继承):用于执行数据库存储过程的调用。

2.2.5 处理ResultSet结果

  ResultSet对象是executeQuery()方法的返回值,它被称为结果集,它代表符合SQL语句条件的所有行,并且它通过一套getXXX方法(这些get方法可以访问当前行中的不同列)提供了对这些行中数据的访问。

  ResultSet里的数据一行一行排列,每行有多个字段,且有一个记录指针指针所指的数据行叫做当前数据行,我们只能来操作当前的数据行我们如果想要取得某一条记录,就要使用ResultSet的next()方法 ,如果我们想要得到ResultSet里的所有记录,就应该使用while循环

  ResultSet对象自动维护指向当前数据行的游标。每调用一次next()方法,游标向下移动一行

  初始状态下记录指针指向第一条记录的前面通过next()方法指向第一条记录循环完毕后指向最后一条记录的后面。

2.2.6 关闭数据库资源

作为一种好的编程风格,应在不需要Statement对象和Connection对象时显式地关闭它们。关闭Statement对象和Connection对象的语法形式为:

public void close() throws SQLException

用户不必关闭ResultSet。当它的 Statement 关闭、重新执行或用于从多结果序列中获取下一个结果时,该ResultSet将被自动关闭。

注意要按先ResultSet结果集,后Statement,最后Connection的顺序关闭资源因为Statement和ResultSet是需要连接是才可以使用的所以在使用结束之后有可能其他的Statement还需要连接,所以不能先关闭Connection。

3.准备工作

3.1 创建数据库并创建student表

 

3.2 创建项目

3.3 创建lib目录并引入MYSQL驱动包

3.4 把lib包引入项目环境

File->Project Structure

 

 

 点击OK

4.使用JDBC完成添加操作

4.1 步骤

  • 加载MySQL的JDBC驱动
  • 建立数据的连接
  • 创建SQL命令的发送器
  • 编写SQL
  • 使用SQL命令发送器发送SQL命令并得到结果
  • 处理结果
  • 关闭数据库资源

4.2 代码

package com.bjpowernode.jdbc;

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

public class Test01Add {
    //驱动器路径
    private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
    //连接数据库地址
    private static final String URL = "jdbc:mysql://localhost:3306/powernode_jdbc?useUnicode=true&useSSL=false&characterEncoding=UTF8";
    //数据库用户名
    private static final String USER_NAME = "root";
    //数据库密码
    private static final String USER_PASSWORD = "root";

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //加载JDBC访问Mysql的驱动
        Class.forName(DRIVER);
        //建立和数据库的连接
        Connection connection = DriverManager.getConnection(URL, USER_NAME, USER_PASSWORD);
        //创建SQL命令发送器
        Statement statement = connection.createStatement();
        //使用SQL命令发送器发送SQL命令并得到结果
        String insertSql = "insert into student values(1,'小刚','32','男','湖北省武汉市')";
        int resultNum = statement.executeUpdate(insertSql);
        //处理结果
        if(resultNum>0){
            System.out.println("添加成功");
        }else{
            System.out.println("添加用户失败");
        }
        //关闭数据源
        statement.close();
        connection.close();
    }


}

4.3 URL详解

4.3.1 为什么要定义URL

Java和MySQL是厂商的,Java程序和MySQL数据库此时不在同一个进程下,此时Java程序需要向MySQL发送请求。

4.3.2 如何发送请求呢?

jdbc:mysql://localhost:3306/powernode_jdbc?useUnicode=true&useSSL=false&characterEncoding=UTF8

 使用URL的方式发送

jdbc主协议
mysql

子协议

localhost/127.0.0.1

 MySQL服务器的地址,如果服务器就是我自己的主机,那么定义localhost或者127.0.0.1就可以了

3306

MySQL服务器的端口号

powernode_jdbc

MySQL数据库服务器的数据库名称

useUnicode=true

Java和MySQL交互使用Unicode编码

useSSL=false

Java和MySQL交互不使用安全层协议

characterEncoding=UTF8

Java和MySQL交互的编码方式为UTF8  【如果不设置会有乱码的】

4.4查看数据库

 

4.4.1 一个URL由哪些部分组成

  • 协议://服务器主机:端口/服务器路径?查询参数
  • 协议 jdbc:mysql:
  • 服务器主机 localhost
  • 端口 3306
  • 服务器路径 whpowernode
  • 参数useUnicode=true&useSSL=false&characterEncoding=UTF8

5.使用JDBC完成更新和删除

5.1步骤

  • 加载MySQL的JDBC驱动
  • 建立数据的连接
  • 创建SQL命令的发送器
  • 编写SQL
  • 使用SQL命令发送器发送SQL命令并得到结果
  • 处理结果
  • 关闭数据库资源

5.2 修改代码

 
package com.bjpowernode.jdbc.使用JDBC完成更新操作;

import java.sql.*;

public class Test02Update {
    //驱动器路径
    private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
    //连接数据库地址
    private static final String URL = "jdbc:mysql://localhost:3306/powernode_jdbc?useUnicode=true&useSSL=false&characterEncoding=UTF8";
    //数据库用户名
    private static final String USER_NAME = "root";
    //数据库密码
    private static final String USER_PASSWORD = "root";

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //加载MySQL的驱动
        Class.forName(DRIVER);
        //建立数据的连接
        Connection connection = DriverManager.getConnection(URL, USER_NAME, USER_PASSWORD);
        //创建SQL的命令发送器
        Statement statement = connection.createStatement();
        //编写SQL
        String updateSql = "update student set name = '小明',age=23,sex='女',address='四川成都' where id = 1";
        //使用SQL命令发送器发送SQL命令并得到结果
        int resultNum = statement.executeUpdate(updateSql);
        //处理结果
        if(resultNum>0){
            System.out.println("修改成功");
        }else {
            System.out.println("处理失败");
        }
        //关闭数据库资源
        statement.close();
        connection.close();
    }
}

5.3 删除代码

package com.bjpowernode.jdbc._01基本的CRUD.使用JDBC完成删除操作;

import javax.print.DocFlavor;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class Test03Delete {
    // 驱动器路径
    private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
    // 连接数据库地址
    private static final String URL = "jdbc:mysql://localhost:3306/powernode_jdbc?useUnicode=true&characterEncoding=UTF8&useSSL=false";
    //数据库用户名
    private static final String USER_NAME = "root";
    //数据库密码
    private static final String USER_PASSWORD = "root";

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //加载MySQL的驱动
        Class.forName(DRIVER);
        //建立数据库的连接
        Connection connection = DriverManager.getConnection(URL, USER_NAME, USER_PASSWORD);
        //创建SQL命令的发送器
        Statement statement = connection.createStatement();
        //编写SQL
        String deleteSql = "delete from student where id = 1";
        //使用SQL命令发送器发送SQL命令并得到结果
        int resultNum = statement.executeUpdate(deleteSql);
        //处理结果
        if(resultNum>0){
            System.out.println("删除成功");
        }else{
            System.out.println("删除失败");
        }
        //关闭数据库资源
        statement.close();
        connection.close();
    }
}

6.DBUtils的简单封装

6.1 为什么要封装

从我们上面的代码大家可以看到,每一次写我们创建一个连接创建一个发送SQL的对象,最后还要关闭资源,那么我们可以考虑把这重复的代码提取出来!

6.2 创建DBUtils封装代码

package com.bjpowernode.jdbc._02DBUtils的简单封装;

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

public class DBUtils {
    // 驱动器路径
    private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
    // 连接数据库地址
    private static final String URL = "jdbc:mysql://localhost:3306/powernode_jdbc?useUnicode=true&characterEncoding=UTF8&useSSL=false";
    //数据库用户名
    private static final String USER_NAME = "root";
    //数据库密码
    private static final String USER_PASSWORD = "root";

    /**
     * 静态加载驱动程序
     */
    static {
        try {
            Class.forName(DRIVER);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     *
     * @return 连接对象
     */
    public static Connection getConnection(){
        try {
            Connection connection = DriverManager.getConnection(URL, USER_NAME, USER_PASSWORD);
            return connection;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
            System.out.println("创建连接对象异常");
        }
        return null;
    }

    /**
     * 关闭资源
     */
    public static void close(AutoCloseable autoCloseable){
        if(autoCloseable!=null){
            try {
                autoCloseable.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


}

6.3 创建上面的工具类对象,前面的代码进行改造

package com.bjpowernode.jdbc._02DBUtils的简单封装;

import sun.security.pkcs11.Secmod;

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

public class Test01Add {
    public static void main(String[] args) throws SQLException {
        Connection connection = DBUtils.getConnection();
        //创建SQL命令发送器
        Statement statement = connection.createStatement();
        //使用SQL命令发送器发送SQL命令得到结果
        String insertSql = "insert into student values(null,'HHH',23,'男','四川省成都市')";
        int resultNum = statement.executeUpdate(insertSql);
        //处理结果
        if(resultNum>0){
            System.out.println("添加成功");
        }else{
            System.out.println("添加失败");
        }
        //关闭数据库资源
        DBUtils.close(statement);
        DBUtils.close(connection);
    }
}

7.使用JDBC完成查询

7.1 循环向student表里面插入20条数

package com.bjpowernode.jdbc._02DBUtils的简单封装;

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

public class Test01Add20 {
    public static void main(String[] args) throws SQLException {
        Connection connection = DBUtils.getConnection();
        // 创建SQL命令发送器
        Statement statement = connection.createStatement();
        // 使用SQL命令发送器发送SQL命令并得到结果
        Random random = new Random();
        for (int i = 1; i <=20 ; i++) {
            Integer id = null;
            String name = "小明"+i;
            int age = random.nextInt(100);
            String sex = random.nextBoolean()?"男":"女";
            String address = "武汉"+i;

            String insertSql = "insert into student values("+id+",'"+name+"',"+age+",'"+sex+"','"+address+"')";
            int resultRowsNum = statement.executeUpdate(insertSql);
            //处理结果
            if(resultRowsNum>0){
                System.out.println("添加成功");
            }else {
                System.out.println("添加失败");
            }


        }
        //关闭数据源
        DBUtils.close(statement);
        DBUtils.close(connection);
    }
}

 7.2 查询

package com.bjpowernode.jdbc._03使用JDBC完成查询.循环向student表里面插入20条数;

import com.bjpowernode.jdbc._02DBUtils的简单封装.DBUtils;

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

public class Test04Query {
    public static void main(String[] args) throws SQLException {

        Connection connection = DBUtils.getConnection();
        //创建SQL命令发送器
        Statement statement = connection.createStatement();
        //编写SQL语句
        String selectSql = "select * from student";
        //使用SQL命令发送器发送SQL命令并得到结果
        ResultSet resultSet = statement.executeQuery(selectSql);
        //处理结果
        while(resultSet.next()){
            int id = resultSet.getInt(1);
            String name = resultSet.getString(2);
            int age = resultSet.getInt(3);
            String sex = resultSet.getString(4);
            String address = resultSet.getString(5);
            System.out.println(id+" "+name+" "+age+" "+sex+" "+address);
        }
        //关闭数据库资源
        DBUtils.close(resultSet);
        DBUtils.close(statement);
        DBUtils.close(connection);

    }
}

 8.使用JDBC完成分页查询

javabean

Student,java

package com.bjpowernode.jdbc._03使用JDBC完成查询.循环向student表里面插入20条数;

import java.util.Objects;

public class Student {
    private  Integer id;
    private String name;
    private Integer age;
    private String sex;
    private String address;


    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;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return Objects.equals(id, student.id) &&
                Objects.equals(name, student.name) &&
                Objects.equals(age, student.age) &&
                Objects.equals(sex, student.sex) &&
                Objects.equals(address, student.address);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, age, sex, address);
    }
}
 
package com.bjpowernode.jdbc._04使用JDBC完成分页查询;

import com.bjpowernode.jdbc.javabean.Student;
import com.bjpowernode.jdbc.util.DBUtils;

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

public class Test05QueryForPage {
    public static void main(String[] args) throws SQLException {
        Connection connection = DBUtils.getConnection();
        //创建SQL命令发送器
        Statement statement = connection.createStatement();

        int pageNum = 2;//页码
        int pageSize = 5;//每页显示的条数

        int index = (pageNum-1)*pageSize;

        //编写SQL
        String sql = "select * from student limit "+index+","+pageSize;
        //使用SQL命令发送器发送SQL命令并得到结果
        ResultSet resultSet = statement.executeQuery(sql);
        //处理结果
        while (resultSet.next()){
            Student student = new Student();

            int id = resultSet.getInt("id");
            String name = resultSet.getString("name");
            int age = resultSet.getInt("age");
            String sex = resultSet.getString("sex");
            String address = resultSet.getString("address");

            student.setId(id);
            student.setName(name);
            student.setAge(age);
            student.setSex(sex);
            student.setAddress(address);

            System.out.println(student);
        }
        //关闭数据源
        DBUtils.close(resultSet);
        DBUtils.close(statement);
        DBUtils.close(connection);
    }
}

 

9.常用接口详解

9.1 DriverManager

用于管理JDBC驱动的服务类。程序中使用该类的的主要功能是获取Connection对象,该类包含如下方法:

public static Connection getConnection(String url, String user, String password) throws SQLException

该方法获得url对应数据库的连接;

9.2 Connection

代表数据库连接对象每个Connection代表一个物理连接会话要想访问数据库,必须先得到数据库连接。该接口的常用方法如下:

方法

说明

Statement createStatement() throws SQLException;

该方法返回一个Statement对象;

PreparedStatement prepareStatement(String sql)throws SQLException;

该方法返回预编译的Statement对象,即将SQL语句提交到数据库进行预编译;

CallableStatement prepareCall(String sql) throws SQLException;

该方法返回CallableStatement对象,该对象用于调用存储过程

上面3个方法都返回用于执行sql语句的Statement对象,PreparedStatement和CallableStatement是Statement的子类只有获得了Statement之后才可以执行sql语句

除此之外,Connection还有如下几个用于控制事务的方法

方法

说明

Savepoint setSavepoint() throws SQLException;

创建一个保存点;

Savepoint setSavepoint(String name) throws SQLException;

以指定名字来创建一个保存点;

void setTransactionIsolation(int level) throws SQLException;

设置事务的隔离级别;

void rollback() throws SQLException;

回滚事务;

void rollback(Savepoint savepoint) throws SQLException;

将事务回滚到指定的保存点;

void setAutoCommit(boolean autoCommit) throws SQLException;

关闭自动提交,打开事务;

void commit() throws SQLException;

提交事务;

9.3 Statement

用于执行sql语句的工具接口。该对象既可以执行DDL,DCL语句,也可以用于执行DML语句,还可以用于执行sql查询。当执行sql查询时,返回查询到的结果集。

=========================================================================

知识点回顾:

DDL(data definition language):

DDL比DML要多,主要的命令有CREATE、ALTER、DROP等,DDL主要是用在定义或改变表(TABLE)的结构,数据类型,表之间的链接和约束等初始化工作上,他们大多在建立表时使用

DCL(Data Control Language):

数据库控制功能。是用来设置或更改数据库用户或角色权限的语句,包括(grant,deny,revoke等)语句。在默认状态下,只有sysadmin,dbcreator,db_owner或db_securityadmin等人员才有权力执行DCL;

DML(data manipulation(操作) language):

SELECT、UPDATE、INSERT、DELETE,就象它的名字一样,这4条命令是用来对数据库里的数据进行操作的语言

=========================================================================

Statement的常用方法如下:

方法

说明

ResultSet executeQuery(String sql) throws SQLException;

该方法用于执行查询语句并返回查询结果对应ResultSet对象该方法只能用于执行查询语句

int executeUpdate(String sql) throws SQLException

该方法用于执行DML数据操作 crud语句,并返回受影响的行数;

该方法也可用于执行DDL数据定于create、alter、drop语句,执行DDL语句将返回0;

boolean execute(String sql) throws SQLException;

该方法可以执行任何sql语句

如果执行后第一个结果为ResultSet对象,则返回true;

如果执行后第一个结果为受影响的行数或没有任何结果,则返回false;

9.4 PreparedStatement

预编译的Statement对象,PreparedStatement是Statement的子接口它允许数据库预编译sql语句(这些sql语句通常带有参数)以后每次只改变sql命令的参数,避免数据库每次都需要编译sql语句,无需再传入sql语句,只要为预编译的sql语句传入参数值即可。所以它比Statement多了如下方法:

方法

说明

void setXxx(int parameterIndex, Xxx value):

该方法根据传入参数值的类型不同,需要使用不同的方法。传入的值根据索引传给sql语句中指定位置的参数。

9.5 ResultSet

结果集对象。该对象包含访问查询结果的方法ResultSet可以通过列索引列名获得列数据。它包含了如下常用方法来移动记录指针。

方法

说明

void close() throws SQLException;

释放ResultSet对象;

boolean absolute( int row ) throws SQLException;

将结果集的记录指针移动到第row行,

如果row是负数,则移动到倒数第row行,如果移动后的记录指针指向一条有效记录,则该方法返回true;

boolean next() throws SQLException;

将结果集的记录指针定位到下一行,如果移动后的记录指针指向一条有效的记录,则该方法返回true

boolean last() throws SQLException;

将结果集的记录指针定位到最后一行,如果移动后的记录指针指向一条有效的记录,则该方法返回true;

10.SQL注入问题

10.1问题引入

10.1.1 创建sys_user表并初始化数据

 

10.1.2 编写代码实现登陆

javaBean

SysUser.java

 
package com.bjpowernode.jdbc.javabean;

import java.util.Objects;

public class SysUser {
    private Integer id;
    private String username;
    private String password;

    public SysUser(Integer id, String username, String password) {
        this.id = id;
        this.username = username;
        this.password = password;
    }

    public SysUser() {
    }

    public Integer getId() {
        return id;
    }



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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }


    @Override
    public String toString() {
        return "SysUser{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        SysUser sysUser = (SysUser) o;
        return Objects.equals(id, sysUser.id) && Objects.equals(username, sysUser.username) && Objects.equals(password, sysUser.password);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, username, password);
    }
}

Test06Login.java

package com.bjpowernode.jdbc._05SQL注入问题.编写代码实现登录.问题引入;

import com.bjpowernode.jdbc.javabean.SysUser;
import com.bjpowernode.jdbc.util.DBUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;

public class Test06Login {
    public static void main(String[] args) throws SQLException {
        Connection connection = DBUtils.getConnection();
        //创建SQL命令发送器
        Statement statement = connection.createStatement();
        //从键盘输入
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String inputUsername = scanner.nextLine();
        System.out.println("请输入密码:");
        String inputPassword = scanner.nextLine();
        //编写SQL
        String sql = "select * from sys_user where username= '"+inputUsername+"' and password='"+inputPassword+"';";
        System.out.println("sql = " + sql);
        //使用SQL命令发送器发送SQL命令并得到结果
        ResultSet resultSet = statement.executeQuery(sql);
        //处理结果
        if(resultSet.next()){
            System.out.printf("登录成功");
            SysUser sysUser = new SysUser();

            int id = resultSet.getInt("id");
            String username = resultSet.getString("username");
            String password = resultSet.getString("password");

            sysUser.setId(id);
            sysUser.setUsername(username);
            sysUser.setPassword(password);

            System.out.println(sysUser);
        }
        //关闭数据库资源
        DBUtils.close(resultSet);
        DBUtils.close(statement);
        DBUtils.close(connection);

    }
}

10.1.3 测试登陆

10.2 解决办法【使用PreparedStatement】

10.2.1 技术原理

        该 PreparedStatement接口继承Statement并与之在两方面有所不同

        一方面:

                PreparedStatement 实例包含已编译的 SQL 语句。这就是使语句“准备好”

                包含于 PreparedStatement 对象中的 SQL 语句可具有一个或多个 IN 参数IN参数的值在 SQL 语句创建时未被指定。

                相反的,该语句为每个 IN 参数保留一个问号(“?”)作为占位符。每个问号的值必须在该语句执行之前,通过适当的setXXX 方法来提供。

                由于 PreparedStatement 对象已预编译过,所以其执行速度要快于 Statement 对象。因此,多次执行的 SQL 语句经常创建为 PreparedStatement 对象,以提高效率。

        另一方面:

                作为 Statement 的子类,PreparedStatement 继承了 Statement 的所有功能。另外它还添加了一整套方法,用于设置发送给数据库以取代 IN 参数占位符的值同时,三种方法 execute、 executeQuery 和 executeUpdate 已被更改以使之不再需要参数这些方法的 Statement 形式(接受 SQL 语句参数的形式)不应该用于 PreparedStatement 对象

10.2.2 创建对象

以下的代码段(其中 con 是 Connection 对象)创建包含带两个 IN 参数占位符的 SQL 语句的 PreparedStatement 对象:

PreparedStatement pstmt = con.prepareStatement("UPDATE table4 SET m = ? WHERE x = ?");

pstmt 对象包含语句 "UPDATE table4 SET m = ? WHERE x = ?",它已发送给DBMS(数据库管理系统),并为执行作好了准备。

10.2.3 传递参数

在执行 PreparedStatement 对象之前,必须设置每个 ? 参数的值。这可通过调用 setXXX 方法来完成,其中 XXX 是与该参数相应的类型。

例子:

例如,如果参数具有Java 类型 long,则使用的方法就是 setLong。setXXX 方法的第一个参数是要设置的参数的序数位置,第二个参数是设置给该参数的值。例如,以下代码将第一个参数设为 123456789,第二个参数设为 100000000:
 

pstmt.setLong(1, 123456789);
pstmt.setLong(2, 100000000);

一旦设置了给定语句的参数值,就可用它多次执行该语句,直到调用clearParameters 方法清除它为止。在连接的缺省模式下(启用自动提交),当语句完成时将自动提交或还原该语句。

如果基本数据库和驱动程序在语句提交之后仍保持这些语句的打开状态,则同一个 PreparedStatement 可执行多次。如果这一点不成立,那么试图通过使用PreparedStatement 对象代替 Statement 对象来提高性能是没有意义的。

10.2.4 修改登录代码

package com.bjpowernode.jdbc._05SQL注入问题.编写代码实现登录.解决办法;

import com.bjpowernode.jdbc.javabean.SysUser;
import com.bjpowernode.jdbc.util.DBUtils;

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

public class Test07Login {

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

        //从键盘输入
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String inputUsername = scanner.nextLine();
        System.out.println("请输入密码:");
        String inputPassword = scanner.nextLine();

        Connection connection = DBUtils.getConnection();

        //编写SQL
        String sql = "select * from sys_user where username = ? and password = ?";
        System.out.println("sql = " + sql);
        //创建SQL命令发送器
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setString(1,inputUsername);
        preparedStatement.setString(2,inputPassword);
        //使用SQL命令发送器发送SQL命令并得到结果
        ResultSet resultSet = preparedStatement.executeQuery();
        //处理结果
        if(resultSet.next()){
            System.out.printf("登录成功");

            SysUser sysUser = new SysUser();

            int id = resultSet.getInt("id");
            String username = resultSet.getString("username");
            String password = resultSet.getString("password");

            sysUser.setId(id);
            sysUser.setUsername(username);
            sysUser.setPassword(password);

            System.out.println(sysUser);
        }
        // 关闭数据库资源
        DBUtils.close(resultSet);
        DBUtils.close(preparedStatement);
        DBUtils.close(connection);
    }
}

10.2.5 再次测试

11.事务处理解决转账问题

11.1 什么是事务

是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作;这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行;事务是一组不可再分割的操作集合(工作逻辑单元)

11.2事务的四大特性:

11.2.1 原子性

事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做

11.2.2一致性

事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是不一致的状态。

11.2.3 隔离性

一个事务的执行不能其它事务干扰即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。

11.2.4 持久性

也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的接下来的其它操作或故障不应该对其执行结果有任何影响。

11.3 需求描述

完成转账操作

要求,两次数据库操作,要么全成功。要么全失败

11.4 准备工作

11.4.1 创建表

CREATE TABLE account
(
    aid INT PRIMARY KEY AUTO_INCREMENT,
    aname VARCHAR(30) NOT NULL,
    amount  DECIMAL(10,2) NOT NULL
);

11.4.2 初始化数据

11.5 代码实现

package com.bjpowernode.jdbc._06事务处理解决转账问题;

import com.bjpowernode.jdbc.util.DBUtils;

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

public class Test08Transaction {
    public static void main(String[] args) {
        //声明连接对象
        Connection connection = null;
        //声明发送SQL的接口对象
        Statement statement = null;
        try {
            //创建连接对象
            connection = DBUtils.getConnection();
            //关闭自动提交事务
            connection.setAutoCommit(false);
            //编写SQL
            String reducedSql = "update account set amount = amount - 1000 where aid = 1";
            String increasedSql = "update account set amount = amount + 1000 where aid = 2";
            // 创建SQL命令发送器
            statement = connection.createStatement();
            statement.executeUpdate(reducedSql);
            statement.executeUpdate(increasedSql);
            connection.commit();
        }catch (Exception e){
            e.printStackTrace();
            try {
                connection.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }finally {
            //关闭数据库资源
            DBUtils.close(statement);
            DBUtils.close(connection);
        }
    }
}

 11.6 测试

第一次成功

在两个执行的sql中间模拟异常,看事务是否会提交

package com.bjpowernode.jdbc._06事务处理解决转账问题;

import com.bjpowernode.jdbc.util.DBUtils;

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

public class Test08Transaction {
    public static void main(String[] args) {
        //声明连接对象
        Connection connection = null;
        //声明发送SQL的接口对象
        Statement statement = null;
        try {
            //创建连接对象
            connection = DBUtils.getConnection();
            //关闭自动提交事务
            connection.setAutoCommit(false);
            //编写SQL
            String reducedSql = "update account set amount = amount - 1000 where aid = 1";
            int i = 1/0;
            String increasedSql = "update account set amount = amount + 1000 where aid = 2";
            // 创建SQL命令发送器
            statement = connection.createStatement();
            statement.executeUpdate(reducedSql);
            statement.executeUpdate(increasedSql);
            connection.commit();
        }catch (Exception e){
            e.printStackTrace();
            try {
                connection.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }finally {
            //关闭数据库资源
            DBUtils.close(statement);
            DBUtils.close(connection);
        }
    }
}

 

12.JDBC批处理

12.1 什么是批处理

批处理是建立一次连接(创建一个Connection对象)的情况下批量执行多个DML()语句,这些DML语句要么全部成功要么全部失败。如何确保全部成功or全部失败呢?在JDBC中开启事务,使用事务管理DML语句

12.2需求描述及操作步骤

使用批处理根据id批量的删除student表中的数据

1 定义SQL配置文件

2 创建Connection对象

3 创建PreparedStatement对象

4 将提交方式设置为手动提交,开启事务

5 设置占位符

6 将占位符添加到批处理中(相当于收集若干个本子,放入包包中。所以使用循环,在循环内部用preparedStatement.addBatch())

7 执行批处理 (preparedStatement.executeBatch()

8 提交事务(connection.commit)

9 如果批处理失败,在catch块中回滚事务

10 关闭资源

12.3 案例代码

package com.bjpowernode.jdbc._07JDBC批处理;

import com.bjpowernode.jdbc.util.DBUtils;

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

public class Test09Batch {
    public static void main(String[] args) {

        //模拟要删除的数据
        List<Integer> ids = Arrays.asList(1, 2, 3, 4, 5);

        //声明连接对象
        Connection connection = null;
        //声明发送SQL的接口对象
        PreparedStatement preparedStatement = null;

        try {
            //创建连接对象
            connection = DBUtils.getConnection();
            //关闭自动提交事务,将提交方式设置为手动提交,开启事务
            connection.setAutoCommit(false);

            //编写SQL,设置占位符
            String sql = "delete from student where id = ?;";
            //创建SQL命令发送器
            preparedStatement = connection.prepareStatement(sql);

            /*
            * 将占位符添加到批处理中(相当于收集若干个本子,放入包包中。所以使用循环,在循环内部用preparedStatement.addBatch();)
            */
            //所以使用循环
            for (Integer id : ids) {
                //将占位符添加到批处理中
                preparedStatement.setInt(1, id);
                //相当于收集若干个本子,放入包包中。
                preparedStatement.addBatch();
            }

            //执行批处理
            int[] rows = preparedStatement.executeBatch();
            System.out.println("受影响的行数为:" + Arrays.toString(rows));

            connection.commit();

        } catch (Exception e) {
            e.printStackTrace();
            try {
                //如果批处理失败,在catch块中回滚事务
                connection.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }finally {
            //关闭数据库资源
            DBUtils.close(preparedStatement);
            DBUtils.close(connection);
        }

    }
}

 

 

13.连接池

13.1 什么是连接池

连接池是创建和管理一个连接的缓冲池的技术这些连接准备好被任何需要它们的线程使用

连接池是装有连接的容器,使用连接的话,可以从连接池中进行获取,使用完成之后将连接归还给连接池。

13.2 为什么使用连接池

连接对象创建和销毁是需要耗费时间的,在服务器初始化的时候就初始化一些连接。把这些连接放入到内存中,使用的时候可以从内存中获取,使用完成之后将连接放入连接池中。从内存中获取和归还的效率要远远高于创建和销毁的效率。(提升性能)。

13.3 连接池的工作原理

13.4我们如何手写【难点】

13.4.1编写说明

我们现在编写的时候一个使用连接池,一个不使用连接池到时候测试的时候做下时间对比

13.4.2 创建jdbc.properties

jdbc.properties放在resources目录下

对应的数据库的各种信息,以及数据库连接池初始化时数量,最大连接数量以及自动增长数量

参数说明
initConnectCount数据库连接池 初始化时数量
maxConnects数据库连接池 最大连接数量
incrementCount数据库连接池 自动增长数量
jdbcDriver = com.mysql.cj.jdbc.Driver
jdbcUrl = jdbc:mysql://localhost:3306/powernode_jdbc?&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
username = root
password = root

initConnectCount = 20
maxConnects = 100
incrementCount = 3

13.4.3 封装一个连接类

该类中包含一个数据库连接一个是否使用的标记以及一个close方法用来将该连接置为可用状态,从而达到数据库连接的可复用,减少持续创建新连接的资源消耗

package com.bjpowernode.jdbc._08连接池.domain;

import java.sql.Connection;

public class PoolConnection {
    /**
     * 数据库连接
     */
    private Connection conn = null;

    /**
     * 标记该连接是否使用
     */
    private boolean isUse = false;

    /**
     * 构造方法
     * @param conn  连接对象
     * @param isUse  是否正在使用 【模拟关闭】
     */
    public PoolConnection(Connection conn, boolean isUse) {
        this.conn = conn;
        this.isUse = isUse;
    }

    public Connection getConn() {
        return conn;
    }

    public void setConn(Connection conn) {
        this.conn = conn;
    }

    public boolean isUse() {
        return isUse;
    }

    public void setUse(boolean use) {
        isUse = use;
    }

    /**
     * 将该连接置为可用状态
     */
    public void close() {
        this.isUse = false;
    }
}

13.4.4 创建一个连接池接口

对外提供的连接池的接口

package com.bjpowernode.jdbc._08连接池.service;

import com.bjpowernode.jdbc._08连接池.domain.PoolConnection;

import java.sql.Connection;

public interface IPool {
    /**
     * 获取连接池中可用连接
     */
    PoolConnection getConnection();

    /**
     * 获取一个数据库连接(不使用连接池)
     */
    Connection getConnectionNoPool();
}

13.4.5 创建一个连接池实现类以及对应方法

首先加载对应配置文件中信息初始化数据库连接池,然后用synchronized来实现多线程情况下线程安全的获取可用连接

package com.bjpowernode.jdbc._08连接池.service.impl;

import com.bjpowernode.jdbc._08连接池.domain.PoolConnection;
import com.bjpowernode.jdbc._08连接池.service.IPool;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import java.util.Vector;

public class JdbcPool implements IPool {
    //驱动
    private static String jdbcDriver;
    //连接地址
    private static String jdbcUrl;
    //数据库用户名
    private static String username;
    //数据库密码
    private static String password;
    //初始化连接数
    private static Integer initConnectCount;
    //最大连接数
    private static Integer maxConnects;
    //当连接不够时自动增长的数
    private static Integer incrementCount;
    //因为Vector是线程安全的,所有暂时选择它
    private static Vector<PoolConnection> connections = new Vector<>();

    /*
     * 通过实例初始化块来初始化
     */
    {
        //读取对应的配置文件,加载入properties中,并设置到对应的参数中
        InputStream is = JdbcPool.class.getClassLoader().getResourceAsStream("jdbc.properties");
        Properties properties = new Properties();
        try {
            properties.load(is);
        } catch (IOException e)  {
            e.printStackTrace();
        }
        jdbcDriver = properties.getProperty("jdbcDriver");
        jdbcUrl = properties.getProperty("jdbcUrl");
        username = properties.getProperty("username");
        password = properties.getProperty("password");
        initConnectCount = Integer.valueOf(properties.getProperty("initConnectCount"));
        maxConnects = Integer.valueOf(properties.getProperty("maxConnects"));
        incrementCount = Integer.valueOf(properties.getProperty("incrementCount"));

        try {
            /*
             * 注册jdbc驱动
             * */
            Driver driver = (Driver) Class.forName(jdbcDriver).newInstance();
            DriverManager.registerDriver(driver);
            /*
             * 根据initConnectCount来初始化连接池
             * */
            createConnections(initConnectCount);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


    /**
     * 获取可用连接
     */
    @Override
    public PoolConnection getConnection() {
        if (connections.isEmpty()) {
            System.out.println("连接池中没有连接");
            throw new RuntimeException("连接池中没有连接");
        }

        return getActiveConnection();
    }
    /**
     * 同步方法来获取连接池中可用连接,在多线程情况下,只有一个线程访问该方法来获取连接,防止由于多线程情况下多个线程获取同一个连接从而引起出错
     */
    private synchronized PoolConnection getActiveConnection() {
        /*
         * 通过循环来获取可用连接,若获取不到可用连接,则依靠无限循环来继续获取
         * */
        while (true) {
            for (PoolConnection con : connections) {
                if (!con.isUse()) {
                    Connection trueConn = con.getConn();
                    try {
                        //验证连接是否失效 0表示不校验超时
                        if (!trueConn.isValid(0)) {
                            con.setConn(DriverManager.getConnection(jdbcUrl, username, password));
                        }
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                    con.setUse(true);
                    return con;
                }
            }
            /*
             * 根据连接池中连接数量从而判断是否增加对应的数量的连接
             * */
            if (connections.size() <= maxConnects - incrementCount) {
                createConnections(incrementCount);
            } else if (connections.size() < maxConnects && connections.size() > maxConnects - incrementCount) {
                createConnections(maxConnects - connections.size());
            }
        }
    }

    /*
     * 创建对应数量的连接并放入连接池中
     * */
    private void createConnections(int count) {
        for (int i = 0; i < count; i++) {
            if (maxConnects > 0 && connections.size() >= maxConnects) {
                System.out.println("连接池中连接数量已经达到最大值");
                throw new RuntimeException("连接池中连接数量已经达到最大值");
            }
            try {
                Connection connection = DriverManager.getConnection(jdbcUrl, username, password);
                /*
                 * 将连接放入连接池中,并将状态设为可用
                 * */
                connections.add(new PoolConnection(connection, false));

            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /*
     * 获取连接池中连接数量
     * */
    public int getSize() {
        return connections.size();
    }
    @Override
    public Connection getConnectionNoPool() {
        Connection connection = null;
        try {
            connection = DriverManager.getConnection(jdbcUrl, username, password);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }
}

13.4.6 创建一个连接池的维护类

通过静态内部类实现连接池的单例模式

package com.bjpowernode.jdbc._08连接池;

import com.bjpowernode.jdbc._08连接池.service.impl.JdbcPool;

public class PoolManager {
    /**
     * 静态内部类实现连接池的单例
     * */
    private static class CreatePool{
        private static JdbcPool pool = new JdbcPool();
    }

    public static JdbcPool getInstance(){
        return CreatePool.pool;
    }
}

13.4.7 案例测试

        最后,上测试方法:起2000个线程,通过new两个CountDownLatch其中一个来实现线程的同时并发执行,

  熟话说,没有对比就没有伤害,我们先来用普通没有连接池的测试一下2000个连接并行执行的时间,代码如下:

package com.bjpowernode.jdbc._08连接池.test;

import com.bjpowernode.jdbc._08连接池.PoolManager;
import com.bjpowernode.jdbc._08连接池.service.impl.JdbcPool;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.CountDownLatch;


public class JdbcNoPoolMain {
    static final int threadSize = 2000;

    static JdbcPool jdbcPool = PoolManager.getInstance();

    static CountDownLatch countDownLatch1 = new CountDownLatch(1);
    static CountDownLatch countDownLatch2 = new CountDownLatch(threadSize);

    public static void main(String[] args) throws InterruptedException {
        threadTest();
    }

    public static void threadTest() throws InterruptedException {
        long time1 = System.currentTimeMillis();
        for (int i = 0; i < threadSize; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        //使得线程阻塞到coutDownLatch1为0时才执行
                        countDownLatch1.await();
                        selectNoPool();
                        //每个独立子线程执行完后,countDownLatch2减1
                        countDownLatch2.countDown();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        //将countDownLatch1置为0,从而使线程并发执行
        countDownLatch1.countDown();
        //等待countDownLatch2变为0时才继续执行
        countDownLatch2.await();
        long time2 = System.currentTimeMillis();
        System.out.println("thread size: " + threadSize + " no use pool :" + (time2 - time1));
    }

    public static void selectNoPool() throws SQLException {

        Connection conn = jdbcPool.getConnectionNoPool();
        Statement sm = null;
        ResultSet rs = null;
        try {
            sm = conn.createStatement();
            rs = sm.executeQuery("select * from student");
        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            while (rs.next()) {
                System.out.println(Thread.currentThread().getName() + " ==== " + "name: " + rs.getString("name") + " age: " + rs.getInt("age"));
            }
            Thread.sleep(100);
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        rs.close();
        sm.close();
        conn.close();
    }
}

 13.5 市面上有哪些可用的连接池

  • c3p0  老了
  • druid 阿里的
  • dbcp 老了
  • Hikari  小日本的,springboot官方推荐的

14.BaseDAO的封装难点

14.1 概述

对于JDBC市面上有一些封装的非常好的ORM框架,如mybatis  mybatisplus hibernate,但是我们现在还没有学到框架,如何自己做模拟一个ORM的框架呢,接下来我们来讲解BaseDAO的封装和使用

14.2 BaseDao代码

package com.bjpowernode.jdbc._09BaseDAO的封装.dao;

import com.bjpowernode.jdbc.util.DBUtils;
import com.sun.org.apache.bcel.internal.generic.ARETURN;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;

public class BaseDao {

    /**
     *
     * @param sql sql指令
     * @param clss orm关联类
     * @param params sql中占位符 对应的参数
     * @param <T>
     * @return
     */
    public static <T> List<T> selectList(String sql, Class<T> clss, Object ... params){
        //创建一个容器 规避空指针异常问题
        List<T> data = new ArrayList<>();//需要改动

        //2.创建连接 使用驱动管理器 创建连接
        Connection conn = DBUtils.getConnection();
        //3.获取指令的预编译对象
        PreparedStatement prep = null;
        ResultSet rs = null;
        try {
            //4.获取指令的预编译对象
            prep = conn.prepareStatement(sql);
            //设置预编译参数
            for (int i = 0; i < params.length; i++) {
                Object param = params[i];//获取参数
                //设置参数
                prep.setObject(i+1,param);
            }
            //5.执行sql指令
             rs= prep.executeQuery();
            //获取结果的元信息
            ResultSetMetaData metaData = rs.getMetaData();
            //获取列数
            int columnCount = metaData.getColumnCount();
            while (rs.next()) {
                T t = clss.newInstance();
                for (int i = 0; i < columnCount; i++) {
                     //获取别名 也是属性名
                    String columnLabel = metaData.getColumnLabel(i + 1);
                    //根据别名获取值
                    Object columnValue = rs.getObject(columnLabel);
                    //获取属性
                    Field field = clss.getDeclaredField(columnLabel);//需要改动
                    //设置属性值
                    field.setAccessible(true);
                    field.set(t,columnValue);

                }
                data.add(t);
            }
            return data;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            DBUtils.close(conn);
            DBUtils.close(prep);
            DBUtils.close(rs);
        }
        return null;
    }

    public <T> T selectOne(String sql,Class<T> clss,Object ... params){
        List<T> list = selectList(sql, clss, params);
        if(!list.isEmpty()&&list.size()==1){
            T t = list.get(0);
            return t;
        }
        return null;
    }

    /**
     * 通用的更新操作
     * @param sql
     * @param params
     * @return
     */
    public boolean update(String sql,Object ... params){
        //1.获取连接
        Connection connection = DBUtils.getConnection();
        PreparedStatement prep = null;
        try{
            prep = connection.prepareStatement(sql);
            //设置预编译参数
            for (int i = 0; i < params.length; i++) {
                Object param = params[i];//获取参数
                //设置参数
                prep.setObject(i+1,param);
            }
            //执行SQL指令
            int i = prep.executeUpdate();
            return i>=1;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            DBUtils.close(connection);
            DBUtils.close(prep);
        }
        return false;
    }
}

14.3 UserDao子类

package com.bjpowernode.jdbc._09BaseDAO的封装.dao;

import com.bjpowernode.jdbc.javabean.SysUser;

public class UserDao extends BaseDao{
    public SysUser selectOneSysUser(String username,String password){
        String selectSql = "select * from sys_user where username = ? and password = ?";
        SysUser sysUser = super.selectOne(selectSql, SysUser.class, username, password);
        return sysUser;
    }

    public void updateSysUser(String username,String password,Integer id){
        String updateSql = "update sys_user set username = ?,password = ? where id = ?";
        boolean b = super.update(updateSql, username, password,id);
        System.out.println("b = " + b);
    }
}

14.4 测试类

引入Junit单元测试的jar包

笔者的Junit单元测试jar包下载链接:https://download.csdn.net/download/weixin_46411355/87398726

package com.bjpowernode.jdbc._09BaseDAO的封装.test;

import com.bjpowernode.jdbc._09BaseDAO的封装.dao.UserDao;
import com.bjpowernode.jdbc.javabean.SysUser;
import org.junit.Test;

public class TestAPI {

    private UserDao userDao = new UserDao();

    @Test
    public void testUserDaoSelectSysUser() {

        String username = "hhh", password = "123";
        SysUser sysUser = userDao.selectOneSysUser(username, password);
        System.out.println(sysUser);
    }


    @Test
    public void testUserDaoUpdateSysUser() {
        Integer id = 5;
        String username = "罗龙江",password="888888";
        userDao.updateSysUser(username,password,id);


    }
}

14.5 分页封装

​​​​​​​14.5.1 创建分页结果封装类PageInfo<T>

package com.bjpowernode.jdbc._10分页的封装;

import java.util.List;

public class PageInfo <T>{

    private List<T> data;//具体的数据
    private Long count;//符合条件的数据条数

    public List<T> getData() {
        return data;
    }

    public void setData(List<T> data) {
        this.data = data;
    }

    public Long getCount() {
        return count;
    }

    public void setCount(Long count) {
        this.count = count;
    }
}

​​​​​​​14.5.2 修改BaseDao

/**
     * 分页查询
     * @param clazz 查询的数据封装的对象对应的Class
     * @param sql sql语句
     * @param  currentPage 当前页数,也就是第几页
     * @param pageSize 每页显示的条数
     * @return 分页封装的对象
     */
    public static <T> PageInfo<T> selectByPage(Class<T> clazz,String sql,int currentPage,int pageSize){
        //分页的时候limit跳过的条数
        int index = 0;
        index = (currentPage-1)*pageSize;
        String selectSql = sql+" limit "+index+","+pageSize;
        // 查询获取当前指定页数的数据
        List<T> list = selectList(selectSql,clazz);
        //获取总的条数
        // select * from student
        Long totalCount = getTotalCount(sql);

        PageInfo<T> pageInfo  = new PageInfo<>();
        pageInfo.setData(list);
        pageInfo.setCount(totalCount);

        return pageInfo;
    }

    /**
     * 获取总的条数
     * @param sql sql语句
     * @return 返回条数
     */
    private static Long getTotalCount(String sql){
        //子查询
        //select count(1) from ( select * from student ) as s
        String countSql = "select count(1) from ("+sql+") as s";

        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            connection = DBUtils.getConnection();
            preparedStatement = connection.prepareStatement(countSql);
            resultSet = preparedStatement.executeQuery();
            resultSet.next();//先让光标指向数据的第一行
            long totalCount = resultSet.getLong(1);
            return totalCount;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            DBUtils.close(resultSet);
            DBUtils.close(preparedStatement);
            DBUtils.close(connection);
        }
        return 0L;
    }

​​​​​​​14.5.3 测试类

package com.bjpowernode.jdbc._10分页的封装;


import com.bjpowernode.jdbc._09BaseDAO的封装.dao.BaseDao;
import com.bjpowernode.jdbc.javabean.Student;
import org.junit.Test;

public class PageTest {
    @Test
    public void testPaging(){
        String sql = "select * from student where id > 4";
        PageInfo<Student> pageInfo = BaseDao.selectByPage(Student.class, sql, 1, 2);
        //总条数
        System.out.println(pageInfo.getCount());
        // 第一页显示的数据
        System.out.println(pageInfo.getData());
}

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/182206.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

并发编程-多线程并发设计原理

并发编程-多线程&并发设计原理并发编程简介多线程&并发设计原理1 多线程回顾1.1 Thread和Runnable1.1.1 Java中的线程1.1.2 Java中的线程&#xff1a;特征和状态1.1.3 Thread和Runnable接口1.1.4 Callable1.2 synchronized关键字1.2.1 锁的对象1.2.2 锁的本质1.2.3 实现…

k8s实现controller如何远程调式?

背景&#xff1a; 使用kubebuilder和code-generate生成自定义资源代码后&#xff0c;实现管理自定义资源的controller逻辑。此时&#xff0c;需要调试controller代码逻辑&#xff0c;有2种思路。方法1&#xff1a;对该代码打包成镜像文件&#xff0c;直接部署进入k8s集群中&…

Springboot+vue中小企业合同管理系统

编写企业合同管理系统&#xff0c;让其能创建合同、修改合同、删除合同、合同变更标识、合同收款提醒、合同时间管理、合同废止标识、结束合同、合同统计、合同查询等几大功能。 (1) 创建合同 管理人员将签订后的合同的各项信息存入数据库中&#xff0c;使合同进入开始执行的…

网络编程(2)

封装和分用 1)封装:就是在数据中添加一些辅助传输的信息&#xff1b; 2)分用:就是解析这些信息 3)发送数据的时候&#xff0c;上层协议要把数据交给下层协议&#xff0c;由下层协议来添加一些信息 4)接收数据的时候&#xff0c;下层协议要把数据交给上层协议&#xff0c;有上层…

分割pdf的办法?看这里就明白了!

对于大多数办公党来说&#xff0c;困难的或许不是制作一些办公文件、文档&#xff0c;重要的是如何将这些文档以合适的形式发送给需要的人。不管是客户还是同事、上级&#xff0c;他们对文档格式、内容的要求都是有不一样的标准的。这时候我们就面临一个重要的问题了&#xff0…

Linux驱动开发:块设备驱动

这里写自定义目录标题一、块设备的简介二、块设备驱动框架1、block_device 结构体2、gendisk 结构体3、block_device_operations 结构体4、块设备 I/O 请求过程5、bio 结构体三、使用请求队列方式的块设备驱动程序1、经过第“二”部分的讲解总结&#xff0c;可以得出驱动程序的…

Java基础10:常用API(上)

Java基础10&#xff1a;常用API&#xff08;上&#xff09;一、Math二、System1. currentTimeMillis2. arraycopy三、Runtime四、Object1. toString2. equals3. clone五、Objects六、BigInteger1. 构造方法&#xff08;获取BigInteger&#xff09;2. 常用方法七、BigDecimal1. …

2023年房地产地段研究报告

房地产的投资业务中&#xff0c;选择一个好的地段&#xff0c;或者说区位&#xff0c;是十分重要的。在房地产行业&#xff0c;房价中包含地价&#xff0c;而房价上升的主要原因则是地价的上升。当房屋所处的地段深受消费者青睐、该地段的房屋供不应求时&#xff0c;房屋的价格…

Minecraft 1.19.2 Fabric模组开发 08.3D动画盔甲

我们本次在Fabric 1.19.2中实现具有动画效果的3D盔甲 效果演示效果演示效果演示 1.首先&#xff0c;为了实现这些效果&#xff0c;我们需要首先使用到一个模组:geckolib(下载地址) 找到项目的build.gradle文件&#xff0c;在repositories和dependencies中添加依赖。 reposit…

python+django校园大学生兼职系统vue357

目 录 摘 要 I Abstracts II 目 录 III 第1章 绪论 1 1.1课题背景 1 1.2研究意义 1 1.3研究内容 2 第2章 技术介绍 1 第3章 需求分析 4 3.1需求分析概述 4 3.2可行性分析 4 3.2.1经济可行性 5 3.2.2技术可行性 5 3.3系统功能设计 …

Target 塔吉特DVS EDI 业务测试指南

Target塔吉特是美国仅次于Walmart沃尔玛的第二大巨型折扣零售百货集团&#xff0c;由于拓展了其数字化履约能力&#xff0c;使得越来越多的国内零售产品供应商和Target建立合作关系。Target要求其供应商通过EDI&#xff08;Electronic Data Interchange&#xff0c;中文名称是电…

基于蜣螂算法改进的随机森林回归算法 - 附代码

基于蜣螂算法改进的随机森林回归算法 - 附代码 文章目录基于蜣螂算法改进的随机森林回归算法 - 附代码1.数据集2.RF模型3.基于蜣螂算法优化的RF4.测试结果5.Matlab代码6.Python代码摘要&#xff1a;为了提高随机森林数据的回归预测准确率&#xff0c;对随机森林中的树木个数和最…

来看看这些电脑清理内存的方法

随着电脑使用时间的增加&#xff0c;你有没有发现电脑用得越多反应越慢&#xff1f;如果你遇到这个问题&#xff0c;可以试试这几个优化设置&#xff0c;让你的电脑速度起死回生&#xff01; 方法一&#xff1a;删除临时文件 按键盘上的Win R&#xff0c;在对话框中输入【%temp…

96. 不同的二叉搜索树

96. 不同的二叉搜索树题目算法设计&#xff1a;枚举算法设计&#xff1a;动态规划题目 传送门&#xff1a;https://leetcode.cn/problems/unique-binary-search-trees/ 算法设计&#xff1a;枚举 当 n 5&#xff0c;用 {1、2、3、4、5} BST数是多少组&#xff1f; 有 5 种情…

如何在 macOS 上安装虚拟机软件 VMware Fusion Player (个人版免费)

文章目录IntroVMware 网站注册事宜安装在 VMware Fusion 中创建虚拟机准备 iso 文件VMware Fusion 主界面Intro VMware 网站注册事宜 需要一个邮箱地址&#xff0c;先注册登陆 VMware。 然后在之后某个页面再次 register &#xff0c;就是随意填写一些字段&#xff1a;所在公…

记录晖哥程序员职业规划一次授课笔记

发现 发明 道 普通知识、特殊知识 形而上学&#xff0c;为道&#xff0c;职场规律 形而下学&#xff0c;为气&#xff0c;python、go 万物生于有&#xff0c;而有生于无&#xff01; 在职场中做无中生有的事。 利他精神 利他即利己 天予弗取反受其咎&#…

mathtype2023专门打数学符号的软件

mathtype是数学公式编辑器&#xff0c;与常见的文字处理软件及演示程序配合使用&#xff0c;能够在各种文档中加入复杂的数学公式及符号&#xff0c;可用在编辑数学的试卷、书籍、报刊、论文、幻灯演示等方面&#xff0c;mathtype2023版是编辑数学资料工具! 在这款软件中主要帮…

JavaScript基础(18)_Date对象、Math对象

Date对象 Date(日期对象)是一个构造函数,必须使用new来调用创建日期对象 1、创建一个Date对象&#xff0c;如果直接使用构造函数创建一个Date对象&#xff0c;则会封装为当前代码"执行的时间"。 var d new Date(); console.log(d); 2、创建一个指定的时间对象&am…

【C++修炼之路】14.模板进阶

每一个不曾起舞的日子都是对生命的辜负 模板进阶模板进阶一. 非类型模板参数1.1 引出场景1.2 非类型模板参数1.3 其他例子二.array类2.1 array类的介绍2.2 array的价值三.模板的特化3.1 概念3.2 函数模板的特化3.3 类模板的特化3.3.1 全特化3.3.2 偏特化四.模板分离编译4.1 什么…

人事管理系统

人才是单位的宝贵财富&#xff0c;而人事管理是人才资源的核心&#xff0c;因此快捷、高效的人事档案理系统成为单位的基本需求。 系统的主要目的是实现企业人事的信息化管理&#xff0c;主要业务是实现对员工信息的管理。 一、功能需求分析 人事管理系统主要是对人事档案管理…