如何解决从JDBC使用Oracle COLLECT聚合函数
我正在尝试使Oracle COLLECT()聚合函数从JDBC开始工作。
我的模式如下
CREATE TABLE t_warehouse (
id NUMBER(5) NOT NULL PRIMARY KEY,warehouse_name VARCHAR2(64) NOT NULL
);
CREATE TABLE t_warehouse_detail (
id NUMBER(5) NOT NULL PRIMARY KEY,warehouse_id NUMBER(5) NOT NULL,detail_value VARCHAR2(128) NOT NULL,CONSTRAINT fk_warehouse
FOREIGN KEY (warehouse_id)
REFERENCES t_warehouse(id)
);
INSERT INTO t_warehouse (id,warehouse_name)
VALUES (1,'warehouse 1');
INSERT INTO t_warehouse_detail (id,warehouse_id,detail_value)
VALUES (1,1,'detail 1');
INSERT INTO t_warehouse_detail (id,detail_value)
VALUES (2,'detail 2');
CREATE OR REPLACE TYPE warehouse_detail_t AS TABLE OF VARCHAR2(128);
我的查询如下
SELECT wh.id,CAST(COLLECT(detail_value) AS warehouse_detail_t) AS "Details"
FROM t_warehouse wh
JOIN t_warehouse_detail whd ON (wh.id = whd.warehouse_id)
GROUP BY wh.id;
我的代码看起来像这样
String url = ...;
String username = ...;
String password = ...;
OracleDataSource ds = new oracle.jdbc.pool.OracleDataSource();
ds.setURL(url);
ds.setUser(username);
ds.setPassword(password);
try (Connection connection = ds.getConnection()) {
connection.setTypeMap(Collections.singletonMap("SCHEMA_NAME.WAREHOUSE_DETAIL_T",DetailValues.class));
try (PreparedStatement preparedStatement = connection.prepareStatement("SELECT wh.id,CAST(COLLECT(detail_value) AS warehouse_detail_t) AS \"Details\""
+ " FROM t_warehouse wh "
+ " JOIN t_warehouse_detail whd ON (wh.id = whd.warehouse_id) "
+ " GROUP BY wh.id");
ResultSet resultSet = preparedStatement.executeQuery()) {
while (resultSet.next()) {
int id = resultSet.getInt("id");
Set<String> detailValues = Collections.emptySet();
}
}
并且我有以下SQLData类
public static final class DetailValues implements SQLData {
private String typeName;
@Override
public String getSQLTypeName() throws SQLException {
return this.typeName;
}
@Override
public void readSQL(SQLInput stream,String typeName) throws SQLException {
this.typeName = typeName;
Array array = stream.readArray();
if (array != null) {
try {
} finally {
array.free();
}
}
System.out.println("readSQL");
}
@Override
public void writeSQL(SQLOutput stream) throws SQLException {
System.out.println("writeSQL");
}
}
我收到以下异常
java.sql.SQLException: Fail to construct descriptor: Unable to resolve type "SCHEMA_NAME.WAREHOUSE_DETAIL_T"
at oracle.sql.TypeDescriptor.getTypeDescriptor(TypeDescriptor.java:1002)
at oracle.jdbc.driver.NamedTypeAccessor.otypeFromName(NamedTypeAccessor.java:82)
at oracle.jdbc.driver.TypeAccessor.initMetadata(TypeAccessor.java:75)
at oracle.jdbc.driver.T4CPreparedStatement.executeForDescribe(T4CPreparedStatement.java:860)
at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:983)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1168)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3666)
at oracle.jdbc.driver.T4CPreparedStatement.executeInternal(T4CPreparedStatement.java:1426)
at oracle.jdbc.driver.OraclePreparedStatement.executeQuery(OraclePreparedStatement.java:3713)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeQuery(OraclePreparedStatementWrapper.java:1167)
at com.acme.CollectTests.collect(CollectTests.java:48)
这与ojdbc8-19.7.0.0和Oracle 19.3一起使用。
在Oracle SQL Developer中,查询也会失败,但有相同的例外。
更新
_g JAR的最佳输出是:
[oracle.jdbc.pool.OracleDataSource oracle.jdbc.pool.OracleDataSource ] Enter: void
[oracle.jdbc.pool.OracleDataSource setConnectionProperty ] 47542153 Enter: "oracle.jdbc.enableACSupport","false"
[oracle.jdbc.pool.OracleDataSource setConnectionProperty ] 47542153 Return: void
[oracle.jdbc.pool.OracleDataSource setConnectionProperty ] 47542153 Exit: [0.370177 ms]
[oracle.jdbc.pool.OracleDataSource processFastConnectionFailoverSysProperty ] 47542153 Enter: void
[oracle.jdbc.pool.OracleDataSource processFastConnectionFailoverSysProperty ] 47542153 Return: void
[oracle.jdbc.pool.OracleDataSource processFastConnectionFailoverSysProperty ] 47542153 Exit: [0.133466 ms]
[oracle.jdbc.pool.OracleDataSource oracle.jdbc.pool.OracleDataSource ] Return: void
[oracle.jdbc.pool.OracleDataSource oracle.jdbc.pool.OracleDataSource ] Exit: [0.386721 ms]
[oracle.jdbc.pool.OracleDataSource setURL ] 47542153 Enter: "jdbc:oracle:thin:@localhost:1521/ORCLPDB1?oracle.net.disableOob=true"
[oracle.jdbc.pool.OracleDataSource setURL ] 47542153 Return: void
[oracle.jdbc.pool.OracleDataSource setURL ] 47542153 Exit: [0.13673 ms]
[oracle.jdbc.pool.OracleDataSource setUser ] 47542153 Enter: "schema_name"
[oracle.jdbc.pool.OracleDataSource setUser ] 47542153 Return: void
[oracle.jdbc.pool.OracleDataSource setUser ] 47542153 Exit: [0.121062 ms]
[oracle.jdbc.pool.OracleDataSource setPassword ] 47542153 Enter: "*****"
[oracle.jdbc.pool.OracleDataSource setPassword ] 47542153 Enter: oracle.jdbc.internal.OpaqueString@67d0ff08
[oracle.jdbc.pool.OracleDataSource setPassword ] 47542153 Return: void
[oracle.jdbc.pool.OracleDataSource setPassword ] 47542153 Exit: [0.128787 ms]
[oracle.jdbc.pool.OracleDataSource setPassword ] 47542153 Return: void
[oracle.jdbc.pool.OracleDataSource setPassword ] 47542153 Exit: [0.379598 ms]
[oracle.jdbc.pool.OracleDataSource getConnection ] 47542153 Enter: void
[oracle.jdbc.pool.OracleDataSource getConnection ] 47542153 Enter: "schema_name","*****"
[oracle.jdbc.pool.OracleDataSource getConnection ] OracleDataSource.getConnection(user,passwd): URL isjdbc:oracle:thin:@localhost:1521/ORCLPDB1?oracle.net.disableOob=true
[oracle.jdbc.pool.OracleDataSource createConnectionBuilder ] 47542153 Enter: void
[oracle.jdbc.pool.OracleDataSource createConnectionBuilder ] 47542153 Return: oracle.jdbc.pool.OracleDataSource$1@5e316c74
[oracle.jdbc.pool.OracleDataSource createConnectionBuilder ] 47542153 Exit: [2.499096 ms]
[oracle.jdbc.pool.OracleDataSource getConnection ] 47542153 Enter: oracle.jdbc.pool.OracleDataSource$1@5e316c74
[oracle.jdbc.pool.OracleDataSource makeURL ] 47542153 Enter: void
[oracle.jdbc.pool.OracleDataSource makeURL ] 47542153 Return: void
[oracle.jdbc.pool.OracleDataSource makeURL ] 47542153 Exit: [0.10966 ms]
[oracle.jdbc.pool.OracleDataSource getPhysicalConnection ] 47542153 Enter: {password=redacted,oracle.jdbc.enableACSupport=false,connection_url=jdbc:oracle:thin:@localhost:1521/ORCLPDB1?oracle.net.disableOob=true,user=schema_name},null
[oracle.jdbc.pool.OracleDataSource getPhysicalConnection ] OracleDataSource.getPhysicalConnection(prop): URL isjdbc:oracle:thin:@localhost:1521/ORCLPDB1?oracle.net.disableOob=true,user: schema_name,password: *****
[oracle.jdbc.driver.HAManager oracle.jdbc.driver.HAManager ] Enter: void
[oracle.jdbc.driver.HAManager oracle.jdbc.driver.HAManager ] Return: void
[oracle.jdbc.driver.HAManager oracle.jdbc.driver.HAManager ] Exit: [0.675662 ms]
[oracle.jdbc.driver.NoSupportHAManager oracle.jdbc.driver.NoSupportHAManager] Enter: void
[oracle.jdbc.driver.NoSupportHAManager oracle.jdbc.driver.NoSupportHAManager] Return: void
[oracle.jdbc.driver.NoSupportHAManager oracle.jdbc.driver.NoSupportHAManager] Exit: [0.101079 ms]
[oracle.jdbc.driver.NoSupportHAManager getInstance ] Enter: void
[oracle.jdbc.driver.NoSupportHAManager getInstance ] Return: oracle.jdbc.driver.NoSupportHAManager@bccb269
[oracle.jdbc.driver.NoSupportHAManager getInstance ] Exit: [0.104732 ms]
[oracle.jdbc.driver.HAManager enableHAIfNecessary ] Enter: "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=ORCLPDB1)))",oracle.jdbc.driver.T4CConnection@3c0be339
[oracle.jdbc.driver.HAManager enableHAIfNecessary ] ========= simplefan.jar or ons.jar is not on the classpath,HA is disabled,java.lang.ClassNotFoundException: oracle.simplefan.FanManager
[oracle.jdbc.driver.HAManager enableHAIfNecessary ] ========= HA is explicitly disabled
[oracle.jdbc.driver.HAManager enableHAIfNecessary ] Return: void
[oracle.jdbc.driver.HAManager enableHAIfNecessary ] Exit: [0.660915 ms]
[oracle.jdbc.pool.OracleDataSource getPhysicalConnection ] 47542153 Return: oracle.jdbc.driver.T4CConnection@3c0be339
[oracle.jdbc.pool.OracleDataSource getPhysicalConnection ] 47542153 Exit: [0.946502 ms]
[oracle.jdbc.pool.OracleDataSource getConnection ] 47542153 Return: oracle.jdbc.driver.T4CConnection@3c0be339
[oracle.jdbc.pool.OracleDataSource getConnection ] 47542153 Exit: [1.244383 ms]
[oracle.jdbc.pool.OracleDataSource getConnection ] 47542153 Return: oracle.jdbc.driver.T4CConnection@3c0be339
[oracle.jdbc.pool.OracleDataSource getConnection ] 47542153 Exit: [1.471857 ms]
[oracle.jdbc.pool.OracleDataSource getConnection ] 47542153 Return: oracle.jdbc.driver.T4CConnection@3c0be339
[oracle.jdbc.pool.OracleDataSource getConnection ] 47542153 Exit: [1.732571 ms]
[oracle.jdbc.driver.NoSupportHAManager dropConnection ] BCCB269 Enter: oracle.jdbc.driver.T4CConnection@3c0be339
[oracle.jdbc.driver.NoSupportHAManager dropConnection ] NoSupportHAManager.dropConnection()
[oracle.jdbc.driver.NoSupportHAManager dropConnection ] BCCB269 Return: void
[oracle.jdbc.driver.NoSupportHAManager dropConnection ] BCCB269 Exit: [0.180062 ms]
Exception in thread "main" java.sql.SQLException: Fail to construct descriptor: Unable to resolve type "SCHEMA_NAME.WAREHOUSE_DETAIL_T"
at oracle.sql.TypeDescriptor.getTypeDescriptor(TypeDescriptor.java:998)
at oracle.jdbc.driver.NamedTypeAccessor.otypeFromName(NamedTypeAccessor.java:78)
at oracle.jdbc.driver.TypeAccessor.initMetadata(TypeAccessor.java:71)
at oracle.jdbc.driver.T4CPreparedStatement.executeForDescribe(T4CPreparedStatement.java:856)
at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:979)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1164)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3662)
at oracle.jdbc.driver.T4CPreparedStatement.executeInternal(T4CPreparedStatement.java:1422)
at oracle.jdbc.driver.OraclePreparedStatement.executeQuery(OraclePreparedStatement.java:3709)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeQuery(OraclePreparedStatementWrapper.java:1163)
解决方法
您的代码可以在SQL中完美运行。也许,我认为您的代码中缺少的唯一想法是该类型末尾的斜杠。请记住,类型与过程,包或函数相同,您需要/
来创建对象
SQL> CREATE TABLE t_warehouse (
id NUMBER(5) NOT NULL PRIMARY KEY,warehouse_name VARCHAR2(64) NOT NULL
); 2 3 4
Table created.
SQL> CREATE TABLE t_warehouse_detail (
id NUMBER(5) NOT NULL PRIMARY KEY,warehouse_id NUMBER(5) NOT NULL,detail_value VARCHAR2(128) NOT NULL,2 CONSTRAINT fk_warehouse
FOREIGN KEY (ware 3 house_id)
REFERENCES t_warehouse(id)
4 5 6 7 8 );
Table created.
SQL> INSERT INTO t_warehouse (id,warehouse_name)
2 VALUES (1,'warehouse 1');
INSERT INTO t_warehouse_detail (id,warehouse_id,detail_value)
VALUES (1,1,'detail 1');
INSERT INTO t_warehouse_detail (id,detail_value)
VALUES (2,'detail 2');
1 row created.
SQL> SQL> 2
1 row created.
SQL> SQL> 2
1 row created.
SQL> commit ;
Commit complete.
SQL> CREATE OR REPLACE TYPE warehouse_detail_t AS TABLE OF VARCHAR2(128);
2
3 /
Type created.
SQL> SELECT wh.id,CAST(COLLECT(detail_value) AS warehouse_detail_t) AS "Details"
FROM t_warehouse wh
JOIN t_warehouse_detail whd ON (wh.id = whd.warehouse_id)
GROUP BY wh.id; 2 3 4
ID
----------
Details
--------------------------------------------------------------------------------
1
WAREHOUSE_DETAIL_T('detail 1','detail 2')
,
问题在于在数据类中实现SQLData。 Oracles JDBC Developer's Guide声明以下内容:
用于集合的定制Java类称为定制集合类。定制集合类必须实现Oracle oracle.jdbc.OracleData接口。另外,定制类或伴随类必须实现oracle.jdbc.OracleDataFactory。 标准的java.sql.SQLData接口仅用于映射SQL对象类型。
一个工作示例:
public static final class DetailValues implements OracleData,OracleDataFactory {
private List<String> details;
@Override
public Object toJDBCObject(Connection connection) throws SQLException {
// TODO implement when type also used in PreparedStatement.setObject
return null;
}
@Override
public OracleData create(Object o,int i) throws SQLException {
String[] details = (String[]) ((OracleArray) o).getArray();
this.details = List.of(details);
return this;
}
}
,然后在遍历ResultSet的示例中使用ResultSet.getObject():
DetailValues detailValues = resultSet.getObject("details",DetailValues.class);
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。