MySQL Connector/J 作为对 JDBC API 的严格实现,通过了 Oracle 公开提供的 JDBC 兼容性测试套件中的所有测试。JDBC 规范对某些功能的实现方式很灵活。本节将按接口级别详细介绍可能影响您使用 MySQL Connector/J 编写应用程序的方式的实现决策。
-
BLOB
您可以通过在 JDBC URL 中添加属性
emulateLocators=true
来使用定位器模拟 BLOB。使用此方法,驱动程序会延迟加载实际的 BLOB 数据,直到您检索其他数据,然后使用检索方法(getInputStream()
、getBytes()
等)在 BLOB 数据流上操作。您必须使用带有列值的列别名,该列值指向实际的 BLOB 名称,例如
SELECT id, 'data' as blob_data from blobtable
您还必须遵循以下规则
BLOB 实现不允许就地修改(它们是副本,如
DatabaseMetaData.locatorsUpdateCopies()
方法所报告的那样)。因此,请使用相应的PreparedStatement.setBlob()
或ResultSet.updateBlob()
(在可更新结果集的情况下)方法将更改保存回数据库。 -
连接
isClosed()
方法不会 ping 服务器以确定它是否可用。根据 JDBC 规范,它只在对连接调用了closed()
时返回 true。如果您需要确定连接是否仍然有效,请发出一个简单的查询,例如SELECT 1
。如果连接不再有效,驱动程序将抛出异常。 -
DatabaseMetaData
外键 信息(
getImportedKeys()
/getExportedKeys()
和getCrossReference()
)仅适用于InnoDB
表。驱动程序使用SHOW CREATE TABLE
检索此信息,因此如果其他任何存储引擎添加了对外键的支持,驱动程序也会透明地支持它们。 -
PreparedStatement
Connector/J 实施了两种准备好的语句变体,即客户端准备好的语句和服务器端准备好的语句。默认情况下使用客户端准备好的语句,因为早期版本的 MySQL 不支持准备好的语句功能,或者其实现存在问题。当服务器支持服务器端准备好的语句和二进制编码结果集时,使用它们。要启用服务器端准备好的语句的使用,请设置
useServerPrepStmts=true
。使用 大型 参数的服务器端准备好的语句时要小心,这些参数使用
setBinaryStream()
、setAsciiStream()
、setUnicodeStream()
、setCharacterStream()
、setNCharacterStream()
、setBlob()
、setClob()
或setNCLob()
设置。要重新执行该语句并将任何大型参数更改为非大型参数,请调用clearParameters()
并重新设置所有参数。原因如下:在服务器端准备好的语句和客户端模拟过程中,大型数据仅在调用
PreparedStatement.execute()
时进行交换。
完成此操作后,用于在客户端读取数据的流将关闭(根据 JDBC 规范),并且无法再次从中读取。
如果参数从大型更改为非大型,驱动程序必须重置准备好的语句的服务器端状态,以允许正在更改的参数取代先前的大型值。这会删除已发送到服务器的所有大型数据,从而需要使用
setBinaryStream()
、setAsciiStream()
、setUnicodeStream()
、setCharacterStream()
、setNCharacterStream()
、setBlob()
、setClob()
或setNCLob()
方法重新发送数据。
因此,要将参数类型更改为非大型类型,您必须在重新执行之前调用
clearParameters()
并重新设置准备好的语句的所有参数。 -
ResultSet
默认情况下,结果集将完全检索并存储在内存中。在大多数情况下,这是最有效的操作方式,并且由于 MySQL 网络协议的设计,它更容易实施。如果您正在使用具有大量行或大型值的 Results,并且无法为 JVM 中所需内存分配堆空间,则可以告诉驱动程序逐行回传结果。
要启用此功能,请以以下方式创建
Statement
实例:stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(Integer.MIN_VALUE);
将仅向前、只读结果集与
Integer.MIN_VALUE
的获取大小相结合,这将作为信号告诉驱动程序逐行流式传输结果集。此后,使用该语句创建的任何结果集都将逐行检索。这种方法有一些注意事项。您必须在连接上发出任何其他查询之前读取结果集中的所有行(或关闭它),否则将抛出异常。
这些语句持有的锁最早可以释放的时间是在语句完成时(无论它们是
MyISAM
表级锁,还是其他存储引擎(如InnoDB
)中的行级锁)。如果语句在事务范围内,则锁将在事务完成时释放(这意味着语句需要先完成)。与大多数其他数据库一样,语句要等到所有等待语句的完成或语句的活动结果集关闭时才会完成。
因此,如果使用流式结果,请尽快处理它们,如果您希望保持对产生结果集的语句所引用的表的并发访问。
另一种选择是使用基于游标的流式传输来每次检索一组行。这可以通过将连接属性
useCursorFetch
设置为 true,然后调用setFetchSize(int)
来实现,其中int
是每次要获取的行数:conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1/?useCursorFetch=true", "user", "s3cr3t"); stmt = conn.createStatement(); stmt.setFetchSize(100); rs = stmt.executeQuery("SELECT * FROM your_table_here");
-
Statement
Connector/J 包含对
Statement.cancel()
和Statement.setQueryTimeout()
的支持。两者都需要一个单独的连接来发出KILL QUERY
语句。对于setQueryTimeout()
,实现会创建一个额外的线程来处理超时功能。注意取消
setQueryTimeout()
语句的失败可能会表现为RuntimeException
,而不是静默失败,因为目前没有办法解阻塞正在执行查询的线程,该线程由于超时到期而被取消,并让它抛出异常。MySQL 不支持 SQL 游标,并且 JDBC 驱动程序也不模拟它们,因此
setCursorName()
没有任何效果。Connector/J 还提供了两种额外的 方法
-
setLocalInfileInputStream()
设置一个InputStream
实例,该实例将用于将数据发送到 MySQL 服务器,用于LOAD DATA LOCAL INFILE
语句,而不是表示语句中给定路径的FileInputStream
或URLInputStream
。执行
LOAD DATA LOCAL INFILE
语句时,将读取此流以完成,并且驱动程序会自动关闭它,因此在每次调用会导致 MySQL 服务器请求数据以满足LOAD DATA LOCAL INFILE
请求的execute*()
之前,需要重置它。如果此值设置为
NULL
,则驱动程序将恢复为使用FileInputStream
或URLInputStream
,具体取决于需要。 -
getLocalInfileInputStream()
返回将用于发送响应LOAD DATA LOCAL INFILE
语句的数据的InputStream
实例。如果使用
setLocalInfileInputStream()
未设置此类流,则此方法将返回NULL
。
-