JDBC的基本使用

1、JDBC和JDBC驱动的基本概念

JDBC(Java DataBase Connectivity),指 Java 数据库连接,是一种标准Java应用编程接口(JAVA API),是 Java 语言用来连接和操作数据库的。使用Java程序访问数据库时,Java代码并不是直接通过TCP连接去访问数据库,而是通过JDBC接口来访问。而JDBC接口则通过JDBC驱动来实现真正对数据库的访问。

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

JDBC 是一套接口规范,它在Java的标准库java.sql里放着,不过这里面大部分都是接口。接口并不能直接实例化,而是必须实例化对应的实现类,然后通过接口引用这个实例。JDBC接口的实现类就是 JDBC 驱动。JDBC接口并不知道我们要使用哪个数据库,所以,用哪个数据库,我们就去使用哪个数据库的“实现类”,我们把某个数据库实现了JDBC接口的jar包称为 JDBC 驱动

例如,我们在Java代码中要访问MySQL,那么必须编写代码操作JDBC接口。注意到JDBC接口是Java标准库自带的,所以可以直接编译。而具体的JDBC驱动是由数据库厂商提供的,例如,MySQL的JDBC驱动由Oracle提供。因此,访问某个具体的数据库,我们只需要引入该厂商提供的JDBC驱动,就可以通过JDBC接口来访问,这样保证了Java程序编写的是一套数据库访问代码,却可以访问各种不同的数据库,因为他们都提供了JDBC驱动:

如果从代码上来看,Java标准库自带的JDBC接口其实就是定义了一组接口,而某个具体的JDBC驱动其实就是实现了这些接口的类:

实际上,一个MySQL的JDBC的驱动就是一个jar包,它本身也是纯Java编写的。我们自己编写的代码只需要引用Java标准库提供的java.sql包下面的相关接口,由此再间接地通过MySQL驱动的jar包通过网络访问MySQL服务器,所有复杂的网络通讯都被封装到JDBC驱动中,因此,Java程序本身只需要引入一个MySQL驱动的jar包就可以正常访问MySQL服务器:

1.1、使用JDBC的好处

JDBC API 是一个 Java API,它可以访问任何类型的表格数据,特别是可以访问存储在关系数据库里的数据。JDBC 可以用 Java 语言在各种平台上实现,比如 Windows 系统, Mac OS 系统,和各种版本的 UNIX 系统。

并且:

  • 各数据库厂商使用相同的接口,Java代码不需要针对不同数据库分别开发;

  • Java程序编译期仅依赖java.sql包,不依赖具体数据库的jar包;

  • 可随时替换底层数据库,访问数据库的Java代码基本不变。

 

2、下载导入 JDBC 驱动

2.1、下载 JDBC 驱动

可参考:https://www.cnblogs.com/NyanKoSenSei/p/11510438.html

2.2、将 JDBC 驱动导入项目

将 JDBC 驱动导入项目就跟将普通的 jar 包导入项目一样。下载完 JDBC 驱动后,将其解压,可以看到里面有两个 jar 包。

在 java 项目中新建一个 lib 文件夹,将 JDBC 解压后里面的 xxx.bin.jar 包复制到项目的 lib 文件夹中,右键要使用的jar包,选择Build Path-->Add to Build Path,将其添加为项目依赖即可。

可参考:https://jingyan.baidu.com/article/bad08e1e23982609c851219e.html

 

3、使用JDBC操作数据库

构建一个 JDBC 应用程序包括以下六个步骤-

  • 导入数据包:需要你导入含有需要进行数据库编程的 JDBC 类的包。大多数情况下,使用 import java.sql. 就足够了。

  • 注册 JDBC 驱动器:需要你初始化一个驱动器,以便于你打开一个与数据库的通信通道。

  • 打开连接:需要使用 DriverManager.getConnection() 方法创建一个 Connection 对象,它代表与数据库的物理连接。

  • 执行查询:需要使用类型声明的对象建立并提交一个 SQL 语句到数据库。

  • 提取结果数据:要求使用适当的 ResultSet.getXXX() 方法从结果集中检索数据。

  • 清理环境:依靠 JVM 的垃圾收集来关闭所有需要明确关闭的数据库资源。

 

