从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);