浅谈JDBC的理解(一)

2023-11-03

一、目录

二、概述

简述

  JDBC是什么?JDBC英文名为:Java Data Base Connectivity(Java数据库连接),官方解释它是Java编程语言和广泛的数据库之间独立于数据库的连接标准的Java API,根本上说JDBC是一种规范,它提供的接口,一套完整的,允许便捷式访问底层数据库。可以用JAVA来写不同类型的可执行文件:JAVA应用程序、JAVA Applets、Java Servlet、JSP等,不同的可执行文件都能通过JDBC访问数据库,又兼备存储的优势。简单说它就是JAVA与数据库的连接的桥梁或者插件,用JAVA代码就能操作数据库的增删改查、存储过程、事务等。

  JDBC有什么用?我们用JAVA就能连接到数据库;创建SQL或者MYSQL语句;执行SQL或MYSQL的查询数据库;查看和修改结果记录。

  我们思考一下?数据库是由不同生产产商决定的,例如Mysql、Oracle、SQL Server,而如果JAVA JDK不可能说提供对不同数据库的实现吧?还有,JAVA具备天生跨平台的优势,它就提供了JDBC的接口API,具体的实现由不同的生产产商决定。这样,数据库生产产商都根据JAVA API去实现各自的应用驱动,这问题就迎刃而解了。

  JDBC的工作原理是什么?我将在下一篇文章叙述JDBC运用的设计模式,以及部分JDK源码。

工作原理图(转自百度百科)

        

常用接口

   提供的接口包括:JAVA API:提供对JDBC的管理链接;JAVA Driver API:支持JDBC管理到驱动器连接。

   DriverManager:这个类管理数据库驱动程序的列表,查看加载的驱动是否符合JAVA Driver API的规范。

   Connection:与数据库中的所有的通信是通过唯一的连接对象。

   Statement:把创建的SQL对象,转而存储到数据库当中。

   ResultSet:它是一个迭代器,用于检索查询数据。

三、快速入门

操作流程图

数据类型图

 数字类型

 时间日期类型

 字符串类型

实例练习

1、Connection

复制代码

public class JDBCUtil {
    //Driver类全名
    public static String DRIVER="com.mysql.jdbc.Driver";
    //jdbc协议:子协议://ip:端口号/数据库名
    public static String URL="jdbc:mysql://localhost:3306/test";
    //数据库用户名
    public static String USERNAME="root";
    //数据库密码
    public static String PASSWORD="root";

    private static Connection connection=null;