3.1、操作数据库代码示例

在导入驱动 jar 包后,我们就可以使用 JDBC 驱动来操作数据库了。

代码示例:

package jdbcTest;

import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class JDBCTest {
    public static void main(String[] args) throws Exception {
        //1.注册驱动(mysql5之后的驱动jar包可以省略注册驱动的步骤)
        Class.forName("com.mysql.jdbc.Driver");
        //2.获取数据库连接对象
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db_test", "root", "123456");
        //3.定义sql语句
        String sql = "update students set name = 'hahaha' where id = 1";
        //4.获取执行sql的对象
        Statement stmt = conn.createStatement();
        //5.执行sql
        int count = stmt.executeUpdate(sql);
        
        System.out.println(count);   
        //6.释放资源
        stmt.close();
        conn.close();
    }
}

合理地 try{} catch(){} 的写法:

public static void main(String[] args) {
    Connection conn = null;
    Statement stmt = null;
    try {
        //1.注册驱动(mysql5之后的驱动jar包可以省略注册驱动的步骤)
        Class.forName("com.mysql.jdbc.Driver");
        //2.获取数据库连接对象
        conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db_test", "root", "123456");
        //3.定义sql语句
        String sql = "update students set name = 'hahaha' where id = 1";
        //4.获取执行sql的对象
        stmt = conn.createStatement();
        //5.执行sql
        int count = stmt.executeUpdate(sql);
        System.out.println(count); 
    } catch (Exception e) {
        e.printStackTrace();
    }finally {
        //6.释放资源
        if(stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        if(conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
    }
}

 

3.2、常用的 JDBC 组件介绍

JDBC 的 API 提供了以下接口和类:

DriverManager :这个类管理一系列数据库驱动程序。匹配连接使用通信子协议从 JAVA 应用程序中请求合适的数据库驱动程序。识别 JDBC 下某个子协议的第一驱动程序将被用于建立数据库连接。

Driver : 这个接口处理与数据库服务器的通信。你将很少直接与驱动程序互动。相反,你使用 DriverManager 中的对象,它管理此类型的对象。它也抽象与驱动程序对象工作相关的详细信息。

Connection : 此接口具有接触数据库的所有方法。该连接对象表示通信上下文,即,所有与数据库的通信仅通过这个连接对象进行。

Statement : 使用创建于这个接口的对象将 SQL 语句提交到数据库。除了执行存储过程以外,一些派生的接口也接受参数。

ResultSet : 在你使用语句对象执行 SQL 查询后,这些对象保存从数据获得的数据。它作为一个迭代器,让您可以通过它的数据来移动。

SQLException : 这个类处理发生在数据库应用程序的任何错误。

 

3.3、先注册 JDBC 驱动程序(mysql5之后的驱动程序可省略)

在使用驱动程序之前,你必须在你的程序里面注册它。我们可以通过加载 Oracle 驱动程序的类文件到内存中来注册驱动程序,在程序里做一次注册即可。注册驱动实际上就是告诉程序应该使用哪个数据库驱动 jar 包。

注册一个驱动程序中最常用的方法是使用 Java 的 Class.forName() 方法来动态加载驱动程序的类文件到内存中,它会自动将其注册。

try {
   Class.forName("oracle.jdbc.driver.OracleDriver");
}
catch(ClassNotFoundException ex) {
   System.out.println("Error: unable to load driver class!");
}

(注意:在mysql5之后的驱动jar包可以省略注册驱动的步骤) 

 

3.4、然后使用JDBC连接数据库(Connection)

要通过 JDBC 操作数据库,我们需要先连接数据库。当你加载了驱动程序之后,你可以通过 DriverManager.getConnection() 方法建立一个连接。Connection代表一个JDBC连接,它相当于 Java 程序到数据库的连接(通常是TCP连接)。

打开一个Connection时,需要准备URL、用户名和密码,才能成功连接到数据库。

数据库连接的代码示例如下:

// JDBC连接的URL, 不同数据库有不同的格式:
String JDBC_URL = "jdbc:mysql://localhost:3306/test";  //URL
String JDBC_USER = "root";           //用户名
String JDBC_PASSWORD = "password";   //密码

// 获取连接
Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD);

...

// 最后需关闭连接
conn.close();

核心代码是DriverManager提供的静态方法getConnection()DriverManager会自动扫描classpath,找到所有的JDBC驱动,然后根据我们传入的URL自动挑选一个合适的驱动。

JDBC连接是一种昂贵的资源,使用后要及时释放。可以使用try (resource)来自动释放JDBC连接:

try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
       ...
}

