彻底搞懂JDBC连接数据库

写在前面:

我是「沸羊羊_」,昵称来自于姓名的缩写 fyy ,之前呕心沥血经营的博客因手残意外注销,现经营此账号。
本人是个小菜,正向着全栈工程师的方向努力着,文章可能并不高产,也很基础,但每写一篇都在用心总结,请大佬勿喷。
如果您对编程有兴趣,请关注我的动态,一起学习研究。
感谢每位读者!

JDBC简介

Java数据库连接,(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC也是Sun Microsystems的商标。我们通常说的JDBC是面向关系型数据库的。

JDBC连接数据库

//连接数据库
public class JdbcDemo1 {
    public static void main(String[] args) throws Exception {
        //1、导入驱动jar包
        //mysql-connector-java.jar
        //2、注册驱动(将异常抛出)        
        Class.forName("com.mysql.jdbc.Driver");
        //3、获取数据库连接对象        
        Connection conn = DriverManager.getConnection("jdbc:mysql://xxx.xxx.xxx.xxx:3306/dbName?characterEncoding=utf-8","root","123456");
        //4、定义sql语句        
        String sql = "update worker_info set WorkerName = '测试' where WorkerId = 3456";
        //5、获取执行 sql 的对象 Statement        
        Statement stmt = conn.createStatement();
        //6、执行 sql        
        int count = stmt.executeUpdate(sql);
        //7、处理结果        
        System.out.println(count);
        //8、释放资源        
        stmt.close();
        conn.close();
    }
}

1、Class.forName(" ");

当我们导入完jar包,连接数据库的第一个步骤就是 Class.forName,它有什么作用?

Class.forName() 方法要求JVM查找并加载指定的类到内存中,此时将"com.mysql.jdbc.Driver" 当做参数传入,就是告诉JVM,去"com.mysql.jdbc"这个路径下找Driver类,将其加载到内存中。

由于加载类文件时会执行其中的静态代码块,其中Driver类的源码如下。

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
            DriverManager.registerDriver(new Driver());//首先new一个Driver对象,并将它注册到DriverManage中
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

接下来我们再看看这个DriverManager.registerDriver 方法。

public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException {
    registerDriver(driver, null);
}

继续看registerDriver方法做了什么。

private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();// registeredDrivers 是一个支持并发的arraylist
public static void registerDriver(java.sql.Driver driver, DriverAction da) throws SQLException {
   if (driver != null) {
        //如果该驱动尚未注册,那么将他添加到 registeredDrivers 中去。这是一个支持并发情况的特殊ArrayList
        registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
    } else {
        // This is for compatibility with the original DriverManager
        throw new NullPointerException();
    }
    println("registerDriver: " + driver);
}

此时,Class.forName(“com.mysql.jdbc.Driver”) 的工作就完成了,工作就是:将mysql驱动注册到DriverManager中去。

2、Connection conn = DriverManager.getConnection();

Connection类

功能

  • 获取执行sql的对象
    • conn.createStatement();
  • 管理事务
    • 开启事务:setAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务。
    • 提交事务:commit();
    • 回滚事务:rollback();

getConnection()方法

将mysql驱动注册到DriverManager中之后,我们就可以通过DriverManager的getConnection方法获得mysql的连接了:

Connection conn = DriverManager.getConnection("jdbc:mysql://xxx.xxx.xxx.xxx:3306/dbName?characterEncoding=utf-8","root","123456");

接下来看看 getConnection()方法。

@CallerSensitive
public static Connection getConnection(String url, String user, String password) throws SQLException {
  ....
  return (getConnection(url, info, Reflection.getCallerClass()));
}

DriverManager调用了自身的 getConnection()方法。

private static Connection getConnection(String url, java.util.Properties info, Class<?> caller) throws SQLException {
   ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
   synchronized(DriverManager.class) {
       // synchronize loading of the correct classloader.
       if (callerCL == null) {
           callerCL = Thread.currentThread().getContextClassLoader();
       }
   }
   // Walk through the loaded registeredDrivers attempting to make a connection.
   // Remember the first exception that gets raised so we can reraise it.
   SQLException reason = null;

   for(DriverInfo aDriver : registeredDrivers) {
       // If the caller does not have permission to load the driver then skip it.
       if(isDriverAllowed(aDriver.driver, callerCL)) {
           try {
               Connection con = aDriver.driver.connect(url, info);
               if (con != null) {
                   // Success!
                   return (con);
               }
           } catch (SQLException ex) {
               if (reason == null) {
                   reason = ex;
               }
           }
       } else {
           println("skipping: " + aDriver.getClass().getName());
       }
   }

   // if we got here nobody could connect.
   if (reason != null)    {
       println("getConnection failed: " + reason);
       throw reason;
   }
   throw new SQLException("No suitable driver found for "+ url, "08001");
}

可以看到它对上文提到的静态变量 registeredDrivers 进行了遍历,调用了connect(url, info)方法,这是一个接口,由各个不同的驱动自己实现。

/**
 * Attempts to make a database connection to the given URL.
 * The driver should return "null" if it realizes it is the wrong kind
 * of driver to connect to the given URL.  This will be common, as when
 * the JDBC driver manager is asked to connect to a given URL it passes
 * the URL to each loaded driver in turn.
 */
Connection connect(String url, java.util.Properties info)
    throws SQLException;

到此为止,我们就获得了connection对象,现在就可以对数据库进行操作了。

3、Statement stmt = conn.createStatement();

连接数据库时,需要获取一个可以执行sql语句的对象,Connection类的createStatement 方法就是创建一个 statement 对象来将SQL语句发送到数据库。

createStatement()方法有很多重载方法,最常用的就是执行不带参数的SQL语句。

简单介绍除无参的createStatement()方法外的两个重载方法。

重载createStatement()方法

语法一:

创建一个 Statement 对象,该对象将生成具有给定类型和并发性的 ResultSet 对象。此方法与上述 createStatement 方法相同,但它允许重写默认结果集类型和并发性。已创建结果集的可保存性可调用 getHoldability() 函数来确定。

Statement createStatement(int resultSetType,int resultSetConcurrency)throws SQLException

参数说明:

  • resultSetType:结果集类型,主要包括 ResultSet.TYPE_FORWARD_ONLY、ResultSet.TYPE_SCROLL_INSENSITIVE、ResultSet.TYPE_SCROLL_SENSITIVE。
  • resultSetConcurrency:并发类型,主要包括 ResultSet.CONCUR_READ_ONLY、ResultSet.CONCUR_UPDATABLE。

示例:

本示例获得了一个 Statement 对象,并发类型为只读,结果集类型光标只能向前移动。

Connection conn = ……  //省略部分代码
Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);  //创建Statement对象

