【openGauss】如何将数据库中的自定义复合类型绑定到java的class中

news/2024/11/2 7:34:42/

前言

某客户,将应用的数据库从oracle迁移到openGauss系,源库中使用了大量的自定义type,所以java代码里也有很多对type对象的处理。在oracle中可以直接传对象给数据库,但是openGauss/postgresql的源码用例中,都是通过PGobject.setValue,把对象中的所有值拼成一个字符串,用逗号分隔,用括号括起来。如果对象中的某个值可能存在有逗号,那么就会出现异常。

我以前没正经写过java,只能看懂个大概意思,当然,依葫芦画瓢也没啥问题。现在有chatGPT,只要思路清晰,应该也能写出来个解决方案。

问题表现

在源码中 ,有如何使用自定义复合类型的示例
\openGauss-connector-jdbc\pgjdbc\src\test\java\org\postgresql\test\jdbc3\CompositeTest.java

@Beforepublic void setUp() throws Exception {_conn = TestUtil.openDB();TestUtil.createSchema(_conn, "\"Composites\"");TestUtil.createCompositeType(_conn, "simplecompositetest", "i int, d decimal, u uuid");TestUtil.createCompositeType(_conn, "nestedcompositetest", "t text, s simplecompositetest");TestUtil.createCompositeType(_conn, "\"Composites\".\"ComplexCompositeTest\"","l bigint[], n nestedcompositetest[], s simplecompositetest");TestUtil.createTable(_conn, "compositetabletest","s simplecompositetest, cc \"Composites\".\"ComplexCompositeTest\"[]");TestUtil.createTable(_conn, "\"Composites\".\"Table\"","s simplecompositetest, cc \"Composites\".\"ComplexCompositeTest\"[]");}@Testpublic void testSimpleArgumentSelect() throws SQLException {PreparedStatement pstmt = _conn.prepareStatement("SELECT ?");PGobject pgo = new PGobject();pgo.setType("simplecompositetest");pgo.setValue("(1,2.2,)");pstmt.setObject(1, pgo);ResultSet rs = pstmt.executeQuery();assertTrue(rs.next());PGobject pgo2 = (PGobject) rs.getObject(1);assertEquals(pgo, pgo2);}

这里关键的代码在下面这4行

PGobject pgo = new PGobject();  // new一个PGobject()对象pgo.setType("simplecompositetest");  //将这个对象设置成自定义类型pgo.setValue("(1,2.2,)");  //设置这个对象的值pstmt.setObject(1, pgo); //把这个对象绑定到sql的变量里

这里其实就有点难受了,simplecompositetest这个类型在数据库中已经定义,而且真实的业务代码中,java里往往会建一个对应的class,数据应该在这个class的对象里,而此处却需要将业务对象转换成字符串,再塞到PGobject对象里后,才能绑定变量到sql。

ORACLE怎么做的

You: 
在oracle里创建自定义类型,然后在java里怎么使用这个自定义类型进行绑定变量的sql执行?Bot:Oracle里创建自定义类型,你可以使用CREATE TYPE语句,例如:CREATE TYPE person_t AS OBJECT (name VARCHAR2(50),age NUMBER
);
这样就创建了一个名为person_t的自定义类型,它有两个属性:name和age。在Java里使用这个自定义类型进行绑定变量的SQL执行,你可以使用OraclePreparedStatement和它的方法setObjectAtName(),例如://假设你已经建立了一个数据库连接conn
String sql = "INSERT INTO person VALUES (:p)";
OraclePreparedStatement stmt = (OraclePreparedStatement) conn.prepareStatement(sql);
//创建一个person_t类型的对象
Object[] person = new Object[2];
person[0] = "张三";
person[1] = 25;
//将对象绑定到SQL语句中的变量p
stmt.setObjectAtName("p", person);
//执行SQL语句
stmt.executeUpdate();
这样就可以在Java里使用自定义类型进行绑定变量的SQL执行了。^1^ ^2^ ^3^希望这些信息对你有用。👍

oracle 里new的这个object,可以直接绑定到sql的变量里

openGauss里怎么处理?

由于是连续对话,代码太长了,所以这里只截取关键的

