oracle.sql.ArrayDescriptor中的错误?

时间:2015-09-29 16:36:16

标签: java sql oracle jdbc

今天,当数据库更新但Web应用程序未重新启动时,我们的Web应用程序出现问题 经过调查,我们发现在db update期间,一个用户定义的类型被删除并再次创建 类型没有改变 - 它只是被删除并使用与以前相同的sql语句再次创建。

以下代码可以显示此问题 执行第二个预处理语句时,存在java.sql.SQLSyntaxErrorException“ORA-00902:invalid datatype”。

String str = "select * from MYTESTTABLE where test in (select column_value from TABLE (?) tmp)";

OracleConnection conn = null;
Properties connectionProps = new Properties();
connectionProps.put("user", "user");
connectionProps.put("password", "pwd");

conn = (OracleConnection) DriverManager
        .getConnection("conn", connectionProps);

Statement stmt = conn.createStatement();
stmt.execute("drop table mytesttable");
stmt.execute("create table MYTESTTABLE ( test NUMBER(22))");
System.out.println("Table MYTESTTABLE was created");

stmt.execute("drop type my_type");
System.out.println("Type my_type was dropped");
stmt.execute("create type my_type as table of NUMBER(22)");
System.out.println("Type my_type was created");

String[] northEastRegion = { "10022", "02110", "07399" };

oracle.sql.ARRAY arr1 = new oracle.sql.ARRAY(
        oracle.sql.ArrayDescriptor.createDescriptor("MY_TYPE", conn),
        conn, northEastRegion);

PreparedStatement ps = conn.prepareStatement(str);
ps.setArray(1, arr1);
ps.executeQuery();

stmt.execute("drop type my_type");
System.out.println("Type my_type was dropped");
stmt.execute("create type my_type as table of NUMBER(22)");
System.out.println("Type my_type was created");

oracle.sql.ARRAY arr2 = new oracle.sql.ARRAY(
        oracle.sql.ArrayDescriptor.createDescriptor("MY_TYPE", conn),
        conn, northEastRegion);

ps = conn.prepareStatement(str);
ps.setArray(1, arr2);
ps.executeQuery(); //java.sql.SQLSyntaxErrorException: ORA-00902: invalid datatype

为什么将arr1和arr2视为不同的类型?
据我所知,问题是这些类型有不同的OID 但是在java代码中,我只使用名称来处理这些类型。我不使用OID。

如何处理这个问题?
我们是否应该在更新DB后重新启动Web应用程序?即使DB的结构没有改变 或者我们应该在创建MY_TYPE时指定(硬编码)OID吗? 什么是正确的方法?

更新
在我看来,我在oracle.sql.ArrayDescriptor中找到了答案和一个错误 请参阅下面的答案。

2 个答案:

答案 0 :(得分:1)

我认为这里的问题是准备好的语句与数据类型的组合用作行源。 Prepared语句有效地创建和优化执行计划,并在创建执行游标时存储游标的地址,因此下次使用不同的绑定变量执行相同的语句时,只需再次执行现有游标(以避免硬解析和软解析) 。删除对象时,Oracle会使依赖于此对象的所有内容失效,包括游标。如果你发出相同的语句,包括解析(未准备好),它应该产生涉及新数据类型的新执行计划,但是你想要重新执行不再有效的游标。

答案 1 :(得分:1)

oracle.sql.ArrayDescriptor 使用缓存 默认情况下,将从缓存加载相同ArrayDescriptor的第二个实例 它与数据库中的类型不同,因为重新创建了数据库中的类型。

ArrayDescriptor中有一个构造函数,您可以在其中指定是否替换任何缓存的描述符。

static ArrayDescriptor  createDescriptor(java.lang.String name, java.sql.Connection conn, boolean recurse, boolean force)
static ArrayDescriptor createDescriptor(SQLName sqlName, java.sql.Connection conn, boolean recurse, boolean force)  

但是构造函数中有一个错误,第一个参数是String - 在该构造函数的主体中没有使用force参数。
它仅在具有SQLName的构造函数体中使用 您可以看到代码here的代码。

因此,为了解决我们的问题,我们可以使用带有SQLName的构造函数并将force参数设置为true。在这种情况下,将不使用缓存。

String[] northEastRegion = { "10022", "02110", "07399" };
String str = "select * from MYTESTTABLE where test in (select column_value from TABLE (?) tmp)";

OracleConnection conn = null;
Properties connectionProps = new Properties();
connectionProps.put("user", "user");
connectionProps.put("password", "pwd");

conn = (OracleConnection) DriverManager
                .getConnection("connstr", connectionProps);

Statement stmt = conn.createStatement();

stmt.execute("drop table mytesttable");
stmt.execute("create table MYTESTTABLE ( test NUMBER(22))");

stmt.execute("drop type my_type1");
stmt.execute("create type my_type1 as table of NUMBER(22)");

ArrayDescriptor desc1 = oracle.sql.ArrayDescriptor.createDescriptor("MY_TYPE1", conn);

stmt.execute("drop type my_type1");
stmt.execute("create type my_type1 as table of NUMBER(22)");

oracle.sql.SQLName sqlName = new oracle.sql.SQLName("MY_TYPE", conn);
ArrayDescriptor desc2 = oracle.sql.ArrayDescriptor.createDescriptor(sqlName, conn, true, true);
oracle.sql.ARRAY arr2 = new oracle.sql.ARRAY(desc2, conn, northEastRegion);

PreparedStatement ps = conn.prepareStatement(str);
ps.setArray(1, arr2);
ps.executeQuery();