当前位置 : 首页 » 文章分类 :  开发  »  Java-JDBC

Java-JDBC

JDBC(Java Database Connectivity) API,即Java数据库编程接口,是一组标准的Java语言中的接口和类,使用这些接口和类,Java客户端程序可以访问各种不同类型的数据库。比如建立数据库连接、执行SQL语句进行数据的存取操作。
JDBC规范采用接口和实现分离的思想设计了Java数据库编程的框架。接口包含在java.sql及javax.sql包中,其中java.sql属于JavaSE,javax.sql属于JavaEE。这些接口的实现类叫做数据库驱动程序,由数据库的厂商或其它的厂商或个人提供。
为了使客户端程序独立于特定的数据库驱动程序,JDBC规范建议开发者使用基于接口的编程方式,即尽量使应用仅依赖java.sql及javax.sql中的接口和类。


错误

com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
The last packet successfully received from the server was 445 milliseconds ago.  The last packet sent successfully to the server was 0 milliseconds ago.

2021-08-11 13:48:25.687 [http-nio-8778-exec-4] WARN  com.zaxxer.hikari.pool.PoolBase - HikariPool-1 - Failed to validate connection com.mysql.jdbc.JDBC4Connection@4afa772f (Communications link failure

The last packet successfully received from the server was 1,764,354 milliseconds ago.  The last packet sent successfully to the server was 1 milliseconds ago.). Possibly consider using a shorter maxLifetime value.

这个错误大概意思是数据库连接不可用,具体怎么引起的,网上说法不一。
有的说是 程序打开数据库连接后,等到做数据库操作时,发现连接被 MySQL 关闭掉了。让修改 mysq 的 wait_timeout 连接超时等待时间,把默认的 8 小时改大。但我感觉我们生产服务器遇到的这个问题应该不是空闲连接被关掉引起的吧,生产上并发还挺高的,怎么可能出现空闲连接。当时报了几十个这种错误,然后又自动恢复了,不知道具体啥情况。

Solving a “communications link failure” with JDBC and MySQL [duplicate]
https://stackoverflow.com/questions/6865538/solving-a-communications-link-failure-with-jdbc-and-mysql

MySQL异常【数据库断开连接】:Communications link failure
https://my.oschina.net/xsh1208/blog/493443


CannotGetJdbcConnectionException

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
### Error querying database.  Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30000ms.
### The error may exist in com/common/mapper/BaseAccountInfoMapper.java (best guess)
### The error may involve com.common.mapper.BaseAccountInfoMapper.queryUserIdByAccountId
### The error occurred while executing a query
### Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30000ms.
        at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:79) ~[mybatis-spring-1.3.0.jar!/:1.3.0]
        at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:447) ~[mybatis-spring-1.3.0.jar!/:1.3.0]
        at com.sun.proxy.$Proxy130.selectOne(Unknown Source) ~[?:?]
        at org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate.java:167) ~[mybatis-spring-1.3.0.jar!/:1.3.0]
        at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:75) ~[mybatis-3.4.0.jar!/:3.4.0]
        at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:53) ~[mybatis-3.4.0.jar!/:3.4.0]
        at com.sun.proxy.$Proxy137.queryUserIdByAccountId(Unknown Source) ~[?:?]

原因是 hikari 连接池中连接都已被占用,获取不到可用的 jdbc 连接,等待一段时间后超时。


JDBC访问数据库步骤

使用JDBC访问数据库的步骤:

  • 注册JDBC驱动
    Class.forName(“JDBC_DRIVER”);
    注意:从JDBC4.0开始,使用Java SPI机制来完成自动注册,不再需要使用Class.forName()显式地加载JDBC驱动程序

  • 创建数据库连接
    通过工厂类DriverManager获取数据库连接
    Connection conn = DriverManager.getConnection(String url, String user ,String password);

  • 获取Statement
    Statement:用于执行静态SQL语句的接口,通过Connection.createStatement()得到
    PreparedStatement:Statement的子接口,预编译的sql语句对象,通过Connection.prepareStatement(sql)得到

  • 执行SQL语句,得到结果集
    Resultset:用于指向结果集对象的接口,结果集对象是通过Statement中的executeQuery()等方法得到的

  • 遍历结果集

  • 关闭资源
    关闭Resultset, Statement, Connection


