基于JDBC
JDBC
环境要求
安装环境对应版本的的 java:yum install java
#以JDK8为例
[antdb@host227 ~]$ cat /etc/redhat-release
CentOS Linux release 7.5.1804 (Core)
[antdb@host227 ~]$ java -version
openjdk version "1.8.0_222"
OpenJDK Runtime Environment (build 1.8.0_222-b10)
OpenJDK 64-Bit Server VM (build 25.222-b10, mixed mode)
数据库访问权限配置
集中式环境通过修改 pg_hba.conf 文件配置数据库访问权限;
分布式环境通过 MGR 的 ADD HBA 命令配置数据库访问权限。
驱动获取
AntDB 提供的 JDBC 的驱动的下载地址为:AntDB 的 tar 包安装完成后,数据库程序目录(二进制目录)下应该的 client_driver 文件中:
[antdb@localhost]$ cd /home/antdb/app/client_driver
[antdb@localhost client_driver]$ ll
总用量 0
drwxr-xr-x 2 antdb antdb 146 2月 28 10:17 adbjre
drwxr-xr-x 3 antdb antdb 17 2月 28 10:17 adbodbc
drwxr-xr-x 3 antdb antdb 17 2月 28 10:17 psycopg2
drwxr-xr-x 6 antdb antdb 56 2月 28 10:17 unixODBC
# 其中 adbjre 目录下有最新的JDBC驱动:
[antdb@localhost]$ cd /home/antdb/app/client_driver/adbjre
[antdb@localhost adbjre]$ ll
总用量 4200
-rwxr-xr-x 1 antdb antdb 847827 2月 28 10:17 AntDB-1.0.4.jre6.jar
-rwxr-xr-x 1 antdb antdb 854187 2月 28 10:17 AntDB-1.0.4.jre7.jar
-rwxr-xr-x 1 antdb antdb 876188 2月 28 10:17 AntDB-1.0.4.jre8.jar
-rwxr-xr-x 1 antdb antdb 847676 2月 28 10:17 AntDB-2.0.4.jre7.jar
-rwxr-xr-x 1 antdb antdb 870071 2月 28 10:17 AntDB-2.0.4.jre8.jar
JDBC url 连接串
AntDB_jdbc 提供了以下几个 url 连接串,用于连接 AntDB 数据库:
不指定grammar,默认grammar为postgres
jdbc:antdb://${ip}:${port}/${dbname}
指定grammar
jdbc:antdb://${ip}:${port}/${dbname}?grammar=postgres
jdbc:antdb://${ip}:${port}/${dbname}?grammar=oracle
也可对接 pg 数据库:
jdbc:antdb://${ip}:${port}/${dbname}
通过 JDBC 访问数据库的例子
数据库驱动类名
net.antdb.Driver
创建测试用户和测试数据库:
连接到数据库
antdb=# CREATE USER jdbc_usr WITH ENCRYPTED PASSWORD 'jdbc@123' superuser;
CREATE ROLE
antdb=# CREATE DATABASE jdbc_db OWNER jdbc_usr;
CREATE DATABASE
测试程序代码:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class insert_conn {
//创建数据库连接。
public static void main(String[] args) throws SQLException,ClassNotFoundException {
String url = "jdbc:antdb://10.20.16.227:9876/postgres";
String user = "antdb";
String password = "xxxx";
Connection conn = null;
try {
System.out.println("connect start");
//加载数据库驱动。
Class.forName("net.antdb.Driver");
//创建数据库连接。
conn = DriverManager.getConnection(url, user, password);
System.out.println("connect ok");
/**
* 创建表t1
* 向表t1中插入10条数据
*/
PreparedStatement psmt = conn.prepareStatement("drop table if exists test cascade");
psmt.execute();
System.out.println("drop ok");
psmt = conn.prepareStatement("create table t1(c1 int not null primary key, c2 text)");
psmt.execute();
System.out.println("create ok");
psmt = conn.prepareStatement("insert into t1 values (1, 'a'),(2,'b'),(3,'c'),(4,'d'),(5,'e'),(6,'f'),(7,'g'),(8,'h'),(9,'i'),(10,'j')");
psmt.execute();
System.out.println("insert ok");
psmt.close();
//关闭数据库连接。
conn.close();
System.out.println("close ok");
}catch (SQLException ex)
{
ex.getStackTrace();
ex.getMessage();
}finally {
if(conn != null) {
conn.close();
}
}
}
}
编译和运行测试程序:
[antdb@host227 test]$ ll
total 916
-rw-r--r-- 1 antdb antdb 931537 Feb 13 16:55 AntDB-2.0.0.jre8.jar
-rw-rw-r-- 1 antdb antdb 1698 Feb 13 17:33 insert_conn.java
[antdb@host227 test]$ javac -cp AntDB-2.0.0.jre8.jar:. insert_conn.java
[antdb@host227 test]$ java -ea -cp AntDB-2.0.0.jre8.jar:. insert_conn
connect start
Feb 13, 2023 5:44:51 PM org.postgresql.jdbc.PgConnection setGrammarOperation
INFO: set to postgres
connect ok
drop ok
create ok
insert ok
close ok
验证程序运行结果:
[antdb@host227 test]$ adb -d postgres -p 9876
psql (13.3)
Type "help" for help.
antdb=# \dt
List of relations
Schema | Name | Type | Owner
--------+------+-------+-------
public | t1 | table | antdb
(1 row)
antdb=# SELECT * FROM t1;
c1 | c2
----+----
1 | a
2 | b
3 | c
4 | d
5 | e
6 | f
7 | g
8 | h
9 | i
10 | j
(10 rows)
查看和修改事务自动提交
在 Java 中可以使用如下语句进行 autocommit 的查看和设置:
System.out.print("default autocommit is "+connection.getAutoCommit()+"\n");
connection.setAutoCommit(false);
System.out.print("current autocommit is "+connection.getAutoCommit()+"\n");
执行后,输出如下:
default autocommit is true
current autocommit is false
在程序中使用的时候,建议设置 autocommit 为 false,手动去控制事务,便于进行业务逻辑的控制。
子事务处理
在数据库事务中,顶层事务控制着各个层次的事务,而嵌套在顶层事务下的事务被称为子事务。子事务控制着每个局部的变化,它可以提交也可以回滚,但其提交操作并不马上生效,只有在其父事务提交后才真正提交。同样,任意一个事务的回滚也会引起它的所有子事务一同回滚。JDBC 支持子事务功能,通过两个设置 autosave 和 cleanupSavepoints 来限制子事务的回滚和提交。autosave 参数控制子事务保存点的自动设置与自动回滚,cleanupSavepoints 参数控制子事务保存点的自动清理。
参数配置
参数组合
-
不指定 autosave 和 cleanupSavepoints,默认 autosave 为 never,cleanupSavepoints 为 false。
-
当 autosave 参数设置为 never ,cleanupSavepoints 参数可以为 ture 和 false 任意状态。
-
当 autosave 参数设置为 always 或 conservative 时,cleanupSavepoints 参数只能设置为 true ,否则通过JDBC与数据库连接建立失败,出现报错:
net.antdb.util.PsOLException: Invalid parameter value, when autosave is enabled, cleanupsavepoints must be true .
-
子事务处理参数配置 url 连接串示例:
不指定autosave和cleanupSavepoints,默认autosave为never,cleanupSavepoints为false jdbc:antdb://${ip}:${port}/${dbname} 指定autosave和cleanupSavepoints(示例) jdbc:antdb://${ip}:${port}/${dbname}?autosave=always&cleanupSavepoints=true
参数说明
参数定义
autosave (string) default never:
指定驱动程序在查询失败时应该采取的行为,不指定该参数时默认autosave = never。
-
autosave = always:每次执行查询之前都会设置一个保存点,并在查询出现异常时自动回滚到该保存点。
-
autosave = never:不设置保存点。执行出现异常时不执行回滚操作。
-
autosave=conservative:仅在特定情况下每次执行查询之前都会设置一个保存点,执行自动回滚。例如,当发生像"缓存的语句不能更改返回类型"或"语句 XXX 无效"这样的错误时才会执行回滚操作。
cleanupSavepoints (boolean) default false:
清理保存点,决定在自动保存模式下创建的 SAVEPOINT 是否在语句之前释放。不指定该参数时默认 cleanupSavepoints = false。
-
cleanupSavepoints = true:在事务提交或回滚后,自动清理先前设置的保存点。
-
cleanupSavepoints = false:不自动清理保存点,保存点将保留在事务结束后。
参数设置建议
-
autosave 参数:建议用户设置为 always。
-
cleanupSavepoints 参数:建议设置为 true。
子事务相关参数使用示例
下述示例验证了autosave = always,cleanupSavepoints = true 时查询失败能够回滚到自动创建的保存点并能继续执行接下来操作,父事务不中断,只回滚该条失败的子事务,并且不影响前面已提交的子事务的场景。
测试程序代码:
import java.sql.*;
public class test {
//定义连接参数。
public static void main(String[] args) throws SQLException {
String url1 = "jdbc:antdb://10.20.16.227:32003/postgres?autosave=always&cleanupSavepoints=true";
//不指定 autosave 和 cleanupSavepoints
String url2 = "jdbc:antdb://10.20.16.227:32003/postgres;
/**
*验证 autosave=always,cleanupSavepoints=true 的情况下,插入数据异常(主键冲突),子事务能回滚到自动创建的保存点并能继续执行子事务操作,事务不中断。
*/
test001(url1);
}
//插入数据异常(主键冲突),子事务执行失败,回滚到查询前自动创建的保存点。
//创建数据库连接。
public static void test001(String url) throws SQLException {
String user = "mengzp";
String password = "xxx";
Connection conn = null;
try {
System.out.println("connect start");
//创建数据库连接。
conn = DriverManager.getConnection(url, user, password);
//关闭自动提交,方便进行子事务测试。
conn.setAutoCommit(false);
System.out.println("connect ok");
/**
* 创建表test(c1字段设置为主键)
* 向表test中插入10条数据
*/
PreparedStatement psmt = conn.prepareStatement("drop table if exists test cascade");
psmt.execute();
System.out.println("drop ok");
psmt = conn.prepareStatement("create table test(c1 int not null primary key, c2 text)");
psmt.execute();
System.out.println("create ok");
psmt = conn.prepareStatement("insert into test values (1, 'a'),(2,'你好'),(3,'c'),(4,'测试'),(5,'e'),(6,'10'),(7,'g'),(8,'写作'),(9,'i'),(10,'j')");
psmt.execute();
System.out.println("insert ok");
//数据插入成功
psmt = conn.prepareStatement("select * from test order by c1");
ResultSet rs = psmt.executeQuery();
while (rs.next()) {
System.out.println("c1="+rs.getInt(1)+";c2="+rs.getString(2));
}
System.out.println("select ok");
try {
//模拟插入数据异常(主键冲突),子事务执行失败,回滚到查询前自动创建的保存点。
psmt = conn.prepareStatement("insert into test values (1, 'a'),(11,'你好'),(12,'c')");
psmt.executeQuery();
} catch (SQLException ex) {
ex.printStackTrace();
System.out.println(ex.getStackTrace());
System.out.println(ex.getMessage());
System.out.println("insert fail");
}
psmt.close();
//提交事务。因为在服务端可以连接数据库,可以查询到test表,test表包含十条数据。
conn.commit();
conn.close();
System.out.println("close ok");
} catch (SQLException ex) {
ex.printStackTrace();
System.out.println(ex.getStackTrace());
System.out.println(ex.getMessage());
} finally {
if (conn != null) {
conn.close();
}
}
}
}
import java.sql.*;
public class test {
//定义连接参数。
public static void main(String[] args) throws SQLException {
String url2 = "jdbc:antdb://10.20.16.227:32003/postgres;
//对比验证:不设置 autosave,cleanupSavepoints 参数默认关闭情况下,插入数据异常(主键冲突),事务中止,提交事务后父事务回滚。
test002(url2);
}
//插入数据异常(主键冲突)导致事务中断,子事务执行失败,回滚整个事务。
//创建数据库连接。
public static void test002(String url) throws SQLException {
String user = "mengzp";
String password = "xxx";
Connection conn = null;
try {
System.out.println("connect start");
//创建数据库连接。
conn = DriverManager.getConnection(url, user, password);
//关闭自动提交,方便进行子事务测试。
conn.setAutoCommit(false);
System.out.println("connect ok");
/**
* 创建表test(c1字段设置为主键)
* 向表test中插入10条数据
*/
PreparedStatement psmt = conn.prepareStatement("drop table if exists t2 cascade");
psmt.execute();
System.out.println("drop ok");
psmt = conn.prepareStatement("create table t2(c1 int not null primary key, c2 text)");
psmt.execute();
System.out.println("create ok");
psmt = conn.prepareStatement("insert into t2 values (1, 'a'),(2,'你好'),(3,'c'),(4,'测试'),(5,'e'),(6,'10'),(7,'g'),(8,'写作'),(9,'i'),(10,'j')");
psmt.execute();
System.out.println("insert ok");
//数据插入成功
psmt = conn.prepareStatement("select * from t2 order by c1");
ResultSet rs = psmt.executeQuery();
while (rs.next()) {
System.out.println("c1="+rs.getInt(1)+";c2="+rs.getString(2));
}
System.out.println("select ok");
try {
//模拟插入数据异常(主键冲突),子事务执行失败,事务中止。
psmt = conn.prepareStatement("insert into t2 values (1, 'a'),(11,'你好'),(12,'c')");
psmt.executeQuery();
} catch (SQLException ex) {
ex.printStackTrace();
System.out.println(ex.getStackTrace());
System.out.println(ex.getMessage());
System.out.println("insert fail");
}
psmt.close();
//提交事务。由于主键冲突导致事务中断,未创建保存点,整个事务回滚,表t2未创建成功,在服务端连接数据库查询不到t2表。
conn.commit();
conn.close();
System.out.println("close ok");
} catch (SQLException ex) {
ex.printStackTrace();
System.out.println(ex.getStackTrace());
System.out.println(ex.getMessage());
} finally {
if (conn != null) {
conn.close();
}
}
}
}
验证程序运行结果:
test001:test 表创建成功,并成功插入十条数据。
test002:t2 表创建失败。
[mengzp@host227 ~]$ adb -d postgres -p 32003
psql (6.3.20 based on PG 13.3)
Type "help" for help.
antdb=# \dt
List of relations
Schema | Name | Type | Owner
--------+------+-------+--------
public | test | table | mengzp
(1 row)
antdb=# select * from test order by c1;
c1 | c2
----+------
1 | a
2 | 你好
3 | c
4 | 测试
5 | e
6 | 10
7 | g
8 | 写作
9 | i
10 | j
(10 rows)