JDBC相关知识

本文介绍了JDBC(JavaDatabaseConnectivity)的基本概念、原理、快速入门步骤,涵盖了Connection、Statement、ResultSet的使用,以及数据库连接池的作用、优点和注意事项。
摘要由CSDN通过智能技术生成

JDBC相关知识

总结黑马视频JDBC原理视频地址

1. JDBC简介

1.1 JDBC概念

  • JDBC就是使用Java语言操作关系型数据库的一套API(接口)
  • Java DataBase Connectivity(Java 数据库连接)

JDBC就是java用来操作关系型数据库的接口:关系型数据库有很多,如:mysql,oracle,dB2等,当同一套java代码去操作不同的关系型数据库时,如没有JDBC则无法操作,因为每个关系型数据库对应的Java代码不同,因此提出了==JDBC:规则,可保证同一套Java代码可操作不同的关系型数据库==。如图所示
在这里插入图片描述

1.2 JDBC本质

  • 官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口
  • 各个数据库厂商去实现这套接口,提供数据库驱动jar包
  • 我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类

1.3 JDBC的优点

  • 各数据库厂商使用相同的接口,Java代码不需要针对不同数据库分别开发
  • 可随时替换底层数据库,访问数据库的java代码基本不变

2. JDBC快速入门

步骤:

  1. 创建工程,导入jar包

  2. 注册驱动

    Class.forName("com.mysql.jdbc.Driver");
    
  3. 获取连接

    Connection conn=DriverManager.getConnection(url,username,password);
    
  4. 定义SQL语句

    String sql="update..."
    
  5. 获取执行SQL对象

    Statement stmt=conn.createStatement();
    
  6. 执行SQL

    stmt.executeUpdate(sql);
    
  7. 处理返回结果

  8. 释放资源
    在这里插入图片描述

    //代码实例
    package com.itheima.jdbc;
    //JDBC API
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.Statement;
    
    /**
     * JDBC快速入门
     */
    public class jdbc {
        public static void main(String[] args) throws Exception {
            //1、注册驱动,将代码用的哪个实现类加载进来
            Class.forName("com.mysql.jdbc.Driver");
            //2、获取连接
            String url="jdbc:mysql://127.0.0.1:3306/db1";//固定写法,最后是数据库的名称
            String username="root";//用户名
            String password="123456";//密码
            Connection conn=DriverManager.getConnection(url,username,password);
            //3、定义sql语句,更新account表中id=1的对应的money的数值为2000
            String sql="update account set money=2000 where id=1";
            //4、获取执行sql的对象Statement
            Statement stmt=conn.createStatement();
            //5、执行sql
            int count=stmt.executeUpdate(sql);
            //6、处理结果:返回受影响的行数来确定执行是否成功
            System.out.println(count);
            //7、释放资源,按照顺序,最先生成的最后释放
            stmt.close();
            conn.close();
        }
     
    }
    

3. JDBC的API详解

3.1 DriverManager:驱动管理类
  • 注册驱动:DriverManager.registerDriver()

    //1、注册驱动,将代码用的哪个实现类加载进来
    Class.forName("com.mysql.jdbc.Driver");
    //查看Driver源码:
    static{
    try{
    DriverManager.registerDriver(new Driver());
    }catch(SQLException var1){
        throw new RuntimeException("Can't register driver!")
    }
    }
    

    提示:

    • MySQL5之后的驱动包,可以省略注册驱动的步骤
    • 自动加载jar包中META-INF/services/java.sql.Driver文件中的驱动类
  • 获取连接

    DriverManager.getConnection(String url,String user,String password);
    //参数说明
    /**
    1、url:连接路径
    语法:jdbc:mysql://ip地址(域名):端口号/数据库名称?参数键值对1&参数键值对2...
    示例:jdbc:mysql://127.0.0.1:3306/db1
    细节:
    ①如果连接的是本机sql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称?参数键值对
    ②配置useSSL=false参数,禁用安全连接方式,解决警告提示
    2、user:用户名
    3、password:密码
    */
    