You: 
我想定义一个class,能和数据库中的自定义类型ty_test对应上,
因为setValue只能接受逗号分隔的字符串输入,如果某个值本身可能
存在逗号或其他特殊符号,就会执行失败 ,ty_test的代码为 
create type ty_test as (col1 text, col2 text, col3 int);Bot:
我理解您的需求。如果您想定义一个class,能和数据库中的自定义类型ty_test对应上,
您可以使用org.opengauss.util.PGobject类的子类,并重写它的setValue()getValue()方法,以实现您自己的逻辑。例如:// Assume we have a custom composite type called ty_test with three fields col1, col2 and col3
public class TY_TEST extends PGobject {private String col1;private String col2;private int col3;public TY_TEST() {setType("ty_test"); // Set the type name in the database}public TY_TEST(String col1, String col2, int col3) {this();this.col1 = col1;this.col2 = col2;this.col3 = col3;}@Overridepublic void setValue(String value) throws SQLException {// Parse the string value and assign to the fields// You can use your own logic to handle special characters// For example, you can use quotation marks to enclose the valuesif (value != null && value.startsWith("(") && value.endsWith(")")) {value = value.substring(1, value.length() - 1); // Remove the parenthesesString[] parts = value.split(","); // Split by commaif (parts.length == 3) {this.col1 = parts[0].replaceAll("\"", ""); // Remove the quotation marksthis.col2 = parts[1].replaceAll("\"", "");this.col3 = Integer.parseInt(parts[2]);} else {throw new SQLException("Invalid value for ty_test: " + value);}} else {throw new SQLException("Invalid value for ty_test: " + value);}}@Overridepublic String getValue() {// Return a string representation of the object// You can use your own logic to handle special characters// For example, you can use quotation marks to enclose the valuesreturn "(" + "\"" + col1 + "\"" + "," + "\"" + col2 + "\"" + "," + col3 + ")";}
}// Then you can use the TY_TEST class as the parameter
TY_TEST values = new TY_TEST("abc", "def", 1);
pstmt.setObject(1, values);
希望这些信息对您有所帮助。

这段代码的几个关键点

  1. extends PGobject
  2. setType(“ty_test”);
  3. this();
  4. getValue()

我测了下,其实setValue可以去掉,因为本身就是用的java的类,不需要用字符串去传,所以整理好可以执行的代码如下

import java.sql.*;
import org.opengauss.util.PGobject;//create type ty_test as (col1 text, col2 text, col3 int);class TY_TEST extends PGobject{private String col1;private  String col2;private  int col3;public TY_TEST() {setType("ty_test"); }public TY_TEST(String i_col1,String i_col2, int i_col3){this();this.col1=i_col1;this.col2=i_col2;this.col3=i_col3;}@Overridepublic String getValue() {return "(" + "\"" + col1 + "\"" + "," + "\"" + col2 + "\"" + "," + col3 + ")";}
}public class test_type4 {static final String jdbcString = "org.opengauss.Driver";static final String urlString = "jdbc:opengauss://192.168.1.115:26000/postgres?batchMode=off";static final String userName = "xxxxxxxx";static final String password = "xxxxxxxx";/*** @param args* @throws Exception*/
public static void main(String[] args) throws SQLException {Connection _conn = DriverManager.getConnection(urlString, userName, password);PreparedStatement pstmt = _conn.prepareStatement("SELECT ?");//PGobject pgo = new PGobject();//pgo.setType("ty_test");//pgo.setValue("(abc,def,1)");TY_TEST values = new TY_TEST("abc","de,f",1);pstmt.setObject(1, values);ResultSet rs = pstmt.executeQuery();rs.next();System.out.println(rs.getString(1));rs.close();pstmt.close();_conn.close();
}
}

执行执行输出为

(abc,“de,f”,1)

这样就不需要再去转换了。但是,实际上这个原理,还是转成了字符串,只是在字符类型的值上加了双引号。所以就算不扩展PGobject,也可以写段代码转换成字符串,比如

  @Override public String toString(){return "(\""+col1+"\",\""+col2+"\",\""+col3+"\")";}
  PGobject pgo = new PGobject();pgo.setType("ty_test");TY_TEST values = new TY_TEST("abc","d,ef",1);pgo.setValue(values.toString());pstmt.setObject(1, pgo);

不过这样就多了几行代码,而且setType可能都会重复出现,还是扩展PGobject更干净。

其他问题

目前已知这个方案,其实就是加双引号再拼字符串,那么如果值里本身就有双引号,该如何处理呢?
当然,这里最简单的就是给双引号加转义了