    /**
     * 获取JDBC连接
     * @return
     */
    public  static Connection getConnection(){
        try {
            //加载驱动程序:它通过反射创建一个driver对象。
            Class.forName(DRIVER);

            //获得数据连接对象。
            // 在返回connection对象之前,DriverManager它内部会先校验驱动对象driver信息对不对,我们只要知道内部过程即可。
            connection= DriverManager.getConnection(URL,USERNAME,PASSWORD);
            return connection;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 通过读取文件连接
     * @param fileName
     * @return
     * @throws SQLException
     */
    public   Connection getConnectionByLoadSettingFile(String fileName) throws SQLException {
        /*
            文件里面的内容:跟上面的常量一模一样
            jdbc.driver=com.mysql.jdbc.Driver
            jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8
            jdbc.username=root
            jdbc.password=root
        */
        Properties props=new Properties();
        try {
            //我的properties文件是放在src根目录下的
            InputStream in=DBUtil.class.getResourceAsStream("/"+fileName);
            if(null==in)
                System.out.println("找不到文件:"+fileName);
            props.load(in);
        } catch (Exception e) {
            e.printStackTrace();
        }
        String driver=props.getProperty("jdbc.driver");
        if(null!=driver)
            System.setProperty("jdbc.drivers",driver);
        String url=props.getProperty("jdbc.url");
        String username=props.getProperty("jdbc.username");
        String password=props.getProperty("jdbc.password");
        connection=DriverManager.getConnection(url,username,password);
        return connection;
    }
}

复制代码

 

2、Statement

复制代码

public class MyClient {
    public static void main(String [] args) throws SQLException {
        Connection connection=null;
        Statement statement=null;

        connection=JDBCUtil.getConnection();
        statement=connection.createStatement();
        //需要在自己的数据库当中建立一张user表
        String sql="insert into user(loginName,userName,password,sex)values('tom123','tom','123456',1)";
        statement.executeUpdate(sql);
    }
}

复制代码

 

3、PareparedStatement

复制代码

public class MyClient {
    public static void main(String [] args) throws SQLException {
        Connection connection=null;
        PreparedStatement pStatement=null;

        connection=JDBCUtil.getConnection();
        String sql="insert into user(loginName,userName,password,sex)values(?,?,?,?)";
        //预编译
        pStatement=connection.prepareStatement(sql);
        //前面的索引对应上面的问号,传递参数。
        pStatement.setString(1,"tom123");
        pStatement.setString(2,"tom");
        pStatement.setString(3,"123456");
        pStatement.setInt(4,1);
        pStatement.executeUpdate();
    }
}

复制代码

 

4、ResultSet

复制代码

public class MyClient {
    public static void main(String [] args) throws SQLException {
        Connection connection=null;
        Statement statement=null;
        ResultSet resultSet;

        connection=JDBCUtil.getConnection();
        String sql="select * from user";
        statement=connection.createStatement();
        //resultSet就是一个迭代器,里面的方法跟迭代器几乎一致。
        resultSet=statement.executeQuery(sql);
        while (resultSet.next()){
            String loginName=resultSet.getString("loginName");
            String userName=resultSet.getString("userName");
            String password=resultSet.getString("password");
            int sex=resultSet.getInt("sex");
            System.out.println(loginName+"-"+userName+"-"+password+"-"+sex);
        }
    }
}

复制代码

四、可滚动和可更新的结果集

相关参数

复制代码

//了解数据集可滚动更新:查看ResultSet接口的几个参数

  /** 结果集不能滚动(默认值)*/
    int TYPE_FORWARD_ONLY = 1003;

    /** 结果集可以滚动,但对数据库变化不敏感*/
    int TYPE_SCROLL_INSENSITIVE = 1004;

    /**结果集可以滚动,且对数据库变化敏感*/
    int TYPE_SCROLL_SENSITIVE = 1005;
  
  /**结果集不能用于更新数据库(默认值)*/
    int CONCUR_READ_ONLY = 1007;

    /**结果集可以用于更新数据库*/
    int CONCUR_UPDATABLE = 1008;


复制代码

可滚动可更新

注意:可滚动简单说就是设置结果集可更新resultSet目前的游标值。可更新就是可以更新结果集里面的增删改查。可更新简单说,就是获取数据集ResultSet以后改动更加灵活。

复制代码

public class Client {
    public static void main(String [] args){
        Connection connection=null;
        PreparedStatement pStatement=null;
        Statement statement=null;
        ResultSet resultSet=null;
        try {
            connection=DBUtil.getInstance().getConnection();
            //第一个参数设置是否可以滚动,第二个参数设置是否可更新
            statement=connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
            String sql="select * from user";
            ResultSet rs=statement.executeQuery(sql);

            /**可滚动的几个方法
                rs.previous();
                rs.next();
                rs.getRow();
                rs.absolute(0);
             **/

            //往数据集里面插入数据同时更新到数据:从表的最后开始插入。
            rs.moveToInsertRow();//把游标移动到插入行,默认在最后一行。
            rs.updateString("loginName","小白脸");
            rs.updateString("userName","大猩猩");
            rs.updateString("password","123");
            rs.updateInt("sex",100);
            rs.insertRow();
            rs.moveToCurrentRow();//把游标移动最后一个位置

            //删除第十行数据
            rs.absolute(10);
            rs.deleteRow();

            while(rs.next()){
                System.out.println(rs.getString(2));
                //把数据集里的数据中的性别全部更新为0
                rs.updateInt("sex",0);
                rs.updateRow();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

复制代码

五、事务

问题思考

  事务是什么?我们将一组语句构成一个事务。当所有语句都是顺利执行以后,事务可以被提交。否则,如果其中某个语句遇到错误,那么事务将被回滚,就好像任何语句都没有被执行一样。

  实际用例。假设我们需要将钱从一个银行账号转移到另外一个账号。此时,一个非常重要的问题就是我们必须同时将钱从一个账号取出并且存入另一个账号。如果在将钱存入其他账号之前系统发生崩溃,那么我们必须撤销取款操作。

  事务有什么特性或者说有什么作用?

  • 原子性:最小的单元,如果一个是失败了,则一切的操作将全部失败。
  • 一致性:如果事务出现错误,则回到最原始的状态
  • 隔离性:多个事务之间无法访问,只有当事务完成后才可以看到结果
  • 持久性:当一个系统崩溃时,一个事务依然可以提交,当事务完成后,操作结果保存在磁盘中,不会被回滚

保存点与批量更新

保存点与事务的接口源码

复制代码

public interface Connection  extends Wrapper, AutoCloseable {
    /** 设置提交方式:自动还是手动*/
    void setAutoCommit(boolean autoCommit) throws SQLException;
    
    boolean getAutoCommit() throws SQLException;

    /**提交事务*/
    void commit() throws SQLException;

   /**事务回滚*/
    void rollback() throws SQLException;
    
    /**设置保存点*/
    Savepoint setSavepoint() throws SQLException;

    Savepoint setSavepoint(String name) throws SQLException;
    
    /**回滚到保存点*/
    void rollback(Savepoint savepoint) throws SQLException;
    
    /**释放保存点资源*/
    void releaseSavepoint(Savepoint savepoint) throws SQLException;
}

public interface Statement  extends Wrapper, AutoCloseable {
    /**加入到批量处理队列*/
    void addBatch( String sql ) throws SQLException;
    
    void clearBatch() throws SQLException;

    /**执行批量处理队列*/
    int[] executeBatch() throws SQLException;
}

复制代码

什么是保存点?使用保存点可以更细粒度地控制回滚操作,而不用每次都退回到初始点。

什么又是批量更新?批量更新包括批量增删改,当我们一次性要插入很多条数据的时候,假设我们每次提交一次又获取数据库连接一次,然后又关闭数据库连接,而且数据库连接是一个耗时操作,这样会大大降低性能,后续文章我会对这部分内容进行详细叙述。而批量更新呢,则先把数据放入一个队列里,并没有真正存入数据库中,当调用commit()方法的时候,队列的数据的操作一次性收集和提交。

复制代码

public class Client {
    public static void main(String [] args) throws SQLException {
        long time=System.currentTimeMillis();
        Connection connection=null;
        PreparedStatement pStatement=null;
        boolean autoCommit=false;
        Savepoint savepoint=null;
        try {
            connection=JDBCUtil.getConnection();
            autoCommit=connection.getAutoCommit();
            connection.setAutoCommit(false);
            String sql="insert into user(loginName,userName,password,sex)values(?,?,?,?)";
            pStatement=connection.prepareStatement(sql);
            //设置保存点
            savepoint=connection.setSavepoint("savePoint");
            for(int i=0;i<1000;i++){
                pStatement.setString(1,"tony"+i);
                pStatement.setString(2,"user"+i);
                pStatement.setString(3,i+"");
                pStatement.setInt(4,i);
                //添加到队列
                pStatement.addBatch();
            }
            //批量执行
            pStatement.executeBatch();
            connection.commit();

        } catch (SQLException e) {
            e.printStackTrace();
            //回滚到保存点
            connection.rollback(savepoint);
        }finally {
            //把事务提交设置为最初设置
            connection.setAutoCommit(autoCommit);
        }
        long temp=System.currentTimeMillis()-time;
        System.out.println(temp+"ms");
    }
}

复制代码

六、思考与总结

思考

  • 问题一:我们都知道获取JDBC连接是一个耗时操作。而我们查看教程的时候,提倡我们获取数据库连接,操作完毕以后要记得关闭,这样固然是正确的。但是,如果一个简单的操作就不停开启连接断开连接,这样会对性能大打折扣。
  • 问题二:JDBC的工作原理?还有它底部运用什么设计模式,让它能够自适应不同数据库产商的驱动呢?
  • 问题三:事务提交和普通提交的性能到底有多大的差别?

总结

  解决一:有一个概念叫做连接池,就是数据库连接这个耗时操作交个一个容器去管理。至于数据库什么时候连接什么时候被关闭,有几个数据库连接对象?这些完全托管给连接池,而不需要客户端去考虑,目前一个比较成熟的是c3p0的连接池。现在模拟一个单例的数据库连接,比较单例数据库连接与随开随关的性能比较。

工具类

复制代码

public class DBUtil {
    public static String DRIVER="com.mysql.jdbc.Driver";
    public static String URL="jdbc:mysql://localhost:3306/test";
    public static String USERNAME="root";
    public static String PASSWORD="root";

    private  Connection connection=null;
    private DBUtil(){
    }
    /**
     * 获得DB工具类的对象,这种获取对象的方式慢慢被jdk推荐使用。
     */
    public static  DBUtil getInstance(){
        return DBUtilClassInstance.dbUtil;
    }

    /**
     * 采用内部类单例模式:天然线程安全,延迟加载,调用效率高。若不了解,参考我的文章设计模式-单例模式
     */
    private static class DBUtilClassInstance{
        private  static  DBUtil dbUtil= new DBUtil();
    }

    /**
     * 获取JDBC连接
     * @return
     */
    public  Connection getConnection(){
        try {
            if(null!=connection && !connection.isClosed()){
                return connection;
            }
            Class.forName(DRIVER);
            System.out.println("驱动程序加载成功!");
            connection=DriverManager.getConnection(URL,USERNAME,PASSWORD);
            return connection;
        } catch (Exception e) {
            System.out.println("未找到驱动程序!");
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 通过读取文件连接
     * @param fileName
     * @return
     * @throws SQLException
     */
    public   Connection getConnectionByLoadSettingFile(String fileName) throws SQLException {
        if(null!=connection && !connection.isClosed()){
            return connection;
        }
        Properties props=new Properties();
        try {
            InputStream in=DBUtil.class.getResourceAsStream("/"+fileName);
            if(null==in)
                System.out.println("找不到文件:"+fileName);
            props.load(in);
        } catch (Exception e) {
            e.printStackTrace();
        }
        String driver=props.getProperty("jdbc.driver");
        if(null!=driver)
            System.setProperty("jdbc.drivers",driver);
        String url=props.getProperty("jdbc.url");
        String username=props.getProperty("jdbc.username");
        String password=props.getProperty("jdbc.password");
        connection=DriverManager.getConnection(url,username,password);
        return connection;
    }

复制代码

性能比较

复制代码

public class Client {
    public static void main(String [] args){
        long time=System.currentTimeMillis();
      for(int i=0;i<100;i++){
          User user=new User("loginName"+i,"userName"+i,"password"+i,i);
          myThread thread=new myThread(user);
          thread.run();
      }
      System.out.println(System.currentTimeMillis()-time+"ms");
    }
}

class myThread implements Runnable{

    private User user;

    public myThread(User user){
        this.user=user;
    }

    public void run() {
        PreparedStatement pStatement=null;
        Connection connection=null;
        try {
            connection=DBUtil.getInstance().getConnectionByLoadSettingFile("db.properties");
            String sql="insert into user(loginName,userName,password,sex)value(?,?,?,?)";
            pStatement=connection.prepareStatement(sql);
            pStatement.setString(1,user.getLoginName());
            pStatement.setString(2,user.getUserName());
            pStatement.setString(3,user.getPassword());
            pStatement.setInt(4,user.getSex());
            pStatement.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //注释掉这个方法就是代表数据库连接用完就随即关闭。
            DBUtil.getInstance().closePreparedStatement(pStatement);
            DBUtil.getInstance().closeConnection(connection);
        }
    }
}

复制代码

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

浅谈JDBC的理解(一) 的相关文章

随机推荐

  • ChromeDriver与Chrome版本的对应关系

    ChromeDriver v2 15 2015 03 26 Supports Chrome v40 43 ChromeDriver v2 14 2015 01 28 Supports Chrome v39 42 Resolved issue
  • 哪些方法可以判断出一个有向图是否有环

    使用深度优先遍历 若从有向图上的某个顶点u出发 在 DFS u 结束之前出现一条从顶点v到u的边 由于v在生成树上是u的子孙 则图中必定存在包含u和v的环 因此深度优先遍历可以检测一个有向图是否有环 拓扑排序时 当某顶点不为任何边的头时才能
  • 无刷电机Foc控制

    foc控制简介 一 知识点简介 什么是foc FOC Field Oriented Control 直译是磁场定向控制 也被称作矢量控制 VC Vector Control 是目前无刷直流电机 BLDC 和永磁同步电机 PMSM 高效控制的
  • osmdroid地图

    1 简介 osmdroid是一个开源的地图填充器 全称是 open street map 至于droid我个人认为应该是安卓版的后缀吧 在网络上找了很多资料 大多是加载在线地图的 加载离线地图的很少 所以把代码记录一下 2 初始化 1 在布
  • Nodejs学习路线图

    转自 http www open open com lib view open1403574545233 html 前言 用Nodejs已经1年有余 陆陆续续写了48篇关于Nodejs的博客文章 用过的包有上百个 和所有人一样 我也从Web
  • python代码有1,2,3,4四个数字,能组成多少个互不相同且无重复数字的三位数?都是多少

    count 0 for i in 1 2 3 4 for j in 1 2 3 4 for k in 1 2 3 4 if i j and j k and i k count 1 print f i j k end print f 一共有
  • c++字符前面的L和_T

    c 字符前面的L和 T 字符串前面加L表示该字符串是Unicode字符串 T是一个宏 如果项目使用了Unicode字符集 定义了UNICODE宏 则自动在字符串前面加上L 否则字符串不变 因此 Visual C 里边定义字符串的时候 用 T
  • virsh console退出 KVM查看、启动、连接、关闭、删除

    1 查看运行的虚拟机 virsh list 查看所有的虚拟机 关闭和运行的 不包括摧毁的 virsh list all 2 启动虚拟机 virsh start 虚拟机名称 虚拟机随物理机启动而启动 virsh autostart 虚拟机名称
  • C#属性和索引器

    属性 C 中类的属性是为了给类中的成员变量提供访问保护的 防止不正确的访问造成程序异常 通常我们都是将成员变量设置为保护或者私有的 然后使用属性来给成员变量提供访问接口 即 class A private int interval 0 pu
  • vscode连接linux虚拟机 环境配置和常见问题解决方案

    vscode连接linux虚拟机配置环境步骤 1 自己安装vscode 傻瓜式安装 配置ssh插件 2 配置虚拟机 1 确保自己的虚拟机上的ssh服务可用 打开虚拟机 在终端命令行上敲入 sudo apt get install opens
  • 自定义v-model事件

    父组件
  • 技术至简-9:什么是脉冲调制以及脉冲幅度调制PAM与脉冲编码调制PCM的区别?

    脉冲调制 在常规的调制中 通常使用正弦波或复指数信号作为载波 来传递基带信号 而脉冲调制是 使用矩形脉冲信号作为载波 来传递基带信号 有分为两种类型 脉冲幅度调制PAM与脉冲编码调制PCM PAM 脉冲幅度调制 利用连续时间的基带时域信号去
  • h264编码算法流程

    xh264编解码 编码层 视频压缩解压缩等核心算法 VLC就是这一系列算法 得到的是编码后的比特序列 即VLC数据 网络抽象层 将压缩后的原始码流拼接头信息用于解码时来识别 安排格式以方便后续的网络传输或者介质存储 将上一步VLC数据映射成
  • springboot集成Druid之后报错:discard long time none received connection. , jdbcUrl:jdbc:mysql://lo

    整合druid的配置 在pom xml中
  • 模拟域频率与数字域频率关系

    我的书 淘宝购买链接 当当购买链接 京东购买链接 数字频率于模拟频率互相转化的公式如下 2 f
  • JS中Promise对象及其使用方式

    聚沙成塔 每天进步一点点 专栏简介 什么是 Promise 对象 如何使用 Promise 写在最后 专栏简介 前端入门之旅 探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅 这个专
  • 前端性能优化之js优化

    文章目录 引言 一 浏览器加载js文件过程 二 浏览器加载js和图片的对比 三 浏览器加载js资源占总资源加载时间的比例 四 v8的编译原理概述 五 代码层面优化 提高V8编译效率 1 函数优化 1 减少函数大小和复杂度 2 避免使用动态特
  • java ats_ATS连接 https

    HTTPS协议是Http Over SSL 简单来说就是HTTP的安全版本 在HTTP的基础上增加SSL TLS加密传输协议 通过HTTPS加密传输和身份认证保证了传输过程的安全性 在登录网银和电子邮箱时 你会常常看到地址栏的网址显示HTT
  • 梦幻模拟战更新服务器正在维护,“梦幻模拟战2.0”更新维护公告

    欢迎来到梦幻模拟战2 0 史上最丰富的更新内容即将降临 梦幻模拟战 代中异星来客雷因法鲁斯 人气角色贝蒂加入战斗 等级上限全面突破 英雄和士兵能力已经觉醒 全新版本和精彩的冒险故事在前方等着你 服务器将于8月15日1 00进行更新维护 维护
  • 浅谈JDBC的理解(一)

    一 目录 二 概述 简述 JDBC是什么 JDBC英文名为 Java Data Base Connectivity Java数据库连接 官方解释它是Java编程语言和广泛的数据库之间独立于数据库的连接标准的Java API 根本上说JDBC