3.2 Connection:数据库连接对象
  • 获取执行SQL的对象

    • 普通执行SQL对象

    Statement creatStatement()

    • 预编译SQL的执行SQL对象:防止SQL注入

    PreparedStatement preparedStatement(sql)

    • 执行存储过程的对象

    CallableStatement prepareCall(sql)

  • 事务管理

    • MySQL事务管理

    开启事务:BEGIN;/START TRANSACTION;

    提交事务:COMMIT

    回滚事务:ROLLBACK

    • JDBC事务管理:Connection接口中定义了3个对应的方法

    开启事务:setAutoCommit(bollean sutoCommit):true为自动提交事务;false为手动提交事务,即为开启事务

    提交事务:commit()

    回滚事务:rollback()

    package com.itheima.jdbc;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.Statement;
    
    /**
       * JDBC的Connection API:事务管理详解
    */
    public class Jdbc_Connection {
       public static void main(String[] args) throws Exception {
           //1、注册驱动,将代码用的哪个实现类加载进来
           Class.forName("com.mysql.jdbc.Driver");
           //2、获取连接
           String url="jdbc:mysql://127.0.0.1:3306/db1";//固定写法,最后是数据库的名称
           String username="root";//用户名
           String password="123456";//密码
           Connection conn=DriverManager.getConnection(url,username,password);
           //3、定义sql语句,更新account表中id=1的对应的money的数值为2000
           String sql1="update account set money=2000 where id=1";
           String sql2="update account set money=2000 where id=2";
           //4、获取执行sql的对象Statement
           Statement stmt=conn.createStatement();
    
           try {
               //开启事务
               conn.setAutoCommit(false);
               //5、执行sql
               int count1 = stmt.executeUpdate(sql1);
               //6、处理结果:返回受影响的行数来确定执行是否成功
               System.out.println(count1);
               //设置异常
               int a = 1 / 0;
               int count2 = stmt.executeUpdate(sql2);
               System.out.println(count2);
               //提交事务
               conn.commit();
           } catch (Exception throwables) {
               //回滚事务
               conn.rollback();
               throwables.printStackTrace();
           }
           //7、释放资源
           stmt.close();
           conn.close();
       }
    }
    
    
3.3 Statement
  • 执行SQL语句

    int executeUpdate(sql):执行DML,DDL语句

    • 返回值:
      1. DML语句影响的行数
      2. DDL语句执行后,执行成功也可能返回0
    package com.itheima.jdbc;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    public class Jdbc_Statement {
     /**
         * 执行DML语句:修改,添加,删除
         * @throws Exception
      */
    @Test
     public void testDML() throws Exception {
        //1、注册驱动,将代码用的哪个实现类加载进来
        Class.forName("com.mysql.jdbc.Driver");
        //2、获取连接
        String url="jdbc:mysql://127.0.0.1:3306/db1";//固定写法,最后是数据库的名称
        String username="root";//用户名
        String password="123456";//密码
        Connection conn=DriverManager.getConnection(url,username,password);
        //3、定义sql语句,更新account表中id=1的对应的money的数值为2000
        String sql="update account set money=2000 where id=1";
        //4、获取执行sql的对象Statement
        Statement stmt=conn.createStatement();
        //5、执行sql
        int count=stmt.executeUpdate(sql);
        //6、处理结果:执行DML语句后返回受影响的行数来确定执行是否成功
        //System.out.println(count);
        if(count>0){
            System.out.println("修改成功");
        }else{
            System.out.println("修改失败");
        }
        //7、释放资源
        stmt.close();
        conn.close();
     }
     /**
         * 执行DDL语句:数据库定义语言:创建、删除、修改:数据库、表等
         * @throws Exception
      */
     @Test
     public void testDML() throws Exception {
         //1、注册驱动,将代码用的哪个实现类加载进来
         Class.forName("com.mysql.jdbc.Driver");
         //2、获取连接
         String url="jdbc:mysql://127.0.0.1:3306/db1";//固定写法,最后是数据库的名称
         String username="root";//用户名
         String password="123456";//密码
         Connection conn=DriverManager.getConnection(url,username,password);
         //3、定义sql语句,更新account表中id=1的对应的money的数值为2000
         String sql="create database db2";
         //4、获取执行sql的对象Statement
         Statement stmt=conn.createStatement();
         //5、执行sql
         int count=stmt.executeUpdate(sql);
         //6、处理结果:执行DML语句后返回受影响的行数来确定执行是否成功
         //System.out.println(count);
         if(count>0){
             System.out.println("修改成功");
         }else{
             System.out.println("修改失败");
         }
         //7、释放资源
         stmt.close();
         conn.close();
     }
    }
    
    
    • ResultSet executQuery(sql):执行DQL语句
      1. 返回值:ResultSet 结果集对象
3.4 ResultSet
  • ResultSet(结果集对象)作用:

    1. 封装了DQL查询语句的结果

      ResultSet stmt.executeQuery(sql):执行DQL语句,返回ResultSet对象
      
      
  • 获取查询结果

    boolean next():(1)将光标从当前位置向前移动一行(2)判断当前行是否为有效行
    返回值:
    1. true:有效行,当前行有数据
    2. false:无效行,当前行没有数据
    
    
    xxx getXxx(参数):获取数据
    xxx:数据类型;:int getInt(参数);String getString(参数)
    参数:int:列的编号,1开始;String:列的名称
    
    
  • 使用步骤

    /**
    1.游标向下移动一行,并判断该行是否有数据:next()
    2.获取数据:getXxx(参数)
    */
    //循环判断游标是否是最后一行末尾
    while(rs.next()){
        //获取数据
        rs.getXxx(参数);
    }
    
    
