java - 通过反射调用 Oracle JDBCPreparedStatement 方法失败

标签 java oracle jdbc reflection

我正在使用标准 Oracle JDBC 驱动程序(JDK6 瘦驱动程序)并尝试通过反射调用PreparedStatement 上的方法。我可以直接调用该方法,但是当我尝试通过反射调用(我认为是)相同的方法时,我收到一个 IllegalAccessException ,表明我尝试调用的方法没有公共(public)访问权限。检查类返回的 Method 对象,它显示它是一个 publci 方法(即 Modifier=1)。

使用其他 JDBC 驱动程序(IBM 的 DB2、Microsoft 的 SQLServer、PostgreSQL 等)进行的相同测试均按预期工作。

由于此代码最终在 Glassfish 容器中的 JEE 应用程序中使用,因此我使用 DataSource 而不是简单的连接。

这是一个简短的测试程序的片段(删除了敏感项目),说明了问题:-

System.out.println("Creating DataSource");
DataSource ds = new OracleDataSource();
((OracleDataSource) ds).setUser("HINT");
((OracleDataSource) ds).setPassword("hint");
((OracleDataSource) ds).setURL("jdbc:oracle:thin:@server-name:1521:database-name");
Connection dbCon = ds.getConnection();
dbCon.setAutoCommit(true);
System.out.println("Connection obtained : " + dbCon.getClientInfo());
PreparedStatement pstmt = (OraclePreparedStatement)dbCon.prepareStatement("SELECT * FROM \"HINT\".\"COUNTRIES1\" WHERE \"COUNTRY_ID\" = ?");

if (useReflection)  // True=Fails, False=Works!
{
    Class clazz = pstmt.getClass();
    Method method = clazz.getMethod("setString", new Class[]{int.class, String.class});
    System.out.println("Modifiers=" + method.getModifiers());
    System.out.println(method.toGenericString());        
    String value = "EG";
    Object[] pstmtParams = new Object[]{new Integer(1), value};  
    method.invoke(pstmt, pstmtParams);              // <<< Fails here
}
else
{
 pstmt.setString(1, "EG");
}

这是 useReflection 设置为 TRUE 时的输出:-

Creating DataSource
Connection obtained : {}
Modifiers=1
public void oracle.jdbc.driver.OraclePreparedStatementWrapper.setString(int,java.lang.String) throws java.sql.SQLException


java.lang.IllegalAccessException: Class     com.****.dataaccess.TestOracle can not access a member of class oracle.jdbc.driver.OraclePreparedStatementWrapper with modifiers "public"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
at java.lang.reflect.Method.invoke(Method.java:588)
at com.rocketsoftware.ascentserver.dataaccess.TestOracle.testPreparedStatementSetters(TestOracle.java:64)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)

深入研究后发现,驱动程序返回的PreparedStatement对象似乎是一个包装类(OraclePreparedStatementWrapper),它扩展并实现了各种内部类,因此显然它在幕后做了一些聪明的事情。然而我一直认为直接或通过反射调用标记为公共(public)访问的方法应该没有区别。

这里明显有问题,但我就是看不到......

最佳答案

我认为问题在于您正在尝试使用您通过无权访问的类请求的方法。这显然隐式地限制了该方法的可见性,即使该方法本身是公共(public)的。

这是一个类似的示例:

// Foo.java
public interface Foo {
    void method();
}

// FooFactory.java
package foo;

public class FooFactory {
    public static Foo getFoo() {
        return new FooImpl();
    }
}

// FooImpl.java
class FooImpl implements Foo {
   @Override
   public void method() {
       System.out.println("FooImpl.method");
   }
}

// Bar.java
package bar;

import foo.*;
import java.lang.reflect.*;

public class Test {
    public static void main(String[] args) throws Exception {
        Foo foo = FooFactory.getFoo();
        Method method = foo.getClass().getMethod("method");
        method.invoke(foo);
    }
}

这会以与您的代码相同的方式失败:

Exception in thread "main" java.lang.IllegalAccessException:
    Class bar.Test can not access a member of class foo.FooImpl with modifiers "public"
    at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:109)
    at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:261)
    at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:253)
    at java.lang.reflect.Method.invoke(Method.java:599)

如果您通过您可以看到的接口(interface)请求方法,那就没问题:

Method method = Foo.class.getMethod("method");

同样,在您的代码中,您只需更改此行:

Class clazz = pstmt.getClass();

至:

// TODO: Change it to Class<?> to avoid the raw type :)
Class clazz = PreparedStatement.class;

或者只是将其内联到下一个语句中:

Method method = PreparedStatement.class.getMethod(...);

关于java - 通过反射调用 Oracle JDBCPreparedStatement 方法失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23829340/

相关文章:

java - 强制 JAVA 项目使用 .java 文件而不是 JRE lib .class 文件(使用 Eclipse)

java - 是否有任何基于浏览器的 IDE 来编写 java 程序?

oracle如何更改表按范围间隔添加分区

sql - 如何格式化时间戳

java - 是否可以不执行一条指令就退出 RUNNABLE 状态?

java - 如何使用 Maven 添加 Postgresql jdbc 驱动程序 9.2-1003-jdbc3?

oracle - 封装规范中的程序

java - 使用准备好的语句的动态列名 + 包含变量的 sql 查询

java - JDBC 在 "same"时间打开 2 个连接

java - 从登录参数创建用户对象