JDBC操作PostgreSQL实例

package com.masi.jdbc.test;

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

public class FirstJDBC {
    static final String JDBC_DRIVER = "org.postgresql.Driver";
    static final String DB_URL = "jdbc:postgresql://172.27.19.164:5444/rbac";
    static final String USER = "rbac";
    static final String PASSWD = "rbac";

    public static void main(String[] args) {
        preparedStatementQuery();
    }

    //使用预编译的SQL查询
    public static void preparedStatementQuery() {
        Connection conn = null;
        PreparedStatement pstmt = null;
        try {
            //第1步:注册JDBC驱动(从JDBC4.0开始,不再需要使用Class.forName()显式地加载JDBC驱动程序)
            Class.forName(JDBC_DRIVER);

            //第2步:创建数据库连接
            conn = DriverManager.getConnection(DB_URL, USER, PASSWD);

            //第3步:获取PreparedStatement
            String usersSQL = "select distinct userid,username,userpasswd,app from rbac_acl_users where app=?";
            pstmt = conn.prepareStatement(usersSQL);

            //第4步:执行SQL,得到结果集
            pstmt.setString(1, "ais");
            ResultSet rs = pstmt.executeQuery();

            //第5步:遍历结果集
            while (rs.next()) {
                int userid = rs.getInt("userid");
                String username = rs.getString("username");
                String userpasswd = rs.getString("userpasswd");
                String app = rs.getString("app");
                System.out.println(userid+", "+username+", "+userpasswd+", "+app);
            }

            //第6步:关闭资源
            rs.close();
            pstmt.close();
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                pstmt.close();
            } catch (Exception e) {
            }
            try {
                conn.close();
            } catch (Exception e) {
            }
        }
    }

    //静态SQL查询
    public static void statementQuery() {
        Connection conn = null;
        Statement stmt = null;
        try {
            //第1步:注册JDBC驱动(从JDBC4.0开始,不再需要使用Class.forName()显式地加载JDBC驱动程序)
            Class.forName(JDBC_DRIVER);

            //第2步:创建数据库连接
            conn = DriverManager.getConnection(DB_URL, USER, PASSWD);

            //第3步:获取Statement
            stmt = conn.createStatement();

            //第4步:执行SQL,得到结果集
            String usersSQL = "select distinct userid,username,userpasswd,app from rbac_acl_users";
            ResultSet rs = stmt.executeQuery(usersSQL);

            //第5步:遍历结果集
            while (rs.next()) {
                int userid = rs.getInt("userid");
                String username = rs.getString("username");
                String userpasswd = rs.getString("userpasswd");
                String app = rs.getString("app");
                System.out.println(userid+", "+username+", "+userpasswd+", "+app);
            }

            //第6步:关闭资源
            rs.close();
            stmt.close();
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                stmt.close();
            } catch (Exception e) {
            }
            try {
                conn.close();
            } catch (Exception e) {
            }
        }
    }

}

JDBC示例代码
http://www.yiibai.com/jdbc/jdbc-sample-code.html

Java JDBC的基本知识
http://blog.csdn.net/williamchew/article/details/51951551

JDBC详解(汇总)
http://blog.csdn.net/cai_xingyun/article/details/41482835


JDBC驱动

JDBC驱动程序就是各个数据库厂家根据JDBC的规范制作的JDBC实现类。

JDBC驱动类型

JDBC驱动程序的四种类型:

  • 第一种类型的驱动程序的实现是通过将JDBC的调用全部委托给其它编程接口来实现的,比如ODBC。这种类型的驱动程序需要安装本地代码库,即依赖于本地的程序,所以便携性较差。比如JDBC-ODBC桥驱动程序
  • 第二种类型的驱动程序的实现是部分基于Java语言的。即该驱动程序一部分是用Java语言编写,其它部分委托本地的数据库的客户端代码来实现。同类型1的驱动一样,该类型的驱动程序也依赖本地的程序,所以便携性较差
  • 第三种类型的驱动程序的实现是全部基于JAVA语言的。该类型的驱动程序通常由某个中间件服务器提供,这样客户端程序可以使用数据库无关的协议和中间件服务器进行通信,中间件服务器再将客户端的JDBC调用转发给数据库进行处理
  • 第四种类型的驱动程序的实现是全部基于JAVA语言的。该类型的驱动程序中包含了特定数据库的访问协议,使得客户端可以直接和数据库进行通信

