使用JDBC连接YashanDB快速入门
课程介绍及演示环境介绍
1.课程介绍
课程介绍:本课程旨在帮助Java开发人员深入理解和掌握YashanDB JDBC驱动的使用方法,通过实际案例和代码演示,使学员能够熟练地在Java应用程序中连接和操作YashanDB数据库,提升开发效率应用性能。
课程定位:专为数据库技术的初学者设计,帮助学员快速上手YashanDB的基本连接配置与数据操作。
学习目标:掌握YashanDB JDBC驱动的核心功能和配置要点,使学员能够根据YashanDB的部署场景独立配置JDBC连接,实现与YashanDB的交互,并熟练运用JDBC API实现对YashanDB的增删改查(CRUD)操作。
2.演示环境介绍
• 操作系统版本:Microsoft Windows 10 专业版
• 操作系统架构:64位
• 数据库部署形态:
|
IP地址
|
端口
|
用途
|
|
192.168.33.136
|
1688
|
崖山数据库V23.4.1.102企业版-主库
|
|
192.168.33.145
|
1688
|
崖山数据库V23.4.1.102企业版-备库
|
|
192.168.33.146
|
1688
|
崖山数据库V23.4.1.102企业版-备库
|
• 数据库工具:YDC 23.4.1.2
一、YashanDB JDBC驱动概述
JDBC(Java DataBase Connectivity)是一套标准的用于在Java语言中访问数据库的应用程序接口(Java API),YashanDB JDBC驱动是YashanDB对Java API的实现,使Java程序可以通过标准的SQL语句对YashanDB执行数据库操作。除此之外,YashanDB JDBC驱动还包括如下主要功能:
• 超时机制,包括连接超时、socket超时、SQL执行超时等。
• 在高可用的主备部署中,自动识别并连接主库。
• 在负载均衡的共享集群和分布式部署中,自动识别并连接负载最小的节点。
• 安全连接配置,包括SSL加密通信、TLCP加密通信、SM4密码加密算法等等。
• 透明应用程序故障转移功能,在当前连接节点发生故障后,客户端能够自动重新连接到已配置的其他节点。
• 心跳连接的配置,开启心跳连接能够使得程序更快的检测到正在使用的连接已经异常。
在实际开发中,我们会使用ORM框架简化对数据库的操作,提高开发效率,常见的基于JDBC的ORM框架有jpa-hibernate、Mybatis 和 Mybatis-plus等等。
二、YashanDB JDBC驱动的安装
1.搭建Java开发环境
1.1 下载JDK
①在Oracle官网根据自己的需要下载JDK安装包。YashanDB JDBC驱动对JDK版本的要求是1.8及以上,对JRE的版本的要求是1.8及以上。这里选择的版本是JDK1.8,在安装JDK的时候JRE也会同时被安装。

②下载后双击打开exe文件,根据提示安装即可。


1.2 配置环境变量
①在系统变量中新增变量JAVA_HOME。
变量名:JAVA_HOME
变量值:C:\Program Files\Java\jdk-1.8(以实际的安装路径为准)

②在系统变量PATH中新增路径%JAVA_HOME%\bin和%JAVA_HOME%\jre\bin。

1.3 验证Java开发环境是否搭建成功
打开CMD命令行操作工具,输入java -version,若能正常输出版本信息,则说明Java开发环境已经搭建成功。

2.离线安装YashanDB JDBC
2.1 新建项目
打开IntelliJ IDEA开发工具,新建一个Java项目。

2.2 引入驱动
打开项目结构窗口,将在官网下载中心下载的JDBC驱动的jar文件添加到项目的外部库中。