在 JDBC 程序的末尾,它必须明确关闭所有的连接到数据库的连接,以结束每个数据库会话。但是,如果忘了,Java 垃圾收集器也会关闭连接,它会完全清除过期的对象。依托垃圾收集器,特别是在数据库编程,是非常差的编程习惯,我们应该养成用 close()方法关闭连接对象的习惯。

 

3.4.1、数据库连接的URL格式

URL是由数据库厂商指定的格式,例如,MySQL的URL是:

jdbc:mysql://服务器IP:端口号/数据库名称?key1=value1&key2=value2

假设数据库运行在本机localhost,端口使用标准的3306,数据库名称是learnjdbc。示例如下:

jdbc:mysql://localhost:3306/learnjdbc?useSSL=false&characterEncoding=utf8

# 如果连接的是本地服务器,并且服务器默认端口是3306,那么可以简写为以下形式,即省略服务器地址和端口号:
jdbc:mysql:///数据库名称

(后面的两个参数表示不使用SSL加密,使用UTF-8作为字符编码(注意MySQL的UTF-8是utf8),不写也行) 

下表列出了常用的 JDBC 驱动程序名和数据库URL。

上表中 URL 格式所有加粗的部分都是静态的,你需要将剩余部分按照你的数据库实际情况进行设置。

 

3.4.2、如何创建连接对象

我们可以通过 DriverManager.getConnection() 方法来建立一个数据库连接,加载 DriverManager.getConnection() 参数有以下三种:

  1. getConnection(String url)
  2. getConnection(String url, Properties prop)
  3. getConnection(String url, String user, String password)

使用URL、用户名、密码:getConnection() 最常用的方式是需要你提供一个数据库 URL,用户名和密码:

String URL = "jdbc:oracle:thin:@amrood:1521:EMP";
String USER = "username";
String PASS = "password"
Connection conn = DriverManager.getConnection(URL, USER, PASS);

只使用URL:在只使用url时,数据库的 URL ,包括用户名和密码,将表现为以下的格式:

jdbc:oracle:driver:username/password@database

示例:

String URL = "jdbc:oracle:thin:username/password@amrood:1521:EMP";
Connection conn = DriverManager.getConnection(URL);

使用数据库 URL 和 Properties 对象:Properties 对象保存了一组关键数值。它通过调用 getConnection() 方法,将驱动程序属性传递给驱动程序。

import java.util.*;

String URL = "jdbc:oracle:thin:@amrood:1521:EMP";
Properties info = new Properties( );
info.put( "user", "username" );
info.put( "password", "password" );

Connection conn = DriverManager.getConnection(URL, info);

 

3.5、获取执行sql的对象并且执行sql

一旦我们获得了数据库的连接,我们就可以和数据库进行交互。JDBC 的 Statement,CallableStatement 和 PreparedStatement 接口定义的方法和属性,可以让你发送 SQL 命令或 PL/SQL 命令到数据库,并从你的数据库接收数据。在数据库中,它们还定义了帮助 Java 和 SQL 数据类型之间转换数据差异的方法。

下表提供了每个接口的用途概要,根据实际目的决定使用哪个接口。

 

查询数据库可以分为以下几步:

第一步,通过Connection提供的createStatement()方法创建一个Statement对象,用于执行一个查询;

第二步,执行Statement对象提供的executeQuery("SELECT * FROM students")并传入SQL语句,执行查询并获得返回的结果集,使用ResultSet来引用这个结果集;

第三步,反复调用ResultSetnext()方法并读取每一行结果。

完整查询代码如下:

