JDBC详解

本文详细介绍了JDBC(Java数据库连接)的概念、使用步骤,包括注册数据库驱动、建立连接、创建Statement对象、执行SQL语句、遍历结果集以及关闭连接。在实际操作中,特别强调了异常处理的重要性,包括Class.forName()方法避免重复注册驱动,以及在finally块中正确关闭资源以避免资源泄露。此外,还讨论了可能遇到的问题和解决方案,如空指针异常和确保资源关闭的策略。
摘要由CSDN通过智能技术生成
一、介绍

为了能让 程序 操作 数据库,对数据库中的 进行操作,每一种数据库都会提供一套连接和操作该数据库的驱动。而且每种数据库的驱动都各不相同,例如:mysql数据库使用mysql驱动,oracle数据库使用oracle驱动。假如我们编写的程序哪一天想要换数据库,那样就会很不方便,因为所有连接数据库的代码都要从新编写。

SUN公司为简化开发工作,统一对数据库的操作,定义了一套Java操作数据库的标准或者规范,这个规范就是JDBC

JDBC全称为:Java Data Base Connectivity(Java数据库连接),它主要由接口组成。我们在开发过程中,只要实现它相应的接口就可以非常简便快速地连接数据库。在开发JDBC应用时,还需要导入相应的数据库的驱动jar包,这些驱动jar包是由数据库公司自己编写的。

二、使用
1、在项目中导入相应数据库的驱动jar包
2、在JDBC中注册数据驱动

虽然我们第一步在新建的Java项目中将mysql数据库的驱动jar包导入进来了,但是JBDC不知道这里有一个驱动包,此时我们就需要将这个驱动包交给JBDC去管理。我们可以使用java.sql包下的 DriverManager 工具类提供的registerDriver(Driver driver) 方法来在JDBC中注册这个数据驱动。这个registerDriver(Driver driver)方法需要一个Driver对象,而这个Driver类本身是JDBC提供的一个接口,我们的驱动里面已经实现了这个接口,所以我们只需要写如下代码就可以实现注册数据库驱动的功能

import java.sql.DriverManager; //需要导入的是接口类包
DriverManager.registerDriver(new Driver());
3、获取(创建)数据库的连接

注册好数据库驱动后,并没有连接上数据库。只有先连接上数据库,才能对数据库进行操作

import java.sql.Connection; //导入的是接口类包
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root"); //这里使用的是一个名为test的mysql数据库,用户名和密码都是root
4、创建传输器对象

上一步我们已经创建了数据库的连接,并已经连上了数据库。但是如果我们想要操作该数据库,我们需要用到sql语句。而我们怎样在Java程序中使用sql语句来操作数据库呢??这里我们就需要一个传输器对象来传输sql语句到数据库中去执行。上一步使用到的Connection类中就有一个createStatement()的方法可以创建一个传输器对象

import java.sql.Statement; //导入的是接口类包
Statement stat = conn.createStatement();
5、利用传输器对象传输sql语句到数据库中执行操作,将结果用结果集返回

java.sql.Statement 身上有许多传输sql语句的方法,其中用的最多的是:

  • executeQuery(String sql) :用于向数据发送查询语句
  • executeUpdate(String sql):用于向数据库发送insert、update或delete语句
  • execute(String sql):用于向数据库发送任意sql语句
import java.sql.ResultSet; //需要导入的接口类包
//传输一条查询语句,查询book表中所有的数据
ResultSet rs =  stat.executeQuery("select * from book"); 
6、遍历结果集,并获取查询对象

JDBC程序中的ResultSet用于代表Sql语句的执行结果。Resultset封装执行结果时,采用了类似于表格的方式。ResultSet对象维护了一个指向表格数据行的游标。初始的时候,游标在第一行之前,调用ResultSet.next() 方法,可以使游标指向具体的数据行,进而调用方法获取该行的数据。

ResultSet对象提供的方法:

获取指定类型的数据

  • getString(int index)
  • getString(String columnName)

ResultSet还提供了对结果集进行滚动的方法

  • next():移动到下一行
  • Previous():移动到前一行
  • absolute(int row):移动到指定行
  • beforeFirst():移动到resultSet的最前面
  • afterLast() :移动到resultSet的最后面
while(rs.next())
 {
     String name = rs.getString("name");
     System.out.println(name);
}
7、关闭连接(先创建的后关闭)

JDBC程序运行完后,切记要释放程序在运行过程中创建的那些与数据库进行交互的对象,这些对象通常是ResultSet, Statement和Connection对象

特别是Connection对象,它是非常稀有的资源,用完后必须马上释放。如果Connection不能及时、正确的关闭,极易导致系统宕机。Connection的使用原则尽量晚创建,尽量早的释放

