JDBC驱动
1.概述
1.1.JDBC 介绍
Java Database Connectivity(JDBC)是一种Java编程语言用于与数据库进行交互的API。它提供了一组用于执行SQL语句、查询和更新数据库的方法。JDBC允许Java应用程序通过标准的数据库连接方式连接到关系型数据库,并通过执行SQL语句来操作数据库。
JDBC为Java应用程序提供了与数据库进行交互的标准接口,使得开发人员可以使用Java来处理数据库操作,而不必关心底层数据库的具体实现。
1.2.UXDB JDBC 简介
UXDB JDBC驱动允许Java程序使用标准的、与数据库无关的Java代码连接到UXDB数据库。是一个纯Java编写的JDBC驱动程序(Type 4),并且它使用UXDB原生网络协议进行通信。
当前版本的驱动程序与UXDB内核版本8.2及更高版本兼容,使用3.0或4.0版本的通信协议,以及Java 6(JDBC 4.0),Java 7(JDBC 4.1)和Java 8(JDBC 4.2)。
2.开始使用
2.1.获取 UXDB JDBC Driver 驱动包与版本信息
2.1.1.获取UXDB JDBC驱动
你可以在优炫的私有nexus上找到最新的UXDB JDBC Driver,下载。
请将其加入maven的settings.xml文件中
<mirrors>
<mirror>
<id>uxsino</id>
<name>uxsino nexus</name>
<url>http://cd.uxsino.com:10882/repository/thirdparty/</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
依赖配置
-
JRE 8
<dependency> <groupId>com.uxsino.uxdb</groupId> <artifactId>uxdbjdbc</artifactId> <version>2.1.2.3</version> </dependency> -
JRE 7
<dependency> <groupId>com.uxsino.uxdb</groupId> <artifactId>uxdbjdbc</artifactId> <version>2.1.2.3.jre7</version> </dependency> -
JRE 6
<dependency> <groupId>com.uxsino.uxdb</groupId> <artifactId>uxdbjdbc</artifactId> <version>2.1.2.3.jre6</version> </dependency>
2.2.设置Java环境的Class Path
为了使用驱动程序,需要在类路径中包含名为uxdbjdbc-*.*.*.*.jar的JAR归档文件。可以通过将它放在CLASSPATH环境变量中,或者在java命令行上使用启动参数来实现。
假设我们有一个应用程序,它使用JDBC驱动程序来访问数据库,而该应用程序被安装在/usr/local/lib/myapp.jar上。而 UXDB 的 JDBC 驱动程序则被安装在/usr/local/share/java/uxdbjdbc-2.1.2.3.jar上。要运行该应用程序,我们将使用以下命令:
export CLASSPATH=/usr/local/lib/myapp.jar:/usr/local/share/java/uxdbjdbc-2.1.2.3.jar:. java MyApp
当前的Java应用程序很可能会使用Maven、Gradle或其他软件包管理工具。你可以使用这些工具将驱动程序的JAR文件包含在你的项目中。
2.3.导入JDBC
任何使用JDBC的源文件都需要导入java.sql包, 使用如下代码:
import java.sql.*;
注意
除非你正在使用UXDB扩展的相关JDBC API,否则不应该直接导入com.uxsino.uxdb包。
2.4.加载驱动
应用程序无需显式加载com.uxsino.uxdb.Driver类,因为UXJDBC驱动程序jar支持Java Service Provider机制。当应用程序连接到UXDB时,JVM会自动加载驱动程序(前提是驱动程序的jar文件位于类路径上)。
注意
在Java 1.6之前,驱动程序必须由应用程序加载:可以通过调用Class.forName("com.uxsino.uxdb.Driver");或者将驱动程序类名作为JVM参数传递来加载,例如java -Djdbc.drivers=com.uxsino.uxdb.Driver example.ImageViewer
这些旧的加载驱动程序的方法仍然受支持,但已不再必要。
2.5.连接到数据库
2.5.1.建立连接
在JDBC中,每一个数据库通过URL(统一资源定位符)来表示。在UXDB中,URL可以采用以下形式之一:
- jdbc:uxdb:database
- jdbc:uxdb:/
- jdbc:uxdb://host/database
- jdbc:uxdb://host/
- jdbc:uxdb://host:port/database
- jdbc:uxdb://host:port/
上述参数分别具有以下含义:
host= 服务器的主机名称或者IP. 默认值为localhost。如果要指定一个IPv6地址,你需要使用方括号将地址围绕起来,例如:jdbc:uxdb://[::1]:5740/accountingport= 52025database= 数据库的database名称。默认连接的database为连接数据库时使用的用户名。
尝试连接时, 需要从JDBC获取一个Connection的实例,你需要用到DriverManager.getConnection()方法: Connection connection = DriverManager.getConnection(url, username, password)
重要
连接URL的任何部分中出现的URL保留字符(例如:/、:、@、(、)、[、]、&、#、=、?和空格)必须进行百分比编码。详细信息请参阅RFC 3986。
2.5.2.连接属性/参数
注意
在url的database参数后面添加“?”,然后继续增加额外的连接参数。
| 名称 | 类型 | 默认值 | 有效输入 | 说明 |
|---|---|---|---|---|
| urlDBname | String | uxdb | postgresql | 改变UxDataBaseMetaData中geturl方法的返回值 |
| productName | String | Uxdb | PostgreSQL | 改变UxDataBaseMetaData中getDatabaseProductName方法的返回值 |
| stringtype | String | null | unspecified | 指定在通过 setString() 方法设置 PreparedStatement 参数时要使用的类型。如果 stringtype 被设置为 varchar(默认值),那么这些参数将作为 varchar 参数发送到服务端。如果 stringtype 被设置为 unspecified,那么这些参数将作为未指定类型的值发送到服务端,服务器将尝试使用隐式转换推断出适当的类型 |
| ApplicationName | String | Uxdb JDBC Driver | * | 指定正在使用连接的应用程序的名称。这个参数允许数据库管理员通过诸如ux_stat_activity等视图来查看连接到服务器的应用程序以及它们通过连接使用的资源 |
| currentSchema | String | null | * | 指定要设置在search-path中的模式(或多个模式以逗号进行分隔)。这些模式将用于解析在该连接上statements中使用的语句 |
| runningMode | String | standard | oracle | 表示jdbc将会以哪一种兼容模式连接数据库,从而改变jdbc内置sql的相关行为,以适配目标数据库。此参数的值与目标数据库无关,如果参数与目标数据库的模式产生错误,将会产生预期的异常 |
| lobBinaryMode | boolean | true | false | 是否使用二进制模式操作大对象 |
| urlDBname | String | uxdb | postgresql | 控制databasemetadata中geturl方法输出结果中的数据库名称 |
| boolean2numeric | boolean | false | true | 使用setBoolean方法时,是否将true和false转换为0和1 |
2.5.3.连接故障转移
UXDB JDBC原生提供Client Site HA。
为了支持简单的连接故障转移,可以在用逗号分隔的连接url中定义多个节点的连接信息(IP、Port)。驱动程序将按顺序依次尝试连接,直到连接成功;如果所有的连接都不成功,则会抛出连接异常。
连接url的语法为:
jdbc:uxdb://node1,node2,node3/accounting?targetServerType=preferSecondary&loadBalanceHosts=true
2.5.3.1.参数
- loadBalanceHosts = boolean
- true:在所有的节点中随机选取一个进行连接
- false:在所有的节点依次选取进行连接
- targetServerType = String
共有4个可选参数:any、primary、secondary、preferSecondary.
- any:备选的节点可以是任意类型
- primary:只会选取节点类型为primary的节点,即使仅有secondary节点时也是如此,出现这种情况会直接返回连接错误
- secondary:只会选取节点类型为secondary的节点,即使仅有primary节点时也是如此,出现这种情况会直接返回连接错误
- preferSecondary:优先选取secondary节点进行连接,如果所有的secondary节点都不可用,那么连接primary节点
2.5.3.2.说明
- 对节点(连接)的选择只出现在创建连接的阶段。
- 当前连接中断后,相关的异常会返回给客户端程序,本次操作会失败,且JDBC不会对操作进行重试。
- 重试机制依赖客户端代码,如果客户端代码没有重试(重连)机制,那么这条语句会永久失败。
3.JDBC Driver API
3.1.连接对象
3.1.1.Connection API
Connection 类是 Java JDBC(Java Database Connectivity)API 中的一个关键接口,用于表示与数据库的连接。它是在 java.sql 包中定义的。
3.1.1.1.主要方法介绍
createStatement
用于创建一个Statement对象,该对象用于执行 SQL 语句。Statement createStatement() throws SQLException;prepareStatement
用于创建一个PreparedStatement对象,该对象表示预编译的 SQL 语句。PreparedStatement prepareStatement(String sql) throws SQLException;close
用于关闭数据库连接。void close() throws SQLException;commit和rollback
用于提交和回滚事务。void commit() throws SQLException; void rollback() throws SQLException;setAutoCommit
用于设置是否自动提交事务。void setAutoCommit(boolean autoCommit) throws SQLException;getMetaData
用于获取与此Connection对象关联的数据库的元数据信息。DatabaseMetaData getMetaData() throws SQLException;setReadOnly和isReadOnly
用于设置和获取数据库连接的只读状态。void setReadOnly(boolean readOnly) throws SQLException; boolean isReadOnly() throws SQLException;setTransactionIsolation和getTransactionIsolation
用于设置和获取事务的隔离级别。void setTransactionIsolation(int level) throws SQLException; int getTransactionIsolation() throws SQLException;nativeSQL
将 SQL 语句转换为本地数据库的语法。String nativeSQL(String sql) throws SQLException;setSavepoint和rollback
用于设置保存点和在需要时回滚到保存点。Savepoint setSavepoint() throws SQLException; void rollback(Savepoint savepoint) throws SQLException;prepareCall
用于创建CallableStatement对象,表示调用存储过程的预编译 SQL 语句。CallableStatement prepareCall(String sql) throws SQLException;
这些方法提供了对数据库连接的常见操作,包括执行 SQL 语句、事务管理、元数据查询等。
3.1.2.DataSource API
DataSource 是 Java JDBC API 中的一个接口,用于获取数据库连接。DataSource 接口提供了一种标准的方式来管理数据库连接,使得应用程序更容易维护和配置。
3.1.2.1.主要方法介绍
getConnection
用于获取与数据库的连接。这是DataSource最重要的方法。Connection getConnection() throws SQLException;getConnection方法的重载形式
允许传入用户名和密码等参数,用于创建带有特定属性的数据库连接。Connection getConnection(String username, String password) throws SQLException;unwrap和isWrapperFor
用于检查DataSource是否包装了指定的类。<T> T unwrap(Class<T> iface) throws SQLException; boolean isWrapperFor(Class<?> iface) throws SQLException;setLoginTimeout和getLoginTimeout
用于设置和获取登录超时时间,即获取数据库连接的最大等待时间。void setLoginTimeout(int seconds) throws SQLException; int getLoginTimeout() throws SQLException;setLogWriter和getLogWriter
用于设置和获取日志输出的PrintWriter对象。void setLogWriter(PrintWriter out) throws SQLException; PrintWriter getLogWriter() throws SQLException;getParentLogger
用于获取父级日志记录器的方法,支持 Java 8 的java.util.logging。Logger getParentLogger() throws SQLFeatureNotSupportedException;
DataSource 的实现类通常是由数据库厂商提供的,或者通过一些开源的 JDBC 连接池实现。使用 DataSource 能够更好地管理数据库连接,提高应用程序的性能和可维护性。例如,常见的实现类包括 BasicDataSource(Apache Commons DBCP)、HikariDataSource(HikariCP)等。
3.1.3.ConnectionPoolDataSource API
javax.sql.ConnectionPoolDataSource 是 Java JDBC API 中的接口,用于表示数据库连接池数据源。连接池数据源允许在应用程序和数据库之间保持一组预先创建的数据库连接,以提高性能和资源利用率。
3.1.3.1.主要方法介绍
getPooledConnection
用于从连接池中获取一个PooledConnection对象,该对象代表一个物理数据库连接。PooledConnection getPooledConnection() throws SQLException;getPooledConnection方法的重载形式 允许传入用户名和密码等参数,用于创建带有特定属性的数据库连接。PooledConnection getPooledConnection(String user, String password) throws SQLException;
ConnectionPoolDataSource 通常是由数据库驱动程序提供的,而不是直接在应用程序中实现。连接池数据源的使用可以通过不同的数据库连接池实现,例如 Apache Commons DBCP、HikariCP、C3P0 等。
以下是一个简单的示例,使用 Apache Commons DBCP 的 PoolingDataSource:
import org.apache.commons.dbcp2.PoolingDataSource;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;
import java.sql.Connection;
import java.sql.SQLException;
public class JdbcWithConnectionPoolDataSource {
public static void main(String[] args) {
// 创建并配置连接池数据源
ConnectionPoolDataSource poolDataSource = configureConnectionPoolDataSource();
// 使用连接池数据源获取连接
try (Connection connection = poolDataSource.getPooledConnection().getConnection()) {
// 执行数据库操作
// ...
} catch (SQLException e) {
e.printStackTrace();
}
}
private static ConnectionPoolDataSource configureConnectionPoolDataSource() {
// 在实际应用中,根据具体的数据库连接池库,配置连接池数据源
// 这里演示使用 Apache Commons DBCP 的 BasicDataSource 作为 ConnectionPoolDataSource 的实现
org.apache.commons.dbcp2.BasicDataSource basicDataSource = new org.apache.commons.dbcp2.BasicDataSource();
basicDataSource.setDriverClassName("com.uxsino.uxdb.Driver");
basicDataSource.setUrl("jdbc:uxdb://localhost:52025/uxdb");
basicDataSource.setUsername("your_username");
basicDataSource.setPassword("your_password");
// 将 BasicDataSource 转换为 ConnectionPoolDataSource
return basicDataSource;
}
}
请注意,实际使用中,你可能会选择使用专门的连接池库,而不是直接使用 ConnectionPoolDataSource 接口。不同的库提供了更丰富的连接池管理功能,可以根据具体需求进行选择。
3.1.4.PooledConnection API
javax.sql.PooledConnection 是 Java JDBC API 中的接口,它表示从数据库连接池中获取的物理连接。PooledConnection 是由 ConnectionPoolDataSource 创建的,它包装了实际的数据库连接,提供了对连接的一些管理和控制的功能。
3.1.4.1.主要方法介绍
addConnectionEventListener和removeConnectionEventListener
用于向PooledConnection注册和移除连接事件监听器,监听连接的状态变化。void addConnectionEventListener(ConnectionEventListener listener); void removeConnectionEventListener(ConnectionEventListener listener);addStatementEventListener和removeStatementEventListener
用于向PooledConnection注册和移除语句事件监听器,监听语句的生命周期事件。void addStatementEventListener(StatementEventListener listener); void removeStatementEventListener(StatementEventListener listener);getConnection
用于从PooledConnection中获取一个物理连接。Connection getConnection() throws SQLException;close
用于关闭PooledConnection,通常会将连接返还给连接池。void close() throws SQLException;
PooledConnection 接口通常用于与连接池一起工作,而不是直接在应用程序中使用。在连接池管理的环境中,当从连接池中请求连接时,将返回一个实现了 PooledConnection 接口的对象,该对象包装了一个真实的数据库连接。通过 PooledConnection,可以监听连接的状态和语句的执行情况,从而进行更细粒度的控制和管理。
3.1.5.XAConnection API
javax.sql.XAConnection 是 Java JDBC API 中的接口,它扩展了 PooledConnection 接口,并提供了对 XA(eXtended Architecture)事务的支持。XA 是一种分布式事务的标准,用于在多个资源管理器(例如数据库)之间协调事务。
3.1.5.1.主要方法介绍
getXAResource
用于获取与此连接相关联的XAResource对象,该对象提供了对 XA 事务的支持。XAResource getXAResource() throws SQLException;close
用于关闭XAConnection,通常会将连接返还给连接池。void close() throws SQLException;
XAConnection 通常与 JTA(Java Transaction API)一起使用,以在分布式事务环境中协调事务。在一个分布式事务中,多个资源(如数据库)可能参与到同一个事务中,而 XAConnection 提供了对这种情况的支持。
使用 XAConnection 可以将多个数据库连接(实现了 XADataSource 接口的数据源)加入到同一个分布式事务中,以确保这些数据库连接在事务提交或回滚时能够协同工作。典型的用法是在一个 JTA 事务中获取 XAConnection,然后从中获取 XAResource,将其参与到全局事务中。
3.1.6.XADataSource API
javax.sql.XADataSource 是 Java JDBC API 中的接口,它扩展了 DataSource 接口,提供了对 XA(eXtended Architecture)事务的支持。XA 是一种分布式事务的标准,用于在多个资源管理器(例如数据库)之间协调事务。
3.1.6.1.主要方法介绍
getXAConnection
用于获取与此数据源的连接,返回一个XAConnection对象,该对象提供了对 XA 事务的支持。XAConnection getXAConnection() throws SQLException;getXAConnection方法的重载形式
允许传入用户名和密码等参数,用于创建带有特定属性的数据库连接。XAConnection getXAConnection(String user, String password) throws SQLException;isWrapperFor和unwrap
用于检查此对象是否包装了指定的类,以及将此对象强制转换为指定类的方法。boolean isWrapperFor(Class<?> iface) throws SQLException; <T> T unwrap(Class<T> iface) throws SQLException;
XADataSource 通常用于与 JTA(Java Transaction API)一起使用,以在分布式事务环境中协调事务。在一个分布式事务中,多个资源(如数据库)可能参与到同一个事务中,而 XADataSource 提供了对这种情况的支持。
使用 XADataSource 可以将多个数据库连接(实现了 XAConnection 接口的数据源)加入到同一个分布式事务中,以确保这些数据库连接在事务提交或回滚时能够协同工作。典型的用法是在一个 JTA 事务中获取 XAConnection,然后从中获取 XAResource,将其参与到全局事务中。
3.2.创建语句对象
3.2.1.Statement 对象
Statement 是 Java JDBC API 中的一个接口,用于执行 SQL 语句并与数据库进行交互。Statement 接口提供了向数据库发送 SQL 查询和更新的方法,以及处理结果的方法。
3.2.1.1.主要方法介绍
execute
用于执行任何 SQL 语句,可以是查询、更新或者DDL语句。返回true表示执行的是查询语句,false表示执行的是更新或者DDL语句。boolean execute(String sql) throws SQLException;executeQuery
用于执行查询语句,返回一个ResultSet对象,该对象包含了查询的结果集。ResultSet executeQuery(String sql) throws SQLException;executeUpdate
用于执行更新语句,例如插入、更新或删除数据。返回一个整数,表示受影响的行数。int executeUpdate(String sql) throws SQLException;addBatch
用于将多个 SQL 语句添加到批处理中,以便一次性执行。void addBatch(String sql) throws SQLException;clearBatch
用于清除批处理中的所有 SQL 语句。void clearBatch() throws SQLException;executeBatch
用于执行批处理中的所有 SQL 语句。int[] executeBatch() throws SQLException;close
用于关闭Statement对象,释放相关资源。void close() throws SQLException;getResultSet
用于获取当前Statement对象的结果集。ResultSet getResultSet() throws SQLException;getUpdateCount
用于获取执行更新语句后受影响的行数。int getUpdateCount() throws SQLException;getMoreResults
用于检查是否有更多的结果集。如果有,返回true。boolean getMoreResults() throws SQLException;
Statement 接口的具体实现类包括 PreparedStatement(预编译的语句,用于执行带有参数的 SQL 语句)、CallableStatement(用于调用存储过程的预编译 SQL 语句)等。选择适当的 Statement 子类取决于要执行的 SQL 语句的类型和参数。
3.2.2.PreparedStatement 对象
PreparedStatement是JDBC中的一个接口,它继承自Statement接口,用于执行预编译的SQL语句。
与Statement不同的是,PreparedStatement允许在执行之前预编译SQL语句,这样可以提高执行相同或类似SQL语句的效率,并且可以防止SQL注入攻击。
String sql = "INSERT INTO employees (id, name, salary) VALUES (?, ?, ?)";
PreparedStatement pstmt = connection.prepareStatement(sql);
在这个例子中,?是占位符,表示待定的参数。在实际执行之前,这个SQL语句已经被预编译。
3.2.2.1.主要方法介绍
- 设置参数
setXXX(int parameterIndex, XXX value):用于设置SQL语句中占位符(?)的值,其中XXX表示具体的数据类型,例如setInt、setString、setDouble等。parameterIndex表示占位符的位置,从1开始计数。pstmt.setInt(1, 101); // 设置第一个占位符为整数值101 pstmt.setString(2, "John Doe"); // 设置第二个占位符为字符串值"John Doe" pstmt.setDouble(3, 50000.00); // 设置第三个占位符为双精度浮点数值50000.00
- 执行SQL语句
executeQuery():用于执行SELECT语句,返回一个ResultSet对象,该对象包含了查询结果。ResultSet rs = pstmt.executeQuery();executeUpdate():用于执行INSERT、UPDATE或DELETE等更新操作,返回受影响的行数。int rowsAffected = pstmt.executeUpdate();
- 批处理
addBatch():将当前设置的参数添加到批处理中,用于执行多个语句。pstmt.addBatch();executeBatch():执行批处理中的所有语句,并返回一个整数数组,其中包含每个语句执行后受影响的行数。int[] batchResults = pstmt.executeBatch();
- 清理资源
close():用于关闭PreparedStatement对象,释放相关的数据库和JDBC资源。pstmt.close();
3.2.3.CallableStatement对象
CallableStatement是JDBC中的一个接口,继承自PreparedStatement,用于调用数据库中的存储过程(Procedure)。存储过程是预先编译并存储在数据库中的一组SQL语句,可以通过存储过程的名字调用执行。
3.2.3.1.主要方法介绍
- 注册输出参数
registerOutParameter(int parameterIndex, int sqlType):用于注册存储过程的输出参数。parameterIndex表示参数的位置,sqlType表示参数的SQL类型。cst.registerOutParameter(1, Types.INTEGER); // 注册第一个输出参数为整数类型
- 设置输入参数
setXXX(int parameterIndex, XXX value):用于设置存储过程的输入参数,与PreparedStatement中的方法类似。cst.setInt(2, 101); // 设置第二个输入参数为整数值101 cst.setString(3, "John Doe"); // 设置第三个输入参数为字符串值"John Doe"
- 执行存储过程
execute():用于执行存储过程。cst.execute();
- 获取输出参数的值
getXXX(int parameterIndex):用于获取存储过程执行后输出参数的值,其中XXX表示具体的数据类型。int resultValue = cst.getInt(1); // 获取第一个输出参数的整数值
- 批处理
CallableStatement也支持批处理,可以使用addBatch()和executeBatch()方法。cst.addBatch(); cst.executeBatch();
- 清理资源
close():用于关闭CallableStatement对象,释放相关的数据库和JDBC资源。cst.close();
3.3.查询结果集处理(ResultSet)
ResultSet是JDBC中用于表示查询结果集的接口。当执行SELECT语句时,数据库返回的结果集就是一个ResultSet对象。
3.3.1.主要方法介绍
- 移动光标
next():将光标移动到结果集的下一行,如果有下一行则返回true,否则返回false。while (resultSet.next()) { // 处理当前行的数据 }previous():将光标移动到结果集的上一行。first():将光标移动到结果集的第一行。last():将光标移动到结果集的最后一行。
- 获取数据
getXXX(int columnIndex):获取当前行指定列的数据,其中XXX表示数据类型,例如getInt、getString、getDouble等。int id = resultSet.getInt(1); // 获取第一列的整数值 String name = resultSet.getString(2); // 获取第二列的字符串值getXXX(String columnLabel):通过列名获取当前行指定列的数据。
- 检索元数据信息
getMetaData():获取ResultSetMetaData对象,用于获取有关结果集中列的元数据信息,如列名、数据类型等。ResultSetMetaData metaData = resultSet.getMetaData(); int columnCount = metaData.getColumnCount();
- 更新数据
-
updateXXX(int columnIndex, XXX value): 更新当前行指定列的数据。resultSet.updateString(2, "New Name"); // 更新第二列的字符串值 resultSet.updateRow(); // 更新当前行注意
ResultSet通常用于读取数据,对于更新操作更推荐使用PreparedStatement。
-
- 关闭结果集
close():用于关闭ResultSet对象,释放相关的数据库和JDBC资源。resultSet.close();
3.4.JDBC 大对象数据处理
3.4.1.UXDB中的大对象
我们知道在 Oracle 数据库中,大对象有三种类型,分别是 CLOB,BLOB 和 BFILE。在 Oracle 数据库中大对象最大存储根据配置可以达到 8TB 到 128TB。然而在 UXDB 数据库中并没有提供这三种数据类型。因此在进行迁移的时候,我们需要做类型的映射。我们可以将 CLOB 和 BLOB 分别映射到 text 和 bytea 数据类型上。此外,UXDB 的插件 ux_largeobject 也提供了一种大对象的支持。
3.4.2.text & bytea
CLOB 和 BLOB 分别用于存储字符大对象和二进制大对象,这与 UXDB 中的 text 和 bytea 很类似,因此在迁移 Oracle 数据库的时候也就将他们分别对应起来。
3.4.3.ux_largeobject
ux_largeobject 是 UXDB 插件提供的一个大对象解决方案。在 ux_largeobject 中,所有的大对象都存储在系统表 ux_largeobject 中;此外,每个大对象在系统表 ux_largeobject_metadata 中也会有一条记录大对象的相关元信息,他们的定义如下所示:
uxdb=# \d ux_largeobject
Table "ux_catalog.ux_largeobject"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
loid | oid | | not null |
pageno | integer | | not null |
data | bytea | | not null |
Indexes:
"ux_largeobject_loid_pn_index" UNIQUE, btree (loid, pageno)
uxdb=# \d ux_largeobject_metadata
Table "ux_catalog.ux_largeobject_metadata"
Column | Type | Collation | Nullable | Default
----------+-----------+-----------+----------+---------
oid | oid | | not null |
lomowner | oid | | not null |
lomacl | aclitem[] | | |
Indexes:
"ux_largeobject_metadata_oid_index" UNIQUE, btree (oid)
采用 ux_largeobject 所存储的大对象最大可以达到 4TB 的存储空间,并且支持随机读写。ux_largeobject 采用 OID 的方式来引用 ux_largeobject 表中的大对象。例如,我们创建一个表来存储图片数据,如下所示:
CREATE TABLE image(name text, master oid);
ux_largeobject 提供了一系列函数用于创建、导入和导出大对象,见官方文档服务端函数。下面是简单的大对象插入导出的测试输出:
uxdb=# INSERT INTO image VALUES('image1', lo_import('/tmp/screenshot.png'));
INSERT 0 1
uxdb=# SELECT loid, COUNT(page no) FROM ux_largeobject GROUP BY loid;
loid | count
-------+-------
24598 | 25
(1 row)
uxdb=# SELECT master, lo_export(raster, '/tmp/screenshot-e.png') FROM image WHERE name = 'image1';
master | lo_export
--------+-----------
24598 | 1
(1 row)
uxdb=# \! md5sum /tmp/screenshot.png /tmp/screenshot-e.png
dc51d60215f547a897d4d73beba65ded /tmp/screenshot.png
dc51d60215f547a897d4d73beba65ded /tmp/screenshot-e.png
需要注意的是,在使用 ux 来管理大对象时,我们需要额外的操作来管理大对象。例如,上面的示例中,如果我们想要删除表 image 中名称为 image1 的记录,我们还需在 ux_largeobject 中删除 loid = 24598 的记录。如下所示:
uxdb=# DELETE FROM image WHERE name = 'image1';
DELETE 1
uxdb=# SELECT name FROM image;
name
------
(0 rows)
uxdb=# SELECT loid, COUNT(pageno) FROM ux_largeobject GROUP BY loid;
loid | count
-------+-------
24598 | 25
(1 row)
uxdb=# DELETE FROM ux_largeobject WHERE loid = 24598;
DELETE 25
uxdb=# SELECT loid, COUNT(pageno) FROM ux_largeobject GROUP BY loid;
loid | count
------+-------
(0 rows)
通常,我们会创建一个触发器来进行 OID 的删除。此外,ux_largeobject 提供了 lo_put 和 lo_get 函数来随机读写大对象。需要注意的是,我们在使用 libpq 对大对象进行读写时必须在事务中。
3.4.4.为什么对大对象进行读写时必须在事务中
UXDB 数据库对于大对象类型的资源进行操作时,必须保证所有的大对象操作函数在同一个SQL事务块中发生,因为大对象类型的文件描述符仅在事务的持续时间内有效。
所有使用这些函数的大型对象操作都必须在 SQL 事务块中进行,因为大型对象文件描述符只在事务期间有效。
3.4.5.大对象数据的设置
3.4.5.1.setblob/setclob
- 对于blob/clob/oid类型的列,在url中增加
lobBinaryMode=false - 对于bytea/text列,无需额外设置
InputStream inputStream = new FileInputStream("image.jpg");
pstmt.setBlob(1, inputStream);
Reader reader = new FileReader("text.txt");
pstmt.setClob(2, reader);
注意
在2.1.2.3以及更新的版本中,bytea/text类型的列与blob/clob/oid类型的列在同一个jdbc连接中互斥。即在使用同一个jdbc连接操作数据库时,只能同时操作bytea/text类型的列或者同时操作blob/clob/oid类型的列。
3.4.5.2.其他方法
- 设置BinaryStream参数
setBinaryStream(int parameterIndex, InputStream inputStream, int length):用于设置二进制流参数,其中inputStream是包含二进制数据的输入流,length表示数据的长度。InputStream binaryStream = new FileInputStream("image.jpg"); pstmt.setBinaryStream(1, binaryStream, (int) new File("image.jpg").length());
- 设置Bytes参数
setBytes(int parameterIndex, byte[] bytes):用于设置字节数组参数,其中bytes是包含二进制数据的字节数组。byte[] binaryData = //... pstmt.setBytes(1, binaryData);
3.4.6.大对象数据的获取
和大对象的设置相同
- 对于blob/clob/oid类型的列,在url中增加
lobBinaryMode=false - 对于bytea/text列,无需额外设置
- 获取Blob数据
getBlob(int columnIndex):用于从结果集中获取二进制大对象数据。Blob blob = resultSet.getBlob(1); InputStream binaryStream = blob.getBinaryStream(); // 处理二进制数据流
- 获取Clob数据
getClob(int columnIndex):用于从结果集中获取字符大对象数据。Clob clob = resultSet.getClob(2); Reader characterStream = clob.getCharacterStream(); // 处理字符数据流
- 获取BinaryStream数据
getBinaryStream(int columnIndex):用于从结果集中获取二进制数据流。InputStream binaryStream = resultSet.getBinaryStream(1); // 处理二进制数据流
- 获取Bytes数据
getBytes(int columnIndex):用于从结果集中获取字节数组。byte[] binaryData = resultSet.getBytes(1); // 处理字节数组
3.4.7.大对象数据的更新
-
更新Blob数据
updateBlob(int columnIndex, InputStream inputStream):用于更新结果集中的二进制大对象数据。InputStream newInputStream = new FileInputStream("new_image.jpg"); resultSet.updateBlob(1, newInputStream); resultSet.updateRow(); // 更新当前行
-
更新Clob数据
updateClob(int columnIndex, Reader reader):用于更新结果集中的字符大对象数据。Reader newReader = new FileReader("new_text.txt"); resultSet.updateClob(2, newReader); resultSet.updateRow(); // 更新当前行
-
释放LOB资源
在处理完LOB后,务必释放相关的资源,以避免内存泄漏。
- 对于
Blob,可以调用free()方法:blob.free(); - 对于
Clob,可以调用free()方法:clob.free();
- 对于
3.5.事务处理
在JDBC中,事务的提交和回滚是通过Connection对象来完成的。Connection提供了一些方法来管理事务的提交和回滚操作。
3.5.1.事务的提交与回滚
-
提交事务
commit():用于将之前的事务操作提交到数据库。try { connection.setAutoCommit(false); // 设置手动提交模式 // 执行一系列数据库操作 connection.commit(); // 提交事务 } catch (SQLException e) { // 处理异常,可以选择回滚事务 connection.rollback(); } finally { connection.setAutoCommit(true); // 恢复自动提交模式 }在上面的示例中,首先通过
setAutoCommit(false)将连接的自动提交模式切换为手动提交。然后,执行一系列数据库操作,如果没有异常,最后通过commit()提交事务。如果发生异常,可以选择回滚事务,通过rollback()来实现。最后,通过setAutoCommit(true)将自动提交模式恢复为默认值。
-
回滚事务
rollback(): 用于回滚之前的事务操作,撤销未提交的更改。try { connection.setAutoCommit(false); // 设置手动提交模式 // 执行一系列数据库操作 connection.rollback(); // 回滚事务 } catch (SQLException e) { // 处理异常 } finally { connection.setAutoCommit(true); // 恢复自动提交模式 }在这个示例中,如果发生异常,通过
rollback()回滚事务,撤销未提交的更改。最后,通过setAutoCommit(true)将自动提交模式恢复为默认值。
需要注意的是,事务的管理应该放在
try-catch-finally块中,以确保在发生异常时能够正确地回滚事务并释放资源。
3.5.2.事务的隔离级别
在JDBC中,事务的隔离级别(Isolation Level)是指在多个并发事务同时执行时,一个事务的执行是否受其他事务的影响。
JDBC支持四种事务隔离级别,分别是:
- READ UNCOMMITTED(读取未提交)
允许一个事务读取另一个事务尚未提交的修改。这种隔离级别最低,存在脏读、不可重复读和幻读的风险。connection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED); - READ COMMITTED(读取已提交)
保证一个事务不会读取到另一个未提交事务的修改。但是,可能会存在不可重复读和幻读的风险。connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); - REPEATABLE READ(可重复读)
保证一个事务在执行期间读取到的数据是一致的,不会被其他事务的修改所影响。但是,可能存在幻读的风险。connection.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); - SERIALIZABLE(串行化)
最高的隔离级别,确保一个事务在执行期间对数据的读取和修改不会被其他事务所干扰。可以防止脏读、不可重复读和幻读,但性能较低。connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
3.5.3.Savepoint
Savepoint是JDBC中用于事务管理的一种机制,它允许在事务中设置一个标记,以便在后续操作中进行回滚到该标记位置,而不必回滚整个事务。Savepoint通常用于在事务中创建一个中间点,使得在某个阶段发生错误时可以选择性地回滚到该点。
在JDBC中使用Savepoint的基本步骤如下。
-
创建Savepoint
使用
Connection对象的setSavepoint方法创建Savepoint,可以指定Savepoint的名称或不指定。Connection connection = // 获取连接 Savepoint savepoint = connection.setSavepoint();或者,指定
Savepoint的名称。Savepoint savepoint = connection.setSavepoint("mySavepoint"); -
执行事务操作
在创建
Savepoint后,可以在事务中执行一系列数据库操作。try { // 执行一系列数据库操作 } catch (SQLException e) { // 处理异常,可能回滚到Savepoint } -
回滚到Savepoint
如果在执行事务操作的过程中发生了异常,可以选择回滚到之前设置的
Savepoint。try { // 执行一系列数据库操作 } catch (SQLException e) { connection.rollback(savepoint); // 处理异常 }在这个例子中,如果发生异常,将会回滚到之前设置的
Savepoint,而不是回滚整个事务。 -
释放Savepoint
Savepoint在使用后可以选择释放,以释放相关资源。connection.releaseSavepoint(savepoint);
4.示例
4.1.DataSource
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
public class DataSourceExample {
public static DataSource getDataSource() {
BasicDataSource dataSource = new BasicDataSource();
// 设置数据库连接信息
dataSource.setDriverClassName("com.uxsino.uxdb.Driver");
dataSource.setUrl("jdbc:uxdb://localhost:52025/uxdb");
dataSource.setUsername("username");
dataSource.setPassword("password");
// 设置连接池属性
dataSource.setInitialSize(5); // 初始连接数
dataSource.setMaxTotal(20); // 最大连接数
dataSource.setMaxIdle(10); // 最大空闲连接数
dataSource.setMinIdle(5); // 最小空闲连接数
dataSource.setMaxWaitMillis(5000); // 最大等待时间(毫秒)
return dataSource;
}
public static void main(String[] args) {
DataSource dataSource = getDataSource();
// 使用try-with-resources确保在结束时关闭连接
try (Connection connection = dataSource.getConnection()) {
// 执行数据库操作
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM mytable");
while (resultSet.next()) {
// 处理查询结果
}
} catch (SQLException e) {
// 处理异常
e.printStackTrace();
}
}
}
4.2.ConnectionPoolDataSource
import com.uxsino.uxdb.ds.UXConnectionPoolDataSource;
import javax.sql.ConnectionPoolDataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class UxdbConnectionPoolDataSourceExample {
public static ConnectionPoolDataSource createDataSource() {
UXConnectionPoolDataSource dataSource = new UXConnectionPoolDataSource();
// 设置数据库连接信息
dataSource.setServerName("localhost");
dataSource.setPortNumber(52025);
dataSource.setDatabaseName("uxdb");
dataSource.setUser("username");
dataSource.setPassword("password");
// 设置连接池属性
dataSource.setMaxConnections(10); // 最大连接数
dataSource.setMinConnections(5); // 最小连接数
dataSource.setMaxIdleTime(600000); // 最大空闲时间(毫秒)
return dataSource;
}
public static void main(String[] args) {
ConnectionPoolDataSource dataSource = createDataSource();
try {
// 使用连接池创建连接
PooledConnection pooledConnection = dataSource.getPooledConnection();
Connection connection = pooledConnection.getConnection();
// 执行数据库操作
// ...
// 关闭连接
connection.close();
pooledConnection.close();
} catch (SQLException e) {
// 处理异常
e.printStackTrace();
}
}
}
4.3.Statement
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class JDBCStatementExample {
public static void main(String[] args) {
// JDBC连接参数
String jdbcUrl = "jdbc:uxdb://localhost:52025/uxdb";
String username = "username";
String password = "password";
try {
// 1. 注册 JDBC 驱动
Class.forName("com.uxsino.uxdb.Driver");
// 2. 创建数据库连接
Connection connection = DriverManager.getConnection(jdbcUrl, username, password);
// 3. 创建 Statement 对象
Statement statement = connection.createStatement();
// 4. 执行 SQL 查询
String sqlQuery = "SELECT * FROM mytable";
ResultSet resultSet = statement.executeQuery(sqlQuery);
// 5. 处理查询结果
while (resultSet.next()) {
// 读取每一行数据
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
// 处理数据
System.out.println("ID: " + id + ", Name: " + name);
}
// 6. 关闭资源
resultSet.close();
statement.close();
connection.close();
} catch (Exception e) {
// 处理异常
e.printStackTrace();
}
}
}
4.4.PreparedStatement
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class JDBCPreparedStatementExample {
public static void main(String[] args) {
// JDBC连接参数
String jdbcUrl = "jdbc:uxdb://localhost:52025/uxdb";
String username = "username";
String password = "password";
try {
// 1. 注册 JDBC 驱动
Class.forName("com.uxsino.uxdb.Driver");
// 2. 创建数据库连接
Connection connection = DriverManager.getConnection(jdbcUrl, username, password);
// 3. 创建 PreparedStatement 对象
String sqlQuery = "SELECT * FROM mytable WHERE id = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sqlQuery);
// 4. 设置参数
int targetId = 1;
preparedStatement.setInt(1, targetId);
// 5. 执行查询
ResultSet resultSet = preparedStatement.executeQuery();
// 6. 处理查询结果
while (resultSet.next()) {
// 读取每一行数据
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
// 处理数据
System.out.println("ID: " + id + ", Name: " + name);
}
// 7. 关闭资源
resultSet.close();
preparedStatement.close();
connection.close();
} catch (Exception e) {
// 处理异常
e.printStackTrace();
}
}
}
4.5.CallableStatement
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Types;
public class JDBCCallableStatementExample {
public static void main(String[] args) {
// JDBC连接参数
String jdbcUrl = "jdbc:uxdb://localhost:52025/uxdb";
String username = "username";
String password = "password";
try {
// 1. 注册 JDBC 驱动
Class.forName("com.uxsino.uxdb.Driver");
// 2. 创建数据库连接
Connection connection = DriverManager.getConnection(jdbcUrl, username, password);
// 3. 创建 CallableStatement 对象
String sqlCall = "{call get_employee(?, ?)}"; // 存储过程的调用语法
CallableStatement callableStatement = connection.prepareCall(sqlCall);
// 4. 设置输入参数
int employeeId = 101;
callableStatement.setInt(1, employeeId);
// 5. 注册输出参数
callableStatement.registerOutParameter(2, Types.VARCHAR);
// 6. 执行存储过程
callableStatement.execute();
// 7. 获取输出参数的值
String employeeName = callableStatement.getString(2);
System.out.println("Employee Name: " + employeeName);
// 8. 关闭资源
callableStatement.close();
connection.close();
} catch (Exception e) {
// 处理异常
e.printStackTrace();
}
}
}
4.6.ResultSet
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class JDBCResultSetExample {
public static void main(String[] args) {
// JDBC连接参数
String jdbcUrl = "jdbc:uxdb://localhost:52025/uxdb";
String username = "username";
String password = "password";
try {
// 1. 注册 JDBC 驱动
Class.forName("com.uxsino.uxdb.Driver");
// 2. 创建数据库连接
Connection connection = DriverManager.getConnection(jdbcUrl, username, password);
// 3. 创建 Statement 对象
Statement statement = connection.createStatement();
// 4. 执行 SQL 查询
String sqlQuery = "SELECT id, name, age FROM mytable";
ResultSet resultSet = statement.executeQuery(sqlQuery);
// 5. 遍历查询结果
while (resultSet.next()) {
// 获取每一行的数据
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
// 打印数据
System.out.println("ID: " + id + ", Name: " + name + ", Age: " + age);
}
// 6. 获取列的值
resultSet.first(); // 移动到第一行
int firstId = resultSet.getInt(1); // 获取第一列的值
System.out.println("First ID: " + firstId);
// 7. 获取特定类型的值
resultSet.last(); // 移动到最后一行
String lastName = resultSet.getString("name");
System.out.println("Last Name: " + lastName);
// 8. 获取总行数
resultSet.beforeFirst(); // 移动到第一行之前
resultSet.last(); // 移动到最后一行
int rowCount = resultSet.getRow(); // 获取行数
System.out.println("Total Rows: " + rowCount);
// 9. 关闭资源
resultSet.close();
statement.close();
connection.close();
} catch (Exception e) {
// 处理异常
e.printStackTrace();
}
}
}
4.7.BLOB
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Blob;
import java.io.InputStream;
public class BlobExample {
public static void main(String[] args) {
String jdbcUrl = "jdbc:uxdb://localhost:52025/uxdb";
// 如果列类型为blob/oid
// String jdbcUrl = "jdbc:uxdb://localhost:52025/uxdb?lobBinaryMode=false";
String username = "your_username";
String password = "your_password";
try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password)) {
// 创建包含 Blob 类型的表
// String createTableQuery = "CREATE TABLE binary_data_table (id SERIAL PRIMARY KEY, binary_data Blob)";
String createTableQuery = "CREATE TABLE binary_data_table (id SERIAL PRIMARY KEY, binary_data BYTEA)";
try (PreparedStatement createTableStatement = connection.prepareStatement(createTableQuery)) {
createTableStatement.execute();
}
// 插入二进制数据
String insertQuery = "INSERT INTO binary_data_table (binary_data) VALUES (?)";
try (InputStream inputStream = yourBinaryDataInputStream;
PreparedStatement insertStatement = connection.prepareStatement(insertQuery)) {
// 如果列类型为blob/oid
// insertStatement.setBlob(1, inputStream);
insertStatement.setBinaryStream(1, inputStream);
insertStatement.executeUpdate();
}
// 查询二进制数据
String selectQuery = "SELECT * FROM binary_data_table";
try (PreparedStatement selectStatement = connection.prepareStatement(selectQuery);
ResultSet resultSet = selectStatement.executeQuery()) {
while (resultSet.next()) {
// 如果列类型为blob/oid
// Blob binaryData = resultSet.getBlob("binary_data");
// InputStream binaryStream = binaryData.getBinaryStream();
InputStream binaryStream = resultSet.getBinaryStream("binary_data");
// ... (处理二进制数据的逻辑)
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.8.CLOB
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Clob;
import java.io.Reader;
public class ClobExample {
public static void main(String[] args) {
String jdbcUrl = "jdbc:uxdb://localhost:52025/uxdb";
// 如果列类型为clob/oid
// String jdbcUrl = "jdbc:uxdb://localhost:52025/uxdb?lobBinaryMode=false";
String username = "your_username";
String password = "your_password";
try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password)) {
// 创建包含 Clob 类型的表
// String createTableQuery = "CREATE TABLE binary_data_table (id SERIAL PRIMARY KEY, binary_data Clob)";
String createTableQuery = "CREATE TABLE text_data_table (id SERIAL PRIMARY KEY, text_data TEXT)";
try (PreparedStatement createTableStatement = connection.prepareStatement(createTableQuery)) {
createTableStatement.execute();
}
// 插入文本数据
String insertQuery = "INSERT INTO text_data_table (text_data) VALUES (?)";
try (Reader reader = yourTextDataReader;
PreparedStatement insertStatement = connection.prepareStatement(insertQuery)) {
// 如果列类型为clob/oid
// insertStatement.setClob(1, reader);
insertStatement.setCharacterStream(1, reader);
insertStatement.executeUpdate();
}
// 查询文本数据
String selectQuery = "SELECT * FROM text_data_table";
try (PreparedStatement selectStatement = connection.prepareStatement(selectQuery);
ResultSet resultSet = selectStatement.executeQuery()) {
while (resultSet.next()) {
// 如果列类型为clob/oid
// Clob textData = resultSet.getClob("text_data");
// String textContent = textData.getSubString(1, (int) textData.length());
String textContent = resultSet.getString("text_data");
// ... (处理文本数据的逻辑)
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.9.Transaction
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class JDBCTransactionExample {
public static void main(String[] args) {
// JDBC连接参数
String jdbcUrl = "jdbc:uxdb://localhost:52025/uxdb";
String username = "username";
String password = "password";
Connection connection = null;
try {
// 1. 注册 JDBC 驱动
Class.forName("com.uxsino.uxdb.Driver");
// 2. 创建数据库连接
connection = DriverManager.getConnection(jdbcUrl, username, password);
// 3. 设置事务的隔离级别(可选)
connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
// 4. 关闭自动提交(开启事务)
connection.setAutoCommit(false);
// 5. 执行事务操作
executeTransaction(connection);
// 6. 提交事务
connection.commit();
} catch (Exception e) {
// 事务回滚
try {
if (connection != null) {
connection.rollback();
}
} catch (SQLException se) {
se.printStackTrace();
}
// 处理异常
e.printStackTrace();
} finally {
// 7. 关闭资源
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
private static void executeTransaction(Connection connection) throws SQLException {
// 事务中的操作1
try (Statement statement1 = connection.createStatement()) {
statement1.executeUpdate("INSERT INTO mytable (name, age) VALUES ('John', 30)");
}
// 事务中的操作2
try (Statement statement2 = connection.createStatement()) {
statement2.executeUpdate("UPDATE mytable SET age = 31 WHERE name = 'John'");
}
}
}
在这个例子中,我们创建了一个包含两个数据库操作的事务。
首先,我们关闭了自动提交(connection.setAutoCommit(false)),这意味着我们将手动管理事务。
然后,我们执行了两个操作,如果其中一个失败,我们将回滚事务(connection.rollback()),否则我们提交事务(connection.commit())。
4.10.DatabaseMetadata
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
public class JDBCDatabaseMetaDataExample {
public static void main(String[] args) {
// JDBC连接参数
String jdbcUrl = "jdbc:uxdb://localhost:52025/uxdb";
String username = "username";
String password = "password";
try {
// 1. 注册 JDBC 驱动
Class.forName("com.uxsino.uxdb.Driver");
// 2. 创建数据库连接
Connection connection = DriverManager.getConnection(jdbcUrl, username, password);
// 3. 获取 DatabaseMetaData 对象
DatabaseMetaData databaseMetaData = connection.getMetaData();
// 4. 获取数据库信息
System.out.println("Database Product Name: " + databaseMetaData.getDatabaseProductName());
System.out.println("Database Product Version: " + databaseMetaData.getDatabaseProductVersion());
System.out.println("Driver Name: " + databaseMetaData.getDriverName());
System.out.println("Driver Version: " + databaseMetaData.getDriverVersion());
// 5. 获取表信息
ResultSet tablesResultSet = databaseMetaData.getTables(null, null, "%", null);
System.out.println("Tables:");
while (tablesResultSet.next()) {
String tableName = tablesResultSet.getString("TABLE_NAME");
System.out.println(tableName);
}
// 6. 获取列信息
String tableName = "mytable";
ResultSet columnsResultSet = databaseMetaData.getColumns(null, null, tableName, "%");
System.out.println("Columns in " + tableName + ":");
while (columnsResultSet.next()) {
String columnName = columnsResultSet.getString("COLUMN_NAME");
String columnType = columnsResultSet.getString("TYPE_NAME");
System.out.println("Name: " + columnName + ", Type: " + columnType);
}
// 7. 关闭资源
tablesResultSet.close();
columnsResultSet.close();
connection.close();
} catch (Exception e) {
// 处理异常
e.printStackTrace();
}
}
}
4.11.获取执行计划
要获取 UXDB 数据库中 SQL 查询的执行计划,可以使用 ux_stat_statements 扩展和 JDBC 驱动的一些功能。
- 首先确保你的 UXDB 数据库启用了
ux_stat_statements扩展。你可以通过编辑 UXDB 配置文件(通常是uxsinodb.conf)并添加以下行来启用:shared_preload_libraries = 'ux_stat_statements' - 重启UXDB数据库
- 接下来,确保你的 JDBC 连接字符串(
jdbcUrl)包含ux_stat_statements:String jdbcUrl = "jdbc:uxdb://localhost:52025/uxdb?user=username&password=password¤tSchema=public&loglevel=2";在这个连接字符串中,
loglevel=2表示开启详细的日志记录,其中包括执行计划。 - 然后,你可以使用以下代码来获取 SQL 查询的执行计划:
import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Statement; public class UxdbExecutionPlanExample { public static void main(String[] args) { // JDBC连接参数 String jdbcUrl = "jdbc:uxdb://localhost:52025/uxdb?user=username&password=password¤tSchema=public&loglevel=2"; try { // 1. 注册 JDBC 驱动 Class.forName("com.uxsino.uxdb.Driver"); // 2. 创建数据库连接 Connection connection = DriverManager.getConnection(jdbcUrl); // 3. 执行 SQL 查询 String sqlQuery = "SELECT * FROM mytable WHERE column = ?"; PreparedStatement preparedStatement = connection.prepareStatement(sqlQuery); preparedStatement.setString(1, "some_value"); // 4. 打印执行计划 printExecutionPlan(preparedStatement); // 5. 关闭资源 preparedStatement.close(); connection.close(); } catch (Exception e) { // 处理异常 e.printStackTrace(); } } private static void printExecutionPlan(PreparedStatement preparedStatement) { try { // 使用 ux_stat_statements 获取执行计划 String planQuery = "SELECT * FROM ux_stat_statements WHERE queryid = ux_prepare(?, ?)::oid"; try (PreparedStatement planStatement = preparedStatement.getConnection().prepareStatement(planQuery)) { planStatement.setString(1, preparedStatement.toString()); planStatement.setInt(2, preparedStatement.getFetchSize()); try (ResultSet resultSet = planStatement.executeQuery()) { while (resultSet.next()) { String query = resultSet.getString("query"); String plan = resultSet.getString("plantext"); System.out.println("Query: " + query); System.out.println("Execution Plan: " + plan); } } } } catch (Exception e) { // 处理异常 e.printStackTrace(); } } }