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

Dreamchaser追梦 Dreamchaser追梦     2022-12-09     295

关键词:

系列文章目录

注:因为集训还没结束,没有统计所有人的博文,所以以上目录并不完整。理论上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软件创新实验室暑假集训springmvc框架(设计原理简单使用源码探究)(代码片段)

系列文章目录20级Java篇【2021软件创新实验室暑假集训】计算机的起源与大致原理【2021软件创新实验室暑假集训】Java基础(一)【2021软件创新实验室暑假集训】Java基础(二)应用篇【2021软件创新实验室暑假集训... 查看详情

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

系列文章目录20级Java篇【2021软件创新实验室暑假集训】计算机的起源与大致原理【2021软件创新实验室暑假集训】Java基础(一)【2021软件创新实验室暑假集训】Java基础(二)【2021软件创新实验室暑假集训】Java基... 查看详情

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

系列文章目录20级Java篇【2021软件创新实验室暑假集训】计算机的起源与大致原理【2021软件创新实验室暑假集训】Java基础(一)【2021软件创新实验室暑假集训】Java基础(二)【2021软件创新实验室暑假集训】Java基... 查看详情

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

系列文章目录20级Java篇【2021软件创新实验室暑假集训】计算机的起源与大致原理【2021软件创新实验室暑假集训】Java基础(一)【2021软件创新实验室暑假集训】Java基础(二)【2021软件创新实验室暑假集训】Java基... 查看详情

2021软件创新实验室暑假集训springboot框架(代码片段)

系列文章目录20级Java篇【2021软件创新实验室暑假集训】计算机的起源与大致原理【2021软件创新实验室暑假集训】Java基础(一)【2021软件创新实验室暑假集训】Java基础(二)【2021软件创新实验室暑假集训】Java基... 查看详情

2021软件创新实验室暑假集训springboot框架(代码片段)

系列文章目录20级Java篇【2021软件创新实验室暑假集训】计算机的起源与大致原理【2021软件创新实验室暑假集训】Java基础(一)【2021软件创新实验室暑假集训】Java基础(二)应用篇【2021软件创新实验室暑假集训... 查看详情

软件创新实验室2021年暑假集训使用typora+gitee搭建图床(代码片段)

使用Typora+gitee搭建图床前言咱们在用typora写博客的时候总会遇到一个问题,那就是图片问题。如果你的typora是没有设置过图床的,那么和设置过图床的typora判若两人。没有设置过的显示路径是本地磁盘的绝对路径,... 查看详情

软件创新实验室2021年寒假集训java技术培训——java前置知识学习

系列文章目录【软件创新实验室2021年寒假集训】汇总篇20级Java培训第一天:【软件创新实验室2021年寒假集训】Java技术培训——Java前置知识学习第二天:Java基础(一)第三天:【软件创新实验室2021年寒假集... 查看详情

软件创新实验室2021年寒假集训java后端开发漫谈及感悟分享

系列文章目录【软件创新实验室2021年寒假集训】汇总篇20级Java培训第一天:【软件创新实验室2021年寒假集训】Java技术培训——Java前置知识学习第二天:Java基础(一)第三天:【软件创新实验室2021年寒假集... 查看详情

软件创新实验室2021年寒假集训java技术培训——java基础(代码片段)

系列文章目录【软件创新实验室2021年寒假集训】汇总篇20级Java培训第一天:【软件创新实验室2021年寒假集训】Java技术培训——Java前置知识学习第二天:Java基础(一)第三天:【软件创新实验室2021年寒假集... 查看详情

2018.08.05暑假集训感受

今天是星期日,实验室人很少,我可以放开心情写写日记了~最近可能是有点厌学吧,到了晚上就没想法继续读下去了,感觉会很烦躁可能是因为学习新知识,但是却因为完全没看懂就觉得很烦吧,我还是很喜欢学习的,但还是... 查看详情

uestc2021暑假前集训(splay树)

      关于splay的教程可以在哔站上查询我校前辈算法讲堂然后代码对标的是两篇博客(实际上是一种姿势,因为这种代码姿势非常的优美)Splay入门解析【保证让你看不懂(滑稽)】-小蒟蒻yyb-博客园(cnblogs.... 查看详情

2021年swpuacm暑假集训day5单调栈算法(代码片段)

什么是单调栈顾名思义,单调栈即满足单调性的栈结构。这里的单调递增或递减是指的从栈顶到栈底单调递增或递减。既然是栈,就满足后进先出的特点。与之相对应的是单调队列。单调栈的实现插入在当前元素插入栈... 查看详情

2021年暑假acm集训队模拟赛第4场——题解(代码片段)

目录A山峦叠嶂B重叠正方形C一群X星人DX星群岛(MST,最小生成树模板题)EX星人的飞行设备(复杂一点的DP)FX星人的救援G最长回文子串HDNA序列拼接I防水板砖(搜索,DFS待补)J黑白树(比较复... 查看详情

hmoi2018南京暑假集训期末考试总结(代码片段)

APOJ1717Dominoes翻译题目给出两列数,为了使两列数的和之间的差距变小,可以交换对应位置的数字,求出当最小的时候,最少的交换次数。思路签到题目,但是我还是太菜了,竟然没有看出是背包。设(dp_i,j)代表前(i)个使得差异值... 查看详情

[暑假集训--数论]poj2657comfort

DescriptionAgame-boardconsistsofNfieldsplacedaroundacircle.Fieldsaresuccessivelynumberedfrom1toNclockwise.Insomefieldstheremaybeobstacles.Playerstartsonafieldmarkedwithnumber1.Hisgoalistoreachagivenfi 查看详情

[暑假集训--数论]poj1365primeland

EverybodyinthePrimeLandisusingaprimebasenumbersystem.Inthissystem,eachpositiveintegerxisrepresentedasfollows:Let{pi}i=0,1,2,...denotetheincreasingsequenceofallprimenumbers.Weknowthatx>1canbereprese 查看详情

zzuacm2015暑假集训round01

A.EncodingProblemDescriptionGivenastringcontainingonly‘A’-‘Z’,wecouldencodeitusingthefollowingmethod:Eachsub-stringcontainingksamecharactersshouldbeencodedto“kX”where“X”istheonlycharacterinthissub-str 查看详情