Class.forName()手动注册驱动

为什么调用Class.forName(),却没有newInstance()?
Class.forName("")的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段。
Class.forName("")加载了指定类后,若类中有静态初始化器,JVM必然会执行该类的静态代码段,而JDBC的Driver类都是会有static代码块,在JDBC规范中明确要求任何Driver类必须向DriverManager注册自己。

例如mysql的驱动com.mysql.jdbc.Driver中的静态初始化器:

package com.mysql.jdbc

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

其实,可以将Class.forName("JDBC_DRIVER")换成DriverManager.registerDriver(new com.mysql.jdbc.Driver()),效果是一样的,只不过这样就需要在代码中依赖具体驱动实现类了,不符合接口与实现分离的思想。
所以,Class.forName(driver)的根本目的就是为了调用DriverManager.registerDriver()


SPI机制自动注册驱动

从JDBC4.0开始,应用程序不再需要使用Class.forName()显式地加载JDBC驱动程序
JDBC 4.0及以上版本开始使用Java Service Provider Interface(Java SPI机制)来完成自动注册,不再需要使用Class.forName()显式地加载JDBC驱动程序。JDBC 4.0 Drivers 必须包括 META-INF/services/java.sql.Driver 文件。此文件包含 java.sql.Driver 的 JDBC 驱动程序实现的名称。例如,要自动加载 my.sql.Driver 类,META-INF/services/java.sql.Driver 文件需要包含下面的条目: my.sql.Driver

原理如下:
在DriverManager的源码中可以看到一个静态块:

static {
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
}

可以在loadInitialDrivers()方法中看到以下一段代码:

ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator driversIterator = loadedDrivers.iterator();

/* Load these drivers, so that they can be instantiated.
 * It may be the case that the driver class may not be there
 * i.e. there may be a packaged driver with the service class
 * as implementation of java.sql.Driver but the actual class
 * may be missing. In that case a java.util.ServiceConfigurationError
 * will be thrown at runtime by the VM trying to locate
 * and load the service.
 *
 * Adding a try catch block to catch those runtime errors
 * if driver not available in classpath but it's
 * packaged as service and that service is there in classpath.
 */
try{
    while(driversIterator.hasNext()) {
        driversIterator.next();
    }
} catch(Throwable t) {
    // Do nothing
}

其中ServiceLoader.load(Driver.class)可以把类路径下所有jar包中META-INF/services/java.sql.Driver文件中定义的类加载上来,此类必须继承自java.sql.Driver


常用JDBC驱动

常用数据库的驱动程序及JDBC URL

MySQL

MySQL Connector/J Driver

  • 驱动程序包名:mysql-connector-java-x.x.xx-bin.jar
  • 驱动程序类名: com.mysql.jdbc.Driver
  • JDBC URL: jdbc:mysql://<host>:<port>/<database_name>

默认端口3306,如果服务器使用默认端口则port可以省略
MySQL Connector/J Driver 允许在URL中添加额外的连接属性:
jdbc:mysql://<host>:<port>/<database_name>?property1=value1&property2=value2
例如:jdbc:mysql://<host>:<port>/<database_name>?user=userName&password=123&useUnicode=true&characterEncoding=gb2312

Oracle

Oracle Thin JDBC Driver

默认端口1521,SID即数据库名

PostgreSQL

PostgreSQL Native JDBC Driver,

  • 下载地址:http://jdbc.postgresql.org/
  • 驱动程序包名:postgresql-9.2-1003-jdbc4.jar
  • 驱动程序类名: org.postgresql.Driver
  • JDBC URL: jdbc:postgresql://<host>:<port>/<database_name>

默认端口5432

SQL Server