语法二:

Statement createStatement(int resultSetType,int resultSetConcurrency,int resultSetHoldability)throws SQLException

参数说明:

  • resultSetType 结果集类型:ResultSet.TYPE_FORWARD_ONL 或 ResultSet.TYPE_SCROLL_INSENSITIVE 或 ResultSet.TYPE_SCROLL_SENSITIVE 之一。
  • resultSetConcurrency 并发类型:ResultSet.CONCUR_READ_ONLY 或 ResultSet.CONCUR_UPDATABLE。
  • resultSetHoldability ResultSet 常量之一:ResultSet.HOLD_CURSORS_OVER_COMMIT 或 ResultSet.CLOSE_CURSORS_AT_COMMIT。

示例:

本示例获得了一个 Statement 对象,并发类型为只读,结果集类型光标只能向前移动,具有此可保存性的打开的 ResultSet 对象将保持开放。

Connection conn = ……  //省略部分代码  //创建Statement对象
Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT);

4、int count = stmt.executeUpdate(sql);

Statement类

  • boolean execute(String sql):可以执行任意SQL
  • int executeUpdate(String sql):指定DML(insert,update,delete)语句,DDL(create,alert,drop)语句。返回值是影响的行数。
  • ResultSet executeQuery(String sql):指定DQL(select)语句

ResultSet类

  • boolean next():游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是返回 false,如果不是,返回true
  • XXX getXXX(参数):获取数据
    • XXX代表数据类型,如 int getInt() , String getString()
    • 参数:
      • int :代表列的编号,从1开始,如:getString(1)
      • String :代表列的名称,如getDouble(“balance”)

正确打开方式:

ResultSet rs = null;
//循环判断游标是否指向最后一行末尾
while (rs.next()){    
//获取数据
}

抽取JDBC工具类

目的:

  • 复用
  • 简化书写

实现:

//JDBCUtil.java
package cn.itcast.util;
import com.sun.javafx.css.CssError;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.sql.*;
import java.util.Properties;
/*
• JDBC工具类
• */
public class JDBCUtil {
private static String url;
private static String user;
private static String password;
private static String driver;
    /*
    • 文件读取,只需要读取一次即可,使用静态代码块
    • */
    static {
        //读取资源文件,获取值
        try {
              //1、创建Properties 集合类
              Properties pro = new Properties();  
              //获取src路径下的文件方式--> ClassLoader 类加载器
              ClassLoader classLoader = JDBCUtil.class.getClassLoader();
              //使用 类加载器 获取资源
              URL res = classLoader.getResource("jdbc.properties");
              //不符合条件即断言
              assert res != null;
              //获取路径
              String path = res.getPath();
              //2、加载文件
              //pro.load(new FileReader("src/jdbc.properties"));
              pro.load(new FileReader(path));
              //3、获取数据,赋值
              url = pro.getProperty("url");
              user = pro.getProperty("user");
              password = pro.getProperty("password");
              driver = pro.getProperty("driver");
              //4、注册驱动
              Class.forName(driver);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    //获取连接
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url,user,password);
    }
    //释放资源
    public static void close(Statement stmt,Connection conn){
        if (stmt!=null){
            try {
                stmt.close();
            }catch (SQLException e){
                e.printStackTrace();
            }
        }
        if (conn!=null){
            try {
                conn.close();
            }catch (SQLException e){
                e.printStackTrace();
            }
        }
    }
    public static void close(ResultSet rs,Statement stmt, Connection conn){
        if (rs!=null){
            try {
                rs.close();
            }catch (SQLException e){
                e.printStackTrace();
            }
        }
        if (stmt!=null){
            try {
                stmt.close();
            }catch (SQLException e){
                e.printStackTrace();
            }
        }
        if (conn!=null){
            try {
                conn.close();
            }catch (SQLException e){
                e.printStackTrace();
            }
        }
    }
}

最后

以上为小编学习总结记录,如有错误,请大佬指出,不胜感激。

猜你喜欢

转载自blog.csdn.net/weixin_42653522/article/details/107394405