2021软件创新实验室暑假集训JDBC(原理使用以及实现简单的数据库连接池)

Posted Dreamchaser追梦

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021软件创新实验室暑假集训JDBC(原理使用以及实现简单的数据库连接池)相关的知识,希望对你有一定的参考价值。

系列文章目录

注:因为集训还没结束,没有统计所有人的博文,所以以上目录并不完整。理论上20级有Java篇10篇,应用篇7篇;19级各赛道各四篇(每次上课都会有一篇博文作总结)

前言

本文主要讲解JDBC的由来,JDBC的使用,JDBC的原理,以及教大家实现一个简单的数据库连接池。

一、JDBC的前世今生

JDBC全称Java DataBase Connectivity(Java数据库连接),是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。

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

很多时候我们往往错误的认识了JDBC,以为它是用于数据库连接的框架。其实不然,它只是sun公司为了规范操作,屏蔽底层数据库之间的差异而定义的一套标准,它位于java.sql包下面。
在这里插入图片描述

各个数据库厂商自己编写相关的驱动包来实现这套标准。因为规范了接口,所以Java工程师们可以不用关心数据库层面的差异,利用统一的jdbc操作数据库即可。

二、JDBC的使用及原理

1.jdbc简单使用步骤

以下操作以mysql为例

①加载驱动程序

//加载MySql驱动
Class.forName("com.mysql.cj.jdbc.Driver")

或者也可以这么写

driver = new com.mysql.cj.jdbc.Driver();
DriverManager.registerDriver(driver);

其实上面的操作本质是一样,
在这里插入图片描述

看一看源码,Class.forName("com.mysql.cj.jdbc.Driver")这句话其实就是把就是把这个类加载到内存中,加载的过程中会执行static块里的代码,而该代码实际上和后面一种是一样的。

②获得数据库连接

根据数据库路径、账号、密码获取数据库连接

DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/imooc", "root", "root");

③创建Statement\\PreparedStatement对象

Statement statement=conn.createStatement();
PreparedStatement ps=conn.prepareStatement(sql);

④调用executeQuery/executeUpdate方法

//调用Statement的executeQuery方法
ResultSet rs = stmt.executeQuery(sql);
//调用Statement的executeUpdate方法
Integer i=statement.executeUpdate(sql);
//调用PreparedStatement的executeQuery方法
ResultSet rs=ps.executeQuery();
//调用PreparedStatement的executeUpdate方法
Integer i=ps.executeUpdate(sql);

注:这里省略了业务处理流程

⑤关闭ResultSet、Statement/PreparedStatement、Connection

这里一定要注意关闭顺序,应该以ResultSet、Statement/PreparedStatement、Connection的顺序(先开后关)

同时捕获异常时最后每个都单独捕获,这样不至于在关闭时因为前面关闭出错而导致后面资源没关闭。

try {
            ....
} catch (SQLException throwables) {
    throwables.printStackTrace();
}finally {
    try {
        rs.close();
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }
    try {
        statement.close();
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }
    try {
        connection.close();
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }
}

2.简单的例子