try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
    try (Statement stmt = conn.createStatement()) {
        try (ResultSet rs = stmt.executeQuery("SELECT id, grade, name, gender FROM students WHERE gender=1")) {
            while (rs.next()) {
                long id = rs.getLong(1); // 注意:索引从1开始
                long grade = rs.getLong(2);
                String name = rs.getString(3);
                int gender = rs.getInt(4);
            }
        }
    }
}

注意要点:

StatmentResultSet都是需要关闭的资源,因此嵌套使用try (resource)确保及时关闭;rs.next()用于判断是否有下一行记录,如果有,将自动把当前行移动到下一行(一开始获得ResultSet时当前行不是第一行);ResultSet获取列时,索引从1开始而不是0;必须根据SELECT的列的对应位置来调用getLong(1)getString(2)这些方法,否则对应位置的数据类型不对,将报错。

 

3.5.1、使用 Statement 对象来操作数据库

在你准备使用 Statement 对象执行 SQL 语句之前,你需要使用 Connection 对象的 createStatement() 方法先创建一个 Statement 对象。

Connection conn = DriverManager.getConnection(URL, 用户名, 密码);
Statement stmt = conn.createStatement( );

当你创建了一个 Statement 对象之后,你可以用它的三个执行方法的任一方法来执行 SQL 语句。

  • boolean execute(String SQL) : 该方法可以执行任何的SQL语句。如果第一个结果是一个 ResultSet 对象,则返回的布尔值为 true ,否则返回 false 。当你需要使用真正的动态 SQL 时,可以使用这个方法来执行 SQL DDL 语句。

  • int executeUpdate(String SQL) : 常用该方法来执行DML语句(增删改),也可执行DDL语句(操作数据库和表结构)。它返回的是执行 SQL 语句影响的行的数目。

  • ResultSet executeQuery(String SQL) : 常用该方法执行DQL语句(查询),它返回一个 ResultSet 对象。
String sql = "update students set name = 'hahaha' where id = 1";
Statement stmt = conn.createStatement();
int count = stmt.executeUpdate(sql);

在使用后我们应该关闭 Statement 对象。通过调用 close() 方法就可以关闭 Statement 对象。其实在我们关闭了 Connection 对象后,它也会自动关闭 Statement 对象。但我们应该始终明确关闭 Statement 对象,以确保真正的清除。

Statement stmt = null;
try {
   stmt = conn.createStatement( );
   . . .
}
catch (SQLException e) {
   . . .
}
finally {
   stmt.close();
}

 

3.5.2、使用PreparedStatement对象操作数据库

PreparedStatement 接口扩展了 Statement 接口,它让你用一个常用的 Statement 对象增加几个高级功能。这个 statement 对象可以提供灵活多变的动态参数。

创建 PreparedStatement 对象:

String SQL = "Update Employees SET age = ? WHERE id = ?";
PreparedStatement pstmt = conn.prepareStatement(SQL);

JDBC 中所有的参数都被用 ? 符号表示,这是已知的参数标记。在执行 SQL 语句之前,你必须赋予每一个参数确切的数值。

setXXX() 方法将值绑定到参数,其中 XXX 表示你希望绑定到输入参数的 Java 数据类型。如果你忘了赋予值,你将收到一个 SQLException。每个参数标记映射它的序号位置。第一标记表示位置 1 ,下一个位置为 2 等等。这种方法不同于 Java 数组索引,它是从 0 开始的。

String sql = "SELECT * FROM user WHERE login=? AND pass=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setObject(1, name);
ps.setObject(2, pass);

所有的 Statement对象 的方法都与数据库交互,(a) execute(),(b) executeQuery(),及 (c) executeUpdate() 也能被 PreparedStatement 对象引用。然而,这些方法被 SQL 语句修改后是可以输入参数的。

PreparedStatement 对象在使用后也需要关闭,只需简单调用 close() 方法就可以完成这项工作。如果你关闭了 Connection 对象,那么它也会关闭 PreparedStatement 对象。然而,你应该始终明确关闭 PreparedStatement 对象,以确保真正的清除。

 

4、JDBC事务