三、通过YashanDB JDBC驱动连接YashanDB程序开发示例
1.JDBC常见接口和类介绍
①DriverManager:驱动管理类。
• getConnection(String url, String user, String password):用于通过URL、用户名、密码建立数据库连接。
②Connection:用于表示程序与数据库的连接。
• setAutoCommit(boolean autoCommit):用于设置数据库连接的事务的自动提交模式。
• setTransactionIsolation(int level):用于设置数据库连接的事务隔离级别。
• setSavepoint(String name):用于创建保存点。
• commit():用于将当前事务中自上次提交或回滚以来的所有更改永久生效。
• rollback()和rollback(Savepoint):用于撤销当前事务中所做的所有更改或是指定的Savepoint对象设置后所做的一切更改。
• createStatement():创建一个向数据库传递SQL语句的对象,主要用于建表等无需传入参数的操作。
• prepareStatement(String sql):创建一个向数据库传递带参数的 SQL 语句的对象,主要用于增删查改操作等需传入参数的操作。
• prepareCall(String sql):创建一个调用数据库存储过程的对象。
③Statement:用于执行静态的SQL语句。
• execute(String sql):用于执行任意SQL语句。
• executeQuery(String sql):用于执行SELECT语句,返回一个包含给定查询生成的数据的ResultSet对象。
④PreparedStatement:用于执行预编译的SQL语句。
• setInt(int parameterIndex, int value)和setString(int parameterIndex, String x):为SQL语句设置参数。
• executeUpdate():用于执行INSERT、UPDATE、DELETE等DML语句以及DDL语句(如CREATE TABLE)。
• addBatch():用于添加批量处理的SQL命令。
• executeBatch():用于批量执行SQL命令。
⑤CallableStatement:用于执行存储过程。
• registerOutParameter(int parameterIndex, int sqlType):用于注册存储过程的输出参数。
• getString(int parameterIndex):获取输出参数。
⑥ResultSet:用于表示数据库的查询结果集。
• next():将游标从当前行移动到下一行。
更多详细的内容可以在JDBC官方文档查阅,而YashanDB目前支持的JDBC接口/方法可以在YashanDB官方文档中查看。
2.数据库连接描述符的常用配置
一般的URL格式为:
|
Bash
jdbc:yasdb://host:port/database_name
|
参数说明:
host:服务器域名或IP地址,需配置为单机/集群实例服务器地址或分布式CN服务器地址,不填时表示连接localhost。
port:数据库服务端口,例如1688。
database_name:数据库名称,必填项。该参数仅用于兼容,无实际含义且不校验参数值。
2.1 一个简单的连接示例
|
Java
package com.wyc;
import java.sql.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main {
private static final String DRIVER = "com.yashandb.jdbc.Driver";
private static final String URL = "jdbc:yasdb://192.168.33.136:1688/yashan";
private static final String USER = "sales";
private static final String PASS = "sales";
public static void main(String[] args) {
try (Connection conn = getConnection()) {
createCustomerTable(conn);
batchInsertCustomers(conn, Arrays.asList(
new Customer(1, "Alice"),
new Customer(2, "Bob"),
new Customer(3, "Charlie")));
System.out.println("\nInserted data:");
queryCustomers(conn).forEach(System.out::println);
updateCustomerName(conn, 2, "Robert");
System.out.println("\nAfter update:");
queryCustomers(conn).forEach(System.out::println);
deleteCustomer(conn, 3);
System.out.println("\nAfter deletion:");
queryCustomers(conn).forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
}
}
|
|
Java
public static Connection getConnection() throws ClassNotFoundException, SQLException {
Class.forName(DRIVER);
return DriverManager.getConnection(URL, USER, PASS);
}
public static void createCustomerTable(Connection conn) throws SQLException {
String sql = "CREATE TABLE IF NOT EXISTS customer (" +
"id INT PRIMARY KEY, " +
"name VARCHAR(32) NOT NULL)";
try (Statement stmt = conn.createStatement()) {
stmt.execute(sql);
}
}
public static void batchInsertCustomers(Connection conn, List<Customer> customers) throws SQLException {
String sql = "INSERT INTO customer (id, name) VALUES (?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
for (Customer c : customers) {
if(c.getId() <= 0) throw new SQLException("无效ID: "+c.getId());
pstmt.setInt(1, c.getId());
pstmt.setString(2, c.getName());
pstmt.addBatch();
}
int[] batchResults = pstmt.executeBatch();
for (int i=0; i<batchResults.length; i++) {
if(batchResults[i] == Statement.EXECUTE_FAILED) {
throw new SQLException("第"+(i+1)+"条记录插入失败");
}
}
}
}
public static List<Customer> queryCustomers(Connection conn) throws SQLException {
List<Customer> list = new ArrayList<>();
String sql = "SELECT * FROM customer";
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
while (rs.next()) {
list.add(new Customer(
rs.getInt("id"),
rs.getString("name")
));
}
}
return list;
}
|
|
Java
public static void updateCustomerName(Connection conn, int id, String newName) throws SQLException {
String sql = "UPDATE customer SET name = ? WHERE id = ?";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, newName);
pstmt.setInt(2, id);
int rows = pstmt.executeUpdate();
System.out.println("\n更新影响行数:" + rows);
}
}
public static void deleteCustomer(Connection conn, int id) throws SQLException {
String sql = "DELETE FROM customer WHERE id = ?";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, id);
int rows = pstmt.executeUpdate();
System.out.println("\n删除影响行数:" + rows);
}
}
static class Customer {
private int id;
private String name;
public Customer(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Customer [id=" + id + ", name=" + name + "]";
}
}
}
|
在接下来的示例程序的URL配置中,除了使用了基础的URL配置,还增加了某些特殊参数的配置,以满足更复杂的应用场景需求:
|
sql
jdbc:yasdb:primary://192.168.33.136:1688,192.168.33.145:1688/yashan?
connectTimeout=60&
socketTimeout=120&
loginTimeout=60&
poolTimeout=180&
failover=on&
failoverType=session&
failoverMethod=basic&
failoverRetries=5&
failoverDelay=1
|
2.2 超时时间配置
YashanDB JDBC驱动建立了超时处理机制,并允许通过配置超时时间让程序在指定期限达到时进行友好退出。
|
超时时间
|
说明
|
|
connectTimeout
|
建立连接时,创建socket连接的超时时间,单位为s。不配置时默认为10s。通过info属性或者URL配置。
|
|
socketTimeout
|
获取connection后,TCP通信过程中,客户端等待服务端返回数据的超时时间,单位为s。不配置时默认为0s,一直等待。通过info属性或者URL配置。
|
|
loginTimeout
|
创建socket成功后,进行登录认证时,客户端等待服务端返回数据的超时时间,单位为s。不配置时默认为300s。通过info属性或者URL配置。
|
2.3 多IP/PORT连接配置
在高可用主备和负载均衡场景下,可以配置多个IP/PORT连接,URL参数格式为:
jdbc:yasdb:serverType://host1:port1,host2:port2,host3:port3/databasename
|
连接模式
|
说明
|
|
primary
|
表示一主多备连接,JDBC将自动识别出主节点并连接。
配置项:
poolTimeout,对于主备服务类型,可以配置JDBC连接主库的超时时间。单位为秒,默认300s。
|
|
standby
|
表示一主多备连接,JDBC将自动识别出备节点并连接。
|
|
loadBalance
|
表示负载均衡连接,JDBC将自动识别出连接数最少的节点并连接。
|
|
primaryLoadBalance
|
表示主节点负载均衡连接,JDBC将自动识别出连接数最少的主节点并连接。
|
|
standbyLoadBalance
|
表示备节点负载均衡连接,JDBC将自动识别出连接数最少的备节点并连接。
|
2.4 透明应用程序故障转移——TAF(Transparent Application Failover)
TAF是一项客户端功能,能最大程度地减少数据库连接因实例或网络故障而失败时对最终用户应用程序的中断。
|
参数名称
|
参数说明
|
|
failover
|
是否开启TAF,参数值为ON或OFF,默认为OFF。
* ON:表示开启TAF,当故障发生时应用程序会自动切换到已配置的其他数据库节点。
* OFF:表示关闭TAF,且暂不支持在OFF状态下自动重连当前数据库节点。
|
|
failoverType
|
故障转移的类型,支持NONE、SESSION和SELECT,默认为NONE。
* NONE:表示不使用故障转移。
* SESSION:表示发生故障后重连并重置Statement句柄。
* SELECT:表示在SESSION类型的基础上,切换连接后重新执行因故障而执行出错的SELECT语句(此时,SELECT语句不能为带LOB类型的绑定参数的查询语句),但不支持再对切换前已获取的对象进行二次操作。
|
|
failoverMethod
|
故障转移的方式,该值将决定从主节点到备节点的故障转移速度。
* BASIC:表示在故障转移时再建立连接。
|
|
failoverRetries
|
重试次数,默认值为5。
|
|
failoverDelay
|
重试间隔时间(单位:秒),默认值为1。
|
3.编写测试代码
使用在第二节中创建的java项目,在Main.java中添加如下内容,需要自行修改的内容包括ip,port,username和password。
|
java
package com.wyc;
import java.sql.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static java.lang.Thread.sleep;
public class Main {
private static final String DRIVER = "com.yashandb.jdbc.Driver";
private static final String URL = "jdbc:yasdb:primary://192.168.33.136:1688,192.168.33.145:1688,192.168.33.146:1688/yashan?\n" +
"connectTimeout=60&\n" +
"socketTimeout=120&\n" +
"loginTimeout=60&\n" +
"poolTimeout=180&\n" +
"failover=on&\n" +
"failoverType=session&\n" +
"failoverMethod=basic&\n" +
"failoverRetries=5&\n" +
"failoverDelay=1\n";
private static final String USER = "sales";
private static final String PASS = "sales";
public static void main(String[] args) {
try (Connection conn = getConnection()) {
// 开启事务管理
conn.setAutoCommit(false);
executeDatabaseOperations(conn);
} catch (Exception e) {
e.printStackTrace();
}
}
|
|
Java
public static void executeDatabaseOperations(Connection conn) throws SQLException {
Savepoint firstBatchSavepoint = null;
Savepoint secondBatchSavepoint = null;
try {
// 1. 创建表和存储过程
createCustomerTable(conn);
createStoredProcedure(conn);
// 2. 第一次批量插入(设置保存点)
firstBatchSavepoint = conn.setSavepoint("BATCH_INSERT_1");
System.out.println("保存点BATCH_INSERT_1设置成功");
batchInsertCustomers(conn, Arrays.asList(
new Customer(1, "Alice"),
new Customer(2, "Bob"),
new Customer(3, "Charlie")));
// 3. 查询验证
System.out.println("\n插入后数据:");
queryCustomers(conn).forEach(System.out::println);
System.out.println("\n调用存储过程查询ID=2的客户名称:");
callStoredProcedure(conn, 2);
// 4. 更新数据
updateCustomerName(conn, 2, "Robert");
// 5. 删除数据
deleteCustomer(conn, 3);
// 6. 第二次批量插入(设置新保存点)
secondBatchSavepoint = conn.setSavepoint("BATCH_INSERT_2");
System.out.println("\n保存点BATCH_INSERT_2设置成功");
batchInsertCustomers(conn, Arrays.asList(
new Customer(4, "David"),
new Customer(5, "Eira"),
new Customer(6, "Frank")));
// 提交事务
conn.commit();
System.out.println("\n事务提交成功");
} catch (SQLException e) {
// 事务回滚策略
if ("The transaction must be rerun.".equals(e.getMessage())) {// 出现该异常信息说明taf已经重连成功,此时可以选择重新执行整个事务
conn.rollback();
executeDatabaseOperations(conn);
System.err.println("事务重新执行成功");
}else if (secondBatchSavepoint != null) {
conn.rollback(secondBatchSavepoint); // 回滚到第二个保存点
System.err.println("已回滚到BATCH_INSERT_2保存点");
} else if (firstBatchSavepoint != null) {
conn.rollback(firstBatchSavepoint); // 回滚到第一个保存点
System.err.println("已回滚到BATCH_INSERT_1保存点");
} else {
conn.rollback(); // 完全回滚
}
System.err.println("事务回滚: " + e.getMessage());
}
// 最终查询(事务外)
System.out.println("\n最终查询的数据数据:");
queryCustomers(conn).forEach(System.out::println);
}
/**
* 获取数据库连接
* @return
* @throws ClassNotFoundException
* @throws SQLException
*/
public static Connection getConnection() throws ClassNotFoundException,SQLException{
Class.forName(DRIVER);
return DriverManager.getConnection(URL, USER, PASS);
}
/**
* 创建表
* @param conn
* @throws SQLException
*/
public static void createCustomerTable(Connection conn) throws SQLException {
String sql = "CREATE TABLE IF NOT EXISTS customer (" +
"id INT PRIMARY KEY, " +
"name VARCHAR(32) NOT NULL)";
try (Statement stmt = conn.createStatement()) {
stmt.execute(sql);
}
}
|
|
Java
/**
* 批量插入
* @param conn
* @param customers
* @throws SQLException
*/
public static void batchInsertCustomers(Connection conn, List<Customer> customers) throws SQLException {
String sql = "INSERT INTO customer (id, name) VALUES (?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
for (Customer c : customers) {
if(c.getId() <= 0) throw new SQLException("无效ID: "+c.getId());
pstmt.setInt(1, c.getId());
pstmt.setString(2, c.getName());
pstmt.addBatch();
}
int[] batchResults = pstmt.executeBatch();
for (int i=0; i<batchResults.length; i++) {
if(batchResults[i] == Statement.EXECUTE_FAILED) {
throw new SQLException("第"+(i+1)+"条记录插入失败");
}
}
}
}
/**
*查询所有客户
* @param conn
* @return
* @throws SQLException
*/
public static List<Customer> queryCustomers(Connection conn) throws SQLException {
List<Customer> list = new ArrayList<>();
String sql = "SELECT * FROM customer";
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
while (rs.next()) {
list.add(new Customer(
rs.getInt("id"),
rs.getString("name")
));
}
}
return list;
}
/**
* 更新客户名称
* @param conn
* @param id
* @param newName
* @throws SQLException
*/
public static void updateCustomerName(Connection conn, int id, String newName) throws SQLException {
String sql = "UPDATE customer SET name = ? WHERE id = ?";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, newName);
pstmt.setInt(2, id);
int rows = pstmt.executeUpdate();
System.out.println("\n更新影响行数:" + rows);
}
}
/**
* 删除客户
* @param conn
* @param id
* @throws SQLException
*/
public static void deleteCustomer(Connection conn, int id) throws SQLException {
String sql = "DELETE FROM customer WHERE id = ?";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, id);
int rows = pstmt.executeUpdate();
System.out.println("\n删除影响行数:" + rows);
}
}
|
|
Java
/**
* 创建一个存储过程,输入客户ID,输出客户名称
*/
public static void createStoredProcedure(Connection conn) throws SQLException {
String dropSql = "DROP PROCEDURE IF EXISTS getCustomerNameById";
String createSql =
"CREATE PROCEDURE getCustomerNameById(custId IN INT, custName OUT VARCHAR) AS " +
"BEGIN " +
" SELECT name INTO custName FROM customer WHERE id = custId; " +
"END;";
try (Statement stmt = conn.createStatement()) {
stmt.execute(dropSql);
stmt.execute(createSql);
System.out.println("存储过程getCustomerNameById创建成功");
}
}
/**
* 调用存储过程 getCustomerNameById,传入客户ID,获取客户名称
*/
public static void callStoredProcedure(Connection conn, int customerId) throws SQLException {
String callSql = "{CALL getCustomerNameById(?, ?)}";
try (CallableStatement cstmt = conn.prepareCall(callSql)) {
cstmt.setInt(1, customerId);
cstmt.registerOutParameter(2, Types.VARCHAR);
cstmt.execute();
String name = cstmt.getString(2);
System.out.println("客户ID=" + customerId + " 的名称是:" + name);
}
}
// 实体类
static class Customer {
private int id;
private String name;
public Customer(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Customer [id=" + id + ", name=" + name + "]";
}
}
}
|
4.运行测试代码
在开发工具中会正常输出如下结果:

也可以通过YDC工具查询当前数据库中的数据,查询结果如下:

附录 参考链接
1.YashanDB官方文档:https://doc.yashandb.com/yashandb/23.4/zh/Development-Guide/JDBC-Driver/00JDBC-Driver.html
2.YashanDB软件下载中心:https://download.yashandb.com/download
3.JDK下载地址:https://www.oracle.com/java/technologies/downloads/
4.JDBC 接口文档:https://docs.oracle.com/javase/8/docs/api/java/sql/package-summary.html