//数据库路径
private static String url="jdbc:mysql://localhost:3306/store?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true";
//数据库账号
private static String user="root";
//数据库密码
private static String password="jinhaolin";
void testQueryStatement(){
Driver driver= null;
Statement statement=null;
Connection connection=null;
ResultSet rs=null;
try {
    //创建驱动类对象
    driver = new com.mysql.cj.jdbc.Driver();
    //注册驱动类
    DriverManager.registerDriver(driver);
    //获取连接
    connection=DriverManager.getConnection(url,user,password);
    //获取Statement
    statement=connection.createStatement();
    //sql语句
    String sql="select * from user";
    //执行查询,获得结果集
    rs=statement.executeQuery(sql);
    //调用next方法,将指针指向下一条记录,一开始调用next方法后,指针指向第一条记录。next返回值为Boolean类型,
    // 表示是否还有下一条记录
    while (rs.next()){
        System.out.println("-----------------------------");
        //获取当条记录的userName字段并打印
        System.out.println("username"+rs.getString("userName"));
        //获取当条记录的password字段并打印
        System.out.println("password"+rs.getString("password"));
        System.out.println("-----------------------------");
    }
} catch (SQLException throwables) {
    throwables.printStackTrace();
}finally {
    try {
        rs.close();
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }
    try {
        statement.close();
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }
    try {
        connection.close();
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }
}

3.Statement/PreparedStatement使用

①Statement

对于Statement,一般都是写好sql语句或者自己拼接好对应的sql语句去执行(自己拼接的话要注意格式,比如字符串得加上‘’,日期格式得以’yyyy-MM-dd HH:mm:ss’形式才行等等)。
一般来讲查询语句调用ResultSet executeQuery(String sql),增删改查都是调用int executeUpdate(String sql)

查询操作

ResultSet executeQuery(String sql)方法会返回一个结果集,结果集的读取调用,模板如下:

//rs中有多条记录,其内置一个指针,每调用一次next方法就会跳转到下一条记录,初始指针指向空。
while (rs.next()){
		  //用getXXX(字段名称)的方式获取当前指针指向的记录字段
          int id=rs.getInt("id");
          //....

以下是代码实例

private static String url="jdbc:mysql://localhost:3306/store?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true";
private static String user="root";
private static String password="jinhaolin";
void testQueryStatement(){
    Driver driver= null;
    Statement statement=null;
    Connection connection=null;
    ResultSet rs=null;
    try {
        //创建驱动类对象
        driver = new com.mysql.cj.jdbc.Driver();
        //注册驱动类
        DriverManager.registerDriver(driver);
        //获取连接
        connection=DriverManager.getConnection(url,user,password);
        //获取Statement
        statement=connection.createStatement();
        //sql语句
        String sql="select * from user";
        //执行查询,获得结果集
        rs=statement.executeQuery(sql);
        //调用next方法,将指针指向下一条记录,一开始调用next方法后,指针指向第一条记录。next返回值为Boolean类型,
        // 表示是否还有下一条记录
        while (rs.next()){
            System.out.println("-----------------------------");
            //获取当条记录的userName字段并打印
            System.out.println("username"+rs.getString("userName"));
            //获取当条记录的password字段并打印
            System.out.println("password"+rs.getString("password"));
            System.out.println("-----------------------------");
        }
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }finally {
        try {
            rs.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        try {
            statement.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        try {
            connection.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

增删改操作

关于增删改一般调用executeUpdate(String sql)`即可,其返回值为当次操作受影响的记录行数。

void testUpdateStatement(){
     Driver driver= null;
     Statement statement=null;
     Connection connection=null;
     try {
         driver = new com.mysql.cj.jdbc.Driver();
         DriverManager.registerDriver(driver);
         connection=DriverManager.getConnection(url,user,password);
         statement=connection.createStatement();
         String sql="update user set userName='李四' where id=2";
         Integer i=statement.executeUpdate(sql);
         System.out.println("当前受影响的记录数:"+i);
     } catch (SQLException throwables) {
         throwables.printStackTrace();
     }finally {
         try {
             statement.close();
         } catch (SQLException throwables) {
             throwables.printStackTrace();
         }
         try {
             connection.close();
         } catch (SQLException throwables) {
             throwables.printStackTrace();
         }
     }
 }

②PreparedStatement

PreparedStatement和Statement操作类似,不过PreparedStatement在创建时就需要传入对应的sql语句,这是为了“预编译”,同时sql语句支持占位符的方式(占位符序号从1开始)。

PreparedStatement操作有两个好处:
1.添加参数时不用操心类型转化
我们自己拼接字符串时总要为参数类型而操心,比如字符串要加’’,日期要改成合适格式。而PreparedStatement会帮我们做了这些事情。
2.防止sql注入(这个后面会讲)

查询操作

void testQueryPreparedStatement(){
    Driver driver= null;
    Connection connection=null;
    PreparedStatement ps=null;
    ResultSet rs=null;
    try {
        driver = new com.mysql.cj.jdbc.Driver();
        DriverManager.registerDriver(driver);
        connection=DriverManager.getConnection(url,user,password);
        String sql="select * from user where id=? or id=?";
        ps=connection.prepareStatement(sql);
        //传参
        ps.setInt(1,1);
        ps.setInt(2,2);
        rs=ps.executeQuery();
        while (rs.next()){
            System.out.println("-----------------------------");
            System.out.println("username"+rs.getString("userName"));
            System.out.println("password"+rs.getString("password"));
            System.out.println("-----------------------------");
        }
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }finally {
        try {
            rs.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        try {
            ps.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        try {
            connection.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

增删改操作

void testUpdatePreparedStatement(){
     Driver driver= null;
     Connection connection=null;
     PreparedStatement ps=null;
     try {
         driver = new com.mysql.cj.jdbc.Driver();
         DriverManager.registerDriver(driver);
         connection=DriverManager.getConnection(url,user,password);
         String sql="update user set userName='李四' where id=?";
         ps=connection.prepareStatement(sql);
         ps.setInt(1,1);
         Integer i=ps.executeUpdate(sql);
         System.out.println("当前受影响的记录数:"+i);
     } catch (SQLException throwables) {
         throwables.printStackTrace();
     }finally {
         try {
             ps.close();
         } catch (SQLException throwables) {
             throwables.printStackTrace();
         }
         try {
             connection.close();
         } catch (SQLException throwables) {
             throwables.printStackTrace();
         }
     }
 }

4.事务的使用

有时候我们希望有些操作要么一起执行,要么一起失败。比如转账业务,需要在转账方账户扣除相应的资金,在转入方增加相应的资金,不能说一方成功了,一方失败了,这是不被允许。

所以我们需要有一种机制能保证某几个操作能一起成功或者一起失败。这就叫事务机制。

数据库事务(transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。

事务使用示例:

void testTransaction(){
        Driver driver= null;
        Connection connection=null;
        PreparedStatement ps1=null,ps2=null;
        try {
            driver = new com.mysql.cj.jdbc.Driver();
            DriverManager.registerDriver(driver);
            connection=DriverManager.getConnection(url,user,password);
            //将自动提交设置为false
           connection.setAutoCommit(false);
            String sql="update user set userName='王五' where id=?";
            ps1=connection.prepareStatement(sql);
            ps1.setInt(1,1);
            ps1.executeUpdate();
            ps2=connection.prepareStatement(sql);
            ps2.setInt(1,2);
            ps2.executeUpdate();
            //所有操作完成后提交事务
            connection.commit();
        } catch (SQLException throwables) {
            //打印堆栈信息
            throwables.printStackTrace();
            try {
                //回滚事务
                connection.rollback();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }finally {
            try {
                ps1.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            try {
                ps2.close();
            } catch (SQLException throwables) {
                throwables

以上是关于2021软件创新实验室暑假集训JDBC(原理使用以及实现简单的数据库连接池)的主要内容,如果未能解决你的问题,请参考以下文章

2021软件创新实验室暑假集训SpringMVC框架(设计原理简单使用源码探究)

2021软件创新实验室暑假集训SpringMVC框架(设计原理简单使用源码探究)

2021软件创新实验室暑假集训总结篇

2021软件创新实验室暑假集训总结篇

2021软件创新实验室暑假集训SpringBoot框架

2021软件创新实验室暑假集训SpringBoot框架