Microsoft SQL Server JDBC Driver(一般用来连接 SQLServer 2000)

  • 驱动程序包名:msbase.jar mssqlserver.jar msutil.jar
  • 驱动程序类名: com.microsoft.jdbc.sqlserver.SQLServerDriver
  • JDBC URL: jdbc:microsoft:sqlserver://<host>:<port>

Microsoft SQL Server 2005 JDBC Driver(2005版本及以后)

  • 驱动程序包名:sqljdbc.jar
  • 驱动程序类名: com.microsoft.sqlserver.jdbc.SQLServerDriver
  • JDBC URL: jdbc:sqlserver://<host>:<port>;databasename=<database_name>

默认端口1433,如果服务器使用默认端口则port可以省略

常用数据库驱动和JDBC+URL
http://blog.csdn.net/zzq58157383/article/details/6868118

JDBC在getConnection之前为什么要调用Class.forName
http://www.ticmy.com/?p=249


相关类和接口

DriverManager

java.sql.DriverManager
public class DriverManager extends Object

管理一组 JDBC 驱动程序的基本服务。
注:DataSource 接口是 JDBC 2.0 API 中的新增内容,它提供了连接到数据源的另一种方法。使用 DataSource 对象是连接到数据源的首选方法。

作为初始化的一部分,DriverManager 类会尝试加载在 “jdbc.drivers” 系统属性中引用的驱动程序类。这允许用户定制由他们的应用程序使用的 JDBC Driver。例如,在 ~/.hotjava/properties 文件中,用户可以指定: jdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.taste.ourDriver

DriverManager 类的方法 getConnection 和 getDrivers 已经得到提高以支持 Java Standard Edition Service Provider 机制。 JDBC 4.0 Drivers 必须包括 META-INF/services/java.sql.Driver 文件。此文件包含 java.sql.Driver 的 JDBC 驱动程序实现的名称。例如,要加载 my.sql.Driver 类,META-INF/services/java.sql.Driver 文件需要包含下面的条目: my.sql.Driver
从JDBC 4.0开始,应用程序不再需要使用 Class.forName() 显式地加载 JDBC 驱动程序。当前使用 Class.forName() 加载 JDBC 驱动程序的现有程序将在不作修改的情况下继续工作。
在调用 getConnection 方法时,DriverManager 会试着从初始化时加载的那些驱动程序以及使用与当前 applet 或应用程序相同的类加载器显式加载的那些驱动程序中查找合适的驱动程序。

getConnection()

public static Connection getConnection(String url, String user, String password)
public static Connection getConnection(String url)
试图建立到给定数据库 URL 的连接。DriverManager 试图从已注册的 JDBC 驱动程序集中选择一个适当的驱动程序。

  • 参数:
  • url - jdbc:subprotocol:subname 形式的数据库 url
  • user - 数据库用户,连接是为该用户建立的
  • password - 用户的密码
  • 返回:到 URL 的连接
  • 抛出: SQLException - 如果发生数据库访问错误

registerDriver()

public static void registerDriver(Driver driver)
向 DriverManager 注册给定驱动程序。新加载的驱动程序类应该调用 registerDriver 方法让 DriverManager 知道自己。

  • 参数:driver - 将向 DriverManager 注册的新的 JDBC Driver
  • 抛出:SQLException - 如果发生数据库访问错误

Connection

java.sql.Connection
public interface Connection extends Wrapper

与特定数据库的连接(会话)。在连接上下文中执行 SQL 语句并返回结果。

Connection 对象的数据库能够提供描述其表、所支持的 SQL 语法、存储过程、此连接功能等等的信息。此信息是使用 getMetaData 方法获得的。

注:在配置 Connection 时,JDBC 应用程序应该使用适当的 Connection 方法,比如 setAutoCommit 或 setTransactionIsolation。在有可用的 JDBC 方法时,应用程序不能直接调用 SQL 命令更改连接的配置。默认情况下,Connection 对象处于自动提交模式下,这意味着它在执行每个语句后都会自动提交更改。如果禁用了自动提交模式,那么要提交更改就必须显式调用 commit 方法;否则无法保存数据库更改。

createStatement()