数据库事务(Transaction)是由若干个SQL语句构成的一个操作序列,有点类似于Java的synchronized同步。数据库系统保证在一个事务中的所有SQL要么全部执行成功,要么全部不执行,即数据库事务具有ACID特性:

  • Atomicity:原子性
  • Consistency:一致性
  • Isolation:隔离性
  • Durability:持久性

数据库事务可以并发执行,而数据库系统从效率考虑,对事务定义了不同的隔离级别。SQL标准定义了4种隔离级别,分别对应可能出现的数据不一致的情况:

 

4.1、JDBC管理事务

要在JDBC中执行事务,本质上就是如何把多条SQL包裹在一个数据库事务中执行。

在 JDBC 中,我们可以使用 connection 对象来管理事务,connection 对象提供了3个方法来进行事务管理:

  • setAutoCommit():设置是否自动提交,方法中需要传入一个boolean类型的参数,true为自动提交,false为手动提交。进行事务管理时,应该在所有sql执行前设置为false,此时意味着开启了事务

  • commit():主动提交事务。应该在当所有sql执行完毕时调用该方法主动提交事务。

  • rollback():回滚事务。应该在捕获异常时调用giant方法来回滚事务。

伪代码:

Connection conn = openConnection();
try {
    // 关闭自动提交,即开启事务
    conn.setAutoCommit(false);
    // 执行多条SQL语句:
    insert(); update(); delete();
    // 提交事务:
    conn.commit();
} catch (SQLException e) {
    // 回滚事务:
    conn.rollback();
} finally {
    conn.setAutoCommit(true);  //最后恢复至自动提交
    conn.close();
}

在MySQL中,默认会自动提交事务,也就是任意一条SQL语句都会被当做是一个事务,并且自动提交,所以需要主动将自动提交关闭掉。使用 conn.setAutoCommit(false) 来关闭自动提交,即开启事务。执行完指定的若干条SQL语句后,我们可以调用 conn.commit() 来提交事务。如果事务提交失败,此时我们可以调用conn.rollback()来回滚事务。最后,在finally中通过conn.setAutoCommit(true)Connection对象的状态恢复到初始值。

实例代码如下:

public void jdbcTest2() {
    Connection conn = null;
    PreparedStatement ps1 = null;
    PreparedStatement ps2 = null;

    try {
        Class.forName("com.mysql.jdbc.Driver");
        conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db_test", "root", "123456");

        //关闭自动提交,开启事务
        conn.setAutoCommit(false);

        //多条sql
        String updatesql1 = "update userbalance set money = money - ? where username = ?";
        ps1 = conn.prepareStatement(updatesql1);
        ps1.setBigDecimal(1, new BigDecimal(500));
        ps1.setString(2, "zhangsan");

        String updatesql2 = "update userbalance set money = money + ? where username = ?";
        ps2 = conn.prepareStatement(updatesql2);
        ps2.setBigDecimal(1, new BigDecimal(500));
        ps2.setString(2, "lisi");

        //执行sql
        int count1 = ps1.executeUpdate();
        //模拟异常
        int a = 5 / 0;
        int count2 = ps2.executeUpdate();

        //提交事务
        conn.commit();

        System.out.println("结果:" + count1 + count2);

    } catch (Exception e) {
        e.printStackTrace();

        //在异常出现时需要回滚事务
        try {
            conn.rollback();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }

    } finally {
        //最后需要恢复至自动提交
        try {
            conn.setAutoCommit(true);
        } catch (SQLException e) {
            e.printStackTrace();
        }

        //释放资源。为了避免空指针异常,必须先判断是否为null
        if(ps1 != null) {
            try {
                ps1.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        if(ps2 != null) {
            try {
                ps2.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        if(conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

 

4.2、JDBC定义事务的隔离级别

如果要设定事务的隔离级别,可以使用如下代码:

// 设定隔离级别为READ COMMITTED:
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

如果没有调用上述方法,那么会使用数据库的默认隔离级别。MySQL的默认隔离级别是REPEATABLE READ

 

posted @ 2020-07-27 23:26  wenxuehai  阅读(668)  评论(0编辑  收藏  举报
//右下角添加目录