rs.close(); // rs表示结果集
stat.close();
conn.close();
三、完整代码
  import java.sql.Connection;
  import java.sql.DriverManager;
  import java.sql.ResultSet;
  import java.sql.SQLException;
  import java.sql.Statement;
 //不能导入 java.sql 中的 Driver 接口,要导入驱动jar包中实现该接口的类,只有这要才能注册相对应的数据库驱动
 import com.mysql.jdbc.Driver; 
 
 public class JDBCTest {
     public static void main(String[] args) throws SQLException  {
         //1.注册数据库驱动
         DriverManager.registerDriver(new Driver());
         //2.获取数据库的连接 
         Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false", "root", "root");
         //3.创建传输器对象
         Statement  stat = conn.createStatement();
         //4.利用传输器对象传输sql语句到数据库中执行操作,将结果用结果集返回
          ResultSet rs =  stat.executeQuery("select * from book");
         //5.遍历结果集,并获取查询结果
         while(rs.next()) {
             String name = rs.getString("name");
             System.out.println(name);
         }
         //6.关闭连接(后开先关)
         rs.close(); 
         stat.close();
         conn.close();
     }    
}
四、上述代码会遇到的问题
1、注册数据库驱动方法不当,导致出现了两次注册

Driver类源码:

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
  //
  // Register ourselves with the DriverManager
  //
  static {
     try {
            java.sql.DriverManager.registerDriver(new Driver());
         } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
         }
   }
}

查看Driver类源码,可以看到第7行代码中mysql驱动在Driver类的实现中自己注册了一次,而我们在自己的代码中也注册了一次,因此导致了注册两次。

解决办法:
使用Class.forName() 方法将mysql中已经实现的Driver类加载到程序中来,由于Driver类在实现接口时使用的是静态代码块,而静态代码块只会在类加载的时候执行一次,即保证了数据库驱动只会被注册一次,同时不用导入mysql驱动里的类包,程序通用性提高

Class.forName("com.mysql.jdbc.Driver");
2、忽略了程序中可能会抛出的异常(最大的问题)

在执行程序时,它的许多方法的调用都会抛出异常。如果它抛出异常后,没有做相应的处理(catch 这个异常),那么程序就会中断执行。Statement对象和Connection对象就没有被关闭,而我们知道Connection对象,它是非常稀有的资源,用完后必须马上释放,如果Connection不能及时、正确的关闭,极易导致系统宕机。所以,我们需要保证无论程序中哪一步出现了异常导致程序中断,连接关闭的代码都会被执行,此时我们就会想到异常处理中的finally代码块,先 try 住然后 catch 异常,最后执行 finally 代码块。

修改之后,发现每个close() 都提示有异常要处理,此时我们也直接 try/catch 每个异常

修改后的代码:

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

public class JDBCTest {
    public static void main(String[] args)  {
        Connection conn = null;
        Statement stat = null;
        ResultSet rs = null;
        try {
                //1.注册数据库驱动
                Class.forName("com.mysql.jdbc.Driver"); // ???
                //2.获取数据库的连接
                 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false", "root", "root");
                //3.创建传输器对象
                  stat = conn.createStatement();
                //4.利用传输器对象传输sql语句到数据库中执行操作,将结果用结果集返回
                  rs =  stat.executeQuery("select * from book");
                //5.遍历结果集,并获取查询结果
                while(rs.next()) {
                    String name = rs.getString("name");
                    System.out.println(name);
                }
        }catch(Exception e) {
            e.printStackTrace();
        }finally {
            //6.关闭连接(后开先关)
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                stat.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
3、程序中易被忽略的异常

由于在程序开头先声明了三个对象的引用,并且都赋值为null,假如程序在执行到注册数据库这一步时就抛出了异常,此时catch到这个异常后执行finally代码块,结果发现ResultSet对象的引用,Connection对象的引用以及Statement对象的引用都是空值,调用这个对象上的方法就会抛出空指针异常

close()这个方法身上也有异常,如果我们不做相应的异常处理,那些对象还是不能被正常关闭

解决办法:
为了防止出现空指针异常,我们可以先判断对象的引用是否为null,如果不为null,则执行异常处理代码

在每个close()异常处理后加上一个finally静态代码块,将每个相应对象的引用值置为null。原理是:如果程序执行到close() 方法并抛出了异常,那么最后finally代码块执行,给该对象的值置为null,由于这个对象没有任何引用指向它,它就成为了垃圾对象,JVM垃圾回收器就会回收这个对象资源,这个对象也就关闭了

最终代码:

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

public class JDBCTest {
    public static void main(String[] args)  {
        Connection conn = null;
        Statement stat = null;
        ResultSet rs = null;
        try {
                //1.注册数据库驱动
                Class.forName("com.mysql.jdbc.Driver");
                //2.获取数据库的连接
                 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false", "root", "root");
                //3.创建传输器对象
                  stat = conn.createStatement();
                //4.利用传输器对象传输sql语句到数据库中执行操作,将结果用结果集返回
                  rs =  stat.executeQuery("select * from book");
                //5.遍历结果集,并获取查询结果
                while(rs.next()) {
                    String name = rs.getString("name");
                    System.out.println(name);
                }
        }catch(Exception e) {
            e.printStackTrace();
        }finally {
            //6.关闭连接(后开先关)
            if(rs != null) {
                    try {
                        rs.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    } finally {
                        rs = null;
                    }
            }
            if(stat != null) {
                    try {
                        stat.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }finally {
                        stat = null;
                    }
            }
            if(conn != null) {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }finally {
                        conn = null;
                    }
            }
        } //--finally
    } //--main
}//--class
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值