Statement createStatement()
创建一个 Statement 对象来将 SQL 语句发送到数据库。不带参数的 SQL 语句通常使用 Statement 对象执行。如果多次执行相同的 SQL 语句,使用 PreparedStatement 对象可能更有效。
使用返回的 Statement 对象创建的结果集在默认情况下类型为 TYPE_FORWARD_ONLY,并带有 CONCUR_READ_ONLY 并发级别。已创建结果集的可保存性可调用 getHoldability() 确定。

  • 返回:一个新的默认 Statement 对象
  • 抛出:SQLException - 如果发生数据库访问错误,或者在关闭的连接上调用此方法

prepareStatement()

PreparedStatement prepareStatement(String sql)
创建一个 PreparedStatement 对象来将参数化的 SQL 语句发送到数据库。
带有 IN 参数或不带有 IN 参数的 SQL 语句都可以被预编译并存储在 PreparedStatement 对象中。然后可以有效地使用此对象来多次执行该语句。
注:为了处理受益于预编译的带参数 SQL 语句,此方法进行了优化。如果驱动程序支持预编译,则 prepareStatement 方法将该语句发送给数据库进行预编译。一些驱动程序可能不支持预编译。在这种情况下,执行 PreparedStatement 对象之前无法将语句发送给数据库。这对用户没有直接影响;但它的确会影响哪些方法将抛出某些 SQLException 对象。
使用返回的 PreparedStatement 对象创建的结果集在默认情况下类型为 TYPE_FORWARD_ONLY,并带有 CONCUR_READ_ONLY 并发级别。已创建结果集的可保存性可调用 getHoldability() 确定。

  • 参数:sql - 可能包含一个或多个 ‘?’ IN 参数占位符的 SQL 语句
  • 返回:包含预编译 SQL 语句的新的默认 PreparedStatement 对象
  • 抛出:SQLException - 如果发生数据库访问错误,或者在关闭的连接上调用此方法

close()

void close()
立即释放此 Connection 对象的数据库和 JDBC 资源,而不是等待它们被自动释放。
在已经关闭的 Connection 对象上调用 close 方法无操作 (no-op)。
建议最好在调用 close 方法之前,应用程序显式提交或回滚一个活动事务。如果调用 close 方法并且有一个活动事务,那么结果将由实现定义。

  • 抛出:SQLException - 如果发生数据库访问错误

isClosed()

boolean isClosed()
查询此 Connection 对象是否已经被关闭。如果在连接上调用了 close 方法或者发生某些严重的错误,则连接被关闭。只有在调用了 Connection.close 方法之后被调用时,此方法才保证返回 true。
通常不能调用此方法确定到数据库的连接是有效的还是无效的。通过捕获在试图进行某一操作时可能抛出的异常,典型的客户端可以确定某一连接是无效的。

  • 返回:如果此 Connection 对象是关闭的,则返回 true;如果它仍然处于打开状态,则返回 false
  • 抛出:SQLException - 如果发生数据库访问错误

Statement

java.sql.Statement
public interface Statement extends Wrapper

用于执行静态 SQL 语句并返回它所生成结果的对象。
在默认情况下,同一时间每个 Statement 对象在只能打开一个 ResultSet 对象。因此,如果读取一个 ResultSet 对象与读取另一个交叉,则这两个对象必须是由不同的 Statement 对象生成的。如果存在某个语句的打开的当前 ResultSet 对象,则 Statement 接口中的所有执行方法都会隐式关闭它。

executeQuery()

ResultSet executeQuery(String sql)
执行给定的 SQL 语句,该语句返回单个 ResultSet 对象。

  • 参数:sql - 要发送给数据库的 SQL 语句,通常为静态 SQL SELECT 语句
  • 返回:包含给定查询所生成数据的 ResultSet 对象;永远不能为 null
  • 抛出: SQLException - 如果发生数据库访问错误,在已关闭的 Statement 上调用此方法,或者给定 SQL 语句生成单个 ResultSet 对象之外的任何其他内容

PreparedStatement

java.sql.PreparedStatement
public interface PreparedStatement extends Statement

表示预编译的 SQL 语句的对象。
SQL 语句被预编译并存储在 PreparedStatement 对象中。然后可以使用此对象多次高效地执行该语句。
注:用于设置 IN 参数值的设置方法(setShort、setString 等等)必须指定与输入参数的已定义 SQL 类型兼容的类型。例如,如果 IN 参数具有 SQL 类型 INTEGER,那么应该使用 setInt 方法。
如果需要任意参数类型转换,使用 setObject 方法时应该将目标 SQL 类型作为其参数。