import java.sql.*;
import org.opengauss.util.PGobject;//create type ty_test as (col1 text, col2 text, col3 int);class TY_TEST extends PGobject{private String col1;private  String col2;private  int col3;public TY_TEST() {setType("ty_test"); }public TY_TEST(String i_col1,String i_col2, int i_col3){this();this.col1=i_col1;this.col2=i_col2;this.col3=i_col3;}@Overridepublic String getValue() {return "(" + "\"" + col1.replaceAll("\"", "\"\"") + "\"" + "," + "\"" + col2.replaceAll("\"", "\"\"") + "\"" + "," + col3 + ")";}
}public class test_type4 {static final String jdbcString = "org.opengauss.Driver";static final String urlString = "jdbc:opengauss://192.168.1.115:26000/postgres?batchMode=off";static final String userName = "xxxxxxxx";static final String password = "xxxxxxxx";/*** @param args* @throws Exception*/
public static void main(String[] args) throws SQLException {Connection _conn = DriverManager.getConnection(urlString, userName, password);PreparedStatement pstmt = _conn.prepareStatement("SELECT ?");TY_TEST values = new TY_TEST("abc","de,\"f",1);pstmt.setObject(1, values);ResultSet rs = pstmt.executeQuery();rs.next();System.out.println(rs.getString(1));rs.close();pstmt.close();PreparedStatement pstmt2 = _conn.prepareStatement("insert into t_ty_test values (?) "); //create table t_ty_test(a ty_test);pstmt2.setObject(1, values);int rs2 = pstmt2.executeUpdate();pstmt2.close();_conn.close();
}
}

由于java打印出来的这个值长这样

(abc,“de,”“f”,1)

所以我建了个表,把值插进去看到底是什么样子

MogDB=> select a, (a).col2 from t_ty_test ;a         | col2
------------------+-------(abc,"de,""f",1) | de,"f
(1 row)

看上去没有问题,显示两个双引号是自动转义出来的。
于是,问题解决,分隔符和字符串标记符都能自动处理了。

虽然但是,还是不得劲

在oracle里代码那么少,openGauss里要实现同样的效果代码量翻了几倍,虽然这些代码规律性很强,可以写个框架自动生成,但是应该有办法通过修改jdbc驱动,实现和oracle相同的用法。
这个课题就抛给感兴趣的人去试试了。

  • 本文作者: DarkAthena
  • 本文链接: https://www.darkathena.top/archives/opengauss-complextype-java-classopengauss
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!

http://www.ppmy.cn/news/81842.html

相关文章

LeetCode 198 打家劫舍

题目: 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。给定一个代表每个…

luatOS网站 lua语言学习 练习题

lua 教程跳转链接&#xff0c;练习题都来自这里 逻辑运算 检验大小&#xff08;自测题&#xff09; 题目&#xff1a;如果已知number变量n&#xff0c;那么如果需要判断n是否符合下面的条件&#xff1a; 3<n≤10 以下四行判断代码&#xff0c;正确的是&#xff1f; &a…

Python+Yolov5果树上的水果(苹果)检测识别

程序示例精选 PythonYolov5果树上的水果(苹果)检测识别 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对<<PythonYolov5果树上的水果(苹果)检测识别>>编写代码&#xff0c;代码整洁…

oracle如何写一个带参数的视图

--创建参数包 create or replace package view_risk is function set_depcode(depcode varchar2) return varchar2; function get_depcode return varchar2; function set_dmonth(dmonth varchar2) return varchar2; function get_dmonth return varchar2; end view_r…

SVD求解两组多维点之间的欧式变换矩阵,及halcon代码实现

之前研究了二维点的仿射变换&#xff0c;用解矩阵的方式求解了两组二维点之间的变换矩阵。 学习了下SVD&#xff0c;看到可以用SVD求解两组多维点之间的欧式变换矩阵&#xff0c;当然也是个最优化问题。 这里的变换只有平移和旋转&#xff0c;没有缩放。 一、先说结论&#…

python异常处理名称整理

Python 异常处理 python提供了两个非常重要的功能来处理python程序在运行中出现的异常和错误。你可以使用该功能来调试python程序。BaseException所有异常的基类UnboundLocalError访问未初始化的本地变量SystemExit

RK3568平台开发系列讲解(驱动基础篇)RK平台IR的使用

🚀返回专栏总目录 文章目录 一、红外遥控配置二、内核驱动2.1 DTS 定义键值表2.2 内核用户码和IR键值的获取2.3 编译 IR 驱动进内核2.4 Android 键值映射三、IR 波形沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将对RK IR的使用进行学习。 一、红外遥控配置 …

新的开始(最小生成树+超级源点)

题目 我们建立一个超级源点&#xff0c;每个点都与超级源点相连&#xff0c;他们的权值为在该点建立发电站的费用&#xff0c;剩下的将其余点相连&#xff0c;权值为连接到已经通电的发电站费用&#xff0c;我们跑一遍最小生成树即可将他们全部相连&#xff0c;并且费用最小。 …