//实例1:
public class Jdbc_ResultSet {
   @Test
    public void testResultSet() throws Exception {
       //1、注册驱动,将代码用的哪个实现类加载进来
       Class.forName("com.mysql.jdbc.Driver");
       //2、获取连接
       String url="jdbc:mysql://127.0.0.1:3306/db1";//固定写法,最后是数据库的名称
       String username="root";//用户名
       String password="123456";//密码
       Connection conn=DriverManager.getConnection(url,username,password);
       //3、定义sql
       String sql="select * from account";
       //4、获取执行对象
       Statement stmt=conn.createStatement();
       //5、执行sql
       ResultSet rs=stmt.executeQuery(sql);
       //6、处理结果,遍历rs中的所有数据
       //6、1光标向下移动
/*       while(rs.next()){
           int id=rs.getInt(1);
           String name=rs.getString(2);
           int money=rs.getInt(3);
           System.out.println(id);
           System.out.println(name);
           System.out.println(money);
           System.out.println("==========");
       }*/
       while(rs.next()){
           int id=rs.getInt("id");
           String name=rs.getString("name");
           int money=rs.getInt("money");
           System.out.println(id);
           System.out.println(name);
           System.out.println(money);
           System.out.println("==========");
       }
       //7、释放资源
       rs.close();
       stmt.close();
       conn.close();
    }

//实例2:需求:查询account账户表数据,封装为Account对象中,并且存储到ArrayList集合中
package com.itheima.jdbc;

import com.itheima.pojo.Account;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
public class Jdbc_ResultSet {
    /**
     * :需求:查询account账户表数据,封装为Account对象中,并且存储到ArrayList集合中
     * 1、定义实体类Account
     * 2、查询数据,封装到Account对象中
     * 3、将Account对象存入ArrayList集合中
     * @throws Exception
     */
    @Test
    public void testResultSet2() throws Exception {
        //1、注册驱动,将代码用的哪个实现类加载进来
        Class.forName("com.mysql.jdbc.Driver");
        //2、获取连接
        String url="jdbc:mysql://127.0.0.1:3306/db1";//固定写法,最后是数据库的名称
        String username="root";//用户名
        String password="123456";//密码
        Connection conn=DriverManager.getConnection(url,username,password);
        //3、定义sql
        String sql="select * from account";
        //4、获取执行对象
        Statement stmt=conn.createStatement();
        //5、执行sql
        ResultSet rs=stmt.executeQuery(sql);
        //创建一个集合
        List<Account> list=new ArrayList<>();
        //6、处理结果,遍历rs中的所有数据

        while(rs.next()){

            //对象的创建
            Account account=new Account();

            int id=rs.getInt("id");
            String name=rs.getString("name");
            double money=rs.getInt("money");
            account.setId(id);
            account.setName(name);
            account.setMoney(money);
            //存入集合
            list.add(account);
        }
        //7、释放资源
        rs.close();
        stmt.close();
        conn.close();
    }

}



实例2需求图:
在这里插入图片描述

4. 数据库连接池

4.1 什么是连接池

数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。

4.2 为什么要使用连接池

数据库连接-问题:数据库连接是一种关键、有限、昂贵的资源。一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样会造成系统的性能低下。

数据库连接池-解决方案:在应用程序启动时建立足够的数据库连接,并将这些连接组成一个连接池(即在一个“池”中存放好多半成品的数据库连接对象),由应用程序动态地对池中的连接进行申请、使用和释放。当出现并发请求时(即连接对象数大于连接池中的连接数时),应该在请求队列中排队等待。此外,应用程序可以根据池中连接的使用率,动态地增加或减少池中的连接数。

连接池技术-优点:尽可能多地重用了消耗内存地资源,大大节省了内存,提高了服务器地服务效率,能够支持更多的客户服务。通过使用连接池,将大大提高程序运行效率,同时,可通过其自身的管理机制来监视数据库连接的数量、使用情况等。
在这里插入图片描述

4.3传统的连接机制与数据库连接池的运行机制区别

不使用连接池流程

以访问MySQL为例,执行一个sql命令,若不使用连接池,需要经过哪些流程。

img

步骤:

  1. TCP建立连接的三次握手(服务端和客户端建立连接)
  2. MySQL认证的三次握手
  3. 真正的SQL执行
  4. MySQL的关闭
  5. TCP的四次握手关闭

优点:

  • 实现简单

缺点:

  • 网络IO较多
  • 数据库的负载较高
  • 响应时间(RT,Response Time,表示处理请求快慢的指标)较长且QPS(Query per Second,直接反应应用吞吐能力的指标)较低。
  • 应用频繁的创建链接和关闭连接,导致临时对象较多,GC(Garbage collection,垃圾收集)频繁
  • 在关闭连接后,会出现大量TIME_WAIT(等待时间)的TCP状态(在2各MSL之后关闭)
使用连接池流程

img

步骤:第一次访问的时候,需要建立连接。但是之后的访问,均会复用之前创建的连接,直接执行SQL语句。

优点:

  • 减少了网络开销
  • 系统的性能会有一个实质的提升
  • 不存在TIME_WAIT状态

4.4数据库连接池工作原理

  1. 连接池的建立

    • 一般在系统初始化时,连接池会根据系统配置建立,并在池中创建了几个连接对象,一遍使用时能从连接池中获取。连接池中的连接不能随意创建和关闭,这样避免了连接随意建立和关闭造成的系统开销。Java中提供了很多容器类可以方便的构建连接池,例如Vector,Stack等。
  2. 连接池中连接的使用管理(核心)

    • 客户请求数据库连接时:查看连接池中是否有空闲连接
      • 若存在空间连接,则将连接分配给客户使用
      • 若无空闲连接,则查看当前所开的连接数是否已达到最大连接数
        • 若没达到,重新创建一个连接给请求的客户
        • 若达到,按照设定的最大等待时间进行等待,若超出最大等待时间,则抛出异常给客户。
    • 客户释放数据库连接时:判断该连接的引用次数是否超过了规定值
      • 若超过,则直接从连接池删除该连接
      • 若没超过,则保留后续为客户服务

    该管理策略保证了数据库连接的有效复用,避免频繁的建立、释放连接所带来的系统资源开销。

  3. 连接池的关闭

    • 当应用程序退出时,关闭连接池中所有连接,释放连接池相关的资源,该过程与创建相反。

4.5 连接池的主要参数

  1. 最小连接数:是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费
  2. 最大连接数:是连接池能申请的最大连接数,如果数据库连接请求超过次数,后面的数据库连接请求将被加入到等待队列中,这回影响以后的数据库操作
  3. 最大空闲时间
  4. 获取连接超时时间
  5. 超时重试连接次数

4.6 连接池需要注意的点

1. 并发问题

问题:为了使连接管理服务具有最大的通用性,必须考虑多线程环境,即并发问题。

解决:各自语言自身提供了对并发管理的支持,利用synchronized(java),lock(C#)关键字即可确保线程使同步的。

2. 事务处理

事务具有原子性:对数据库的操作符合“ALL-OR-NOTHING”原则,即对于一组SQL语句要么全做,要么全不做。

当2个线程共用一个连接Connection对象,而且各自都有自己的事务要处理时候,对于连接池来说很麻烦。原因为:即使Connection类提供了相应的事务支持,可是我们仍然不确定那个数据库操作是对应那个事务的,这是优于我们有2个线程都在进行事务操作而引起的。因此,我们可以使用每一个事务独占一个连接来实现,虽然这种方法有点浪费连接池资源但是可以大大降低事务的复杂性。

3. 连接池的分配与释放

连接池的分配与释放,对系统的性能有很大的影响。合理的分配与释放,可以提高连接的服用都,从而降低建立新连接的开销,同时还可以加快用户的访问速度。

对于连接的管理可使用一个List。即把已经创建的连接都放入List中去统一管理。每当用户请求一个连接时,系统检查这个List中有没有可以分配的连接。如果有就把那个最合适的连接分配给它,如果没有就抛出一个异常给用户。

4. 连接池的配置与维护

连接池中放置多少连接,才能使系统的性能最佳?

可采取设置最小连接数和最大连接数等参数来空间。说明:最小连接数是系统启动时连接池所创建的连接数。如果创建过多,则系统启动就慢,但创建后系统的响应速度会很快;如果创建过少,则系统启动的很快,响应起来确慢。这样,在开发时,设置较小的最小连接数,开发起来会很快,而在系统实际使用时设置较大的,因为这样对访问客户来说速度会快些。

  • 最大连接数:连接池中允许连接的最大数目,具体设置多少,要看系统的访问量,可通过软件需求上得到。
  • 最小连接数:动态和静态两种策略
    • 动态:每隔一段时间就对连接池进行检测,如果发现连接数量小于最小连接数,则补充相应数量的新连接,以保证连接池的正常运转。
    • 静态:发现空闲连接不够时再去检查。
  • 38
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值