在以下设置参数的示例中,con 表示一个活动连接:

PreparedStatement pstmt = con.prepareStatement("UPDATE EMPLOYEES SET SALARY = ? WHERE ID = ?");
pstmt.setBigDecimal(1, 153833.00)
pstmt.setInt(2, 110592)

setString()

void setString(int parameterIndex, String x)
将指定参数设置为给定 Java String 值。在将此值发送给数据库时,驱动程序将它转换成一个 SQL VARCHAR 或 LONGVARCHAR 值(取决于该参数相对于驱动程序在 VARCHAR 值上的限制的大小)。

  • 参数:
  • parameterIndex - 第一个参数是 1,第二个参数是 2,……
  • x - 参数值
  • 抛出:SQLException - 如果 parameterIndex 不对应于 SQL 语句中的参数标记;如果发生数据库访问错误,或者在关闭的 PreparedStatement 上调用此方法

setInt()

void setInt(int parameterIndex, int x)
将指定参数设置为给定 Java int 值。在将此值发送到数据库时,驱动程序将它转换成一个 SQL INTEGER 值。

  • 参数:
  • parameterIndex - 第一个参数是 1,第二个参数是 2,……
  • x - 参数值
  • 抛出:SQLException - 如果 parameterIndex 不对应于 SQL 语句中的参数标记;如果发生数据库访问错误,或者在关闭的 PreparedStatement 上调用此方法

executeQuery()

ResultSet executeQuery()
在此 PreparedStatement 对象中执行 SQL 查询,并返回该查询生成的 ResultSet 对象。

  • 返回:包含该查询生成的数据的 ResultSet 对象;不会返回 null
  • 抛出:SQLException - 如果发生数据库访问错误,在关闭的 PreparedStatement 上调用此方法,或者 SQL 语句没有返回 ResultSet 对象

executeUpdate()

int executeUpdate()
在此 PreparedStatement 对象中执行 SQL 语句,该语句必须是一个 SQL 数据操作语言(Data Manipulation Language,DML)语句,比如 INSERT、UPDATE 或 DELETE 语句;或者是无返回内容的 SQL 语句,比如 DDL 语句。

  • 返回:(1) SQL 数据操作语言 (DML) 语句的行数 (2) 对于无返回内容的 SQL 语句,返回 0
  • 抛出:SQLException - 如果发生数据库访问错误,在关闭的 PreparedStatement 上调用此方法,或者 SQL 语句返回一个 ResultSet 对象

ResultSet

java.sql.ResultSet
public interface ResultSet extends Wrapper

表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。
ResultSet 对象具有指向其当前数据行的光标。最初,光标被置于第一行之前。next 方法将光标移动到下一行;因为该方法在 ResultSet 对象没有下一行时返回 false,所以可以在 while 循环中使用它来迭代结果集。
默认的 ResultSet 对象不可更新,仅有一个向前移动的光标。因此,只能迭代它一次,并且只能按从第一行到最后一行的顺序进行。

ResultSet 接口提供用于从当前行获取列值的获取 方法(getBoolean、getLong 等)。可以使用列的索引编号或列的名称获取值。一般情况下,使用列索引较为高效。列从 1 开始编号。为了获得最大的可移植性,应该按从左到右的顺序读取每行中的结果集列,每列只能读取一次。
对于获取方法,JDBC 驱动程序尝试将底层数据转换为在获取方法中指定的 Java 类型,并返回适当的 Java 值。JDBC 规范有一个表,显示允许的从 SQL 类型到 ResultSet 获取方法所使用的 Java 类型的映射关系。
用作获取方法的输入的列名称不区分大小写。用列名称调用获取方法时,如果多个列具有这一名称,则返回第一个匹配列的值。在生成结果集的 SQL 查询中使用列名称时,将使用列名称选项。对于没有在查询中显式指定的列,最好使用列编号。如果使用列名称,则程序员应该注意保证名称唯一引用预期的列,这可以使用 SQL AS 子句确定。

当生成 ResultSet 对象的 Statement 对象关闭、重新执行或用来从多个结果的序列获取下一个结果时,ResultSet 对象将自动关闭。
ResultSet 对象的列的编号、类型和属性由 ResultSet.getMetaData 方法返回的 ResulSetMetaData 对象提供。

next()

boolean next()
将光标从当前位置向前移一行。ResultSet 光标最初位于第一行之前;第一次调用 next 方法使第一行成为当前行;第二次调用使第二行成为当前行,依此类推。
当调用 next 方法返回 false 时,光标位于最后一行的后面。任何要求当前行的 ResultSet 方法调用将导致抛出 SQLException。如果结果集的类型是 TYPE_FORWARD_ONLY,则其 JDBC 驱动程序实现对后续 next 调用是返回 false 还是抛出 SQLException 将由供应商指定。
如果对当前行开启了输入流,则调用 next 方法将隐式关闭它。读取新行时,将清除 ResultSet 对象的警告链。

  • 返回:如果新的当前行有效,则返回 true;如果不存在下一行,则返回 false
  • 抛出: SQLException - 如果发生数据库访问错误或在关闭的结果集上调用此方法

getString(int)

String getString(int columnIndex)
以 Java 编程语言中 String 的形式获取此 ResultSet 对象的当前行中指定列的值。

  • 参数:columnIndex - 第一个列是 1,第二个列是 2,……
  • 返回:列值;如果值为 SQL NULL,则返回值为 null
  • 抛出: SQLException - 如果 columnIndex 无效;如果发生数据库访问错误或在已关闭的结果集上调用此方法

getString(String)

String getString(String columnLabel)
以 Java 编程语言中 String 的形式获取此 ResultSet 对象的当前行中指定列的值。

  • 参数:columnLabel - 使用 SQL AS 子句指定的列标签。如果未指定 SQL AS 子句,则标签是列名称
  • 返回:列值;如果值为 SQL NULL,则返回值为 null
  • 抛出:SQLException - 如果 columnLabel 无效;如果发生数据库访问错误或在已关闭的结果集上调用此方法

可滚动/更新的ResultSet对象

可以生成可滚动和/或可更新的 ResultSet 对象。以下代码片段(其中 con 为有效的 Connection 对象)演示了如何生成可滚动且不受其他更新影响的可更新结果集

Statement stmt = con.createStatement(
                              ResultSet.TYPE_SCROLL_INSENSITIVE,
                              ResultSet.CONCUR_UPDATABLE);
ResultSet rs = stmt.executeQuery("SELECT a, b FROM TABLE2");
// rs will be scrollable, will not show changes made by others,
// and will be updatable

更新方法的使用

可以用以下两种方式使用更新方法:

  • 更新当前行中的列值。
    在可滚动的 ResultSet 对象中,可以向前和向后移动光标,将其置于绝对位置或相对于当前行的位置。以下代码片段更新 ResultSet 对象 rs 第五行中的 NAME 列,然后使用方法 updateRow 更新导出 rs 的数据源表。

    rs.absolute(5); // moves the cursor to the fifth row of rs
    rs.updateString("NAME", "AINSWORTH"); // updates the NAME column of row 5 to be AINSWORTH
    rs.updateRow(); // updates the row in the data source
    
  • 将列值插入到插入行中。
    可更新的 ResultSet 对象具有一个与其关联的特殊行,该行用作构建要插入的行的暂存区域 (staging area)。以下代码片段将光标移动到插入行,构建一个三列的行,并使用方法 insertRow 将其插入到 rs 和数据源表中。

    rs.moveToInsertRow(); // moves cursor to the insert row
    rs.updateString(1, "AINSWORTH"); // updates the first column of the insert row to be AINSWORTH
    rs.updateInt(2,35); // updates the second column to be 35
    rs.updateBoolean(3, true); // updates the third column to true
    rs.insertRow();
    rs.moveToCurrentRow();
    

上一篇 Java-SPI

下一篇 2017年第二季度运动记录

阅读
评论
6.7k
阅读预计27分钟
创建日期 2017-04-13
修改日期 2019-09-26
类别

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论