17

为什么 java.sql.Connection 不能在下面的代码中转换为 oracle.jdbc.OracleConnection?

我的主要目标是将新用户名传递给 Oracle 连接并将其保存在例如“osuser”列中的“SESSION”表中,因为我想跟踪 DB 用户更改并将其显示在表中。

@Repository
public class AuditLogDAOImpl implements AuditLogDAO {

    @PersistenceContext(unitName="myUnitName")
    EntityManager em;

    @Resource(name = "dataSource")
    DataSource dataSource;

    public void init() {

        try {
            Connection connection = DataSourceUtils.getConnection(dataSource);
            OracleConnection oracleConnection = (OracleConnection) connection; //Here I got cast exception!

            String metrics[] = new String[OracleConnection.END_TO_END_STATE_INDEX_MAX];
            metrics[OracleConnection.END_TO_END_CLIENTID_INDEX] = "my_new_username";

            oracleConnection.setEndToEndMetrics(metrics, (short) 0);

            java.util.Properties props = new java.util.Properties();
            props.put("osuser", "newValue");

            oracleConnection.setClientInfo(props);

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

这是错误日志:

10:42:29,251 INFO  [STDOUT] org.jboss.resource.adapter.jdbc.jdk6.WrappedConnectionJDK6@bcc8cb
10:42:51,701 ERROR [STDERR] java.lang.ClassCastException: $Proxy286 cannot be cast to oracle.jdbc.OracleConnection

通常在这种情况下我有两个问题:

  • 为什么从 Connection 转换到 OracleConnection 失败和
  • 实现我的意图的最佳方法是什么(我的意思是在 Oracle DB 中将新用户名设置为 v$session.osuser?

我使用 Oracle 11g、Hibernate(使用实体管理器)、通过 jndi 的数据源。

请帮忙,谢谢!

编辑:

经过一些改进后,铸造问题仍然存在。

改进:

Connection connection = DataSourceUtils.getConnection(dataSource);
connection = ((org.jboss.resource.adapter.jdbc.WrappedConnection)connection).getUnderlyingConnection();
OracleConnection oracleConnection = (OracleConnection) connection;

错误:

java.lang.ClassCastException: $Proxy287 cannot be cast to org.jboss.resource.adapter.jdbc.WrappedConnection
4

10 回答 10

40

您正在检索的连接可能是一个包装的连接。

如果您确实需要获取底层 Oracle 连接,您应该使用:

if (connection.isWrapperFor(OracleConnection.class)){
   OracleConnection oracleConnection= connection.unwrap(OracleConnection.class);  
}else{
   // recover, not an oracle connection
}

和方法从 Java 1.6 开始可用isWrapperForunwrap并且应该由 A/S 连接包装器有意义地实现。

于 2013-03-18T17:55:55.687 回答
4

连接池通常有一个围绕真实连接实例的包装器,这就是你的转换失败的原因。

无论如何,您正在做的事情都行不通,因为仅在建立连接时才检查属性实例中的参数。由于您的连接已经处于活动状态,因此它不会改变任何内容。

您需要使用DBMS_APPLICATION_INFO.SET_CLIENT_INFO()才能更改现有连接。

于 2013-03-18T17:46:41.177 回答
3

这仅适用于通过搜索如何在 OracleConnection 中设置指标来这里的人,我在这方面花费了大量时间,因此可能会对某人有所帮助。

获得“连接”后,这应该可以工作:

DatabaseMetaData dmd = connection.getMetaData();
Connection metaDataConnection = null;

if(dmd != null)
{
    metaDataConnection = dmd.getConnection();
}

if(!(metaDataConnection instanceof OracleConnection))
{
    log.error("Connection is not instance of OracleConnection, returning");
    return; /* Not connection u want */
}

OracleConnection oraConnection = (OracleConnection)metaDataConnection;

String[] metrics = new String[END_TO_END_STATE_INDEX_MAX]; // Do the rest below...

它适用于 OracleConnection,但在设置指标时我遇到了差异问题:

short zero = 0;
oraConnection.setEndToEndMetrics(metrics, zero);

通过我设置指标几次的方法代理连接后,我得到:

java.sql.SQLRecoverableException: No more data to read from socket

但我认为这与一些 Spring 接线初始化或连接池有关。

于 2014-02-28T23:56:02.913 回答
1

您可以访问 Wrapper 内部的 OracleObject,在这种情况下,包装器类型是 NewProxyConnection:

(我在我的项目中使用过它,它有效......没有谜团,只需使用反射)

Field[] fieldsConn= connection.getClass().getDeclaredFields();

Object innerConnObject = getFieldByName(fieldsConn,"inner").get(connection);


if(innerConnObject instanceof OracleConnection ){
  OracleConnection oracleConn = (OracleConnection)innerConnObject;
 //OracleConnection unwrap = ((OracleConnection)innerConnObject).unwrap();
  // now you have the OracleObject that the Wrapper 
}


//Method: Set properties of the ooject accessible.
 public static  Field getFieldByName(Field[] campos, String name) {
    Field f = null;
    for (Field campo : campos) {
        campo.setAccessible(true);
        if (campo.getName().equals(name)) {
            f = campo;
            break;
        }
    }
    return f;
 }
于 2014-09-26T07:44:34.140 回答
1

我在使用 spring 获取连接时遇到了这个问题。通常,每一层都在基本类上添加一个包装器。我刚刚完成了 connection.getClass().getName() 以查看正在重新调整的连接的运行时类型。它将是一个包装器/代理,您可以通过它轻松找到获取基本 OracleConnection 类型的方法。

于 2013-03-18T17:49:38.367 回答
0

尝试以下

我遇到了同样的问题。我们使用的是 spring,它有一个名为 NativeJdbcExtractor 的类。它有许多实现,以下一个适用于 TomCat。Jboss 有一个特定的实现,称为 JBossNativeJdbcExtractor

<bean id="jdbcExtractor" class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor"></bean>

在您的 DAO 中,您可以注入 bean 并使用以下方法

protected NativeJdbcExtractor jdbcExtractor;
Connection conn=jdbcExtractor.getNativeConnection(oracleConnection);
于 2013-10-16T04:24:30.730 回答
0

经过反复试验。这种方式有效:

        DelegatingConnection delConnection = new DelegatingConnection(dbcpConnection);
    oraConnection = (oracle.jdbc.OracleConnection)delConnection.getInnermostDelegate();

但是这种方式为 oraConnection 返回了一个空指针:

DelegatingConnection delConnection = (DelegatingConnection) dbcpConnection;
    oraConnection = (oracle.jdbc.OracleConnection)delConnection.getInnermostDelegate();
于 2019-05-03T07:34:26.547 回答
0

不确定我的情况是否相关,但对于我的项目,简单地更改数据库配置设置实际上会导致 unwrap 失败!

我正在使用带有 Scala 的 Play 框架;这仅在logSql=false时对我有用:

db.withConnection { implicit c  =>
  val oracleConnection = c.unwrap(classOf[OracleConnection])
}

(这只是展开 OracleConnection 的 Scala 版本)

当我设置logSql=true时,我得到:

com.sun.proxy.$Proxy17 无法转换为 oracle.jdbc.OracleConnection java.lang.ClassCastException: com.sun.proxy.$Proxy17 无法转换为 oracle.jdbc.OracleConnection

所以关于logSql 配置的一些东西实际上会导致 unwrap 失败。不知道为什么。

无论使用哪种配置,我的连接对象都是:

HikariProxyConnection@1880261898 包装 oracle.jdbc.driver.T4CConnection@6b28f065

isWrapperFor(OracleConnection)在这两种情况下都是正确的

Hikari Connection Pool 和 Bone CP 会发生这种情况。也许这是 Oracle JDBC 中的一个错误?

根据MA​​NIFEST.MF的 Oracle JDBC 驱动程序版本

实现版本:11.2.0.3.0
存储库 ID:JAVAVM_11.2.0.4.0_LINUX.X64_130711

于 2016-08-19T21:09:57.137 回答
-1

以下工作可以绕过 AQ 的 TopicConnection.getTopicSession => JMS-112

//DEBUG: Native DataSource : weblogic.jdbc.common.internal.RmiDataSource
con = DataSource.getConnection();
debug("Generic SQL Connection: " + con.toString());               
//DEBUG: Generic Connection: weblogic.jdbc.wrapper.PoolConnection_oracle_jdbc_driver_T4CConnection
if (con != null && con.isWrapperFor(OracleConnection.class)) {
  WebLogicNativeJdbcExtractor wlne = new WebLogicNativeJdbcExtractor();//org.springframework to the rescue!!
  java.sql.Connection nativeCon = wlne.getNativeConnection(con);
  this.oraConnection = (OracleConnection) nativeCon;
  debug("Unwrapp SQL Connection: " + this.oraConnection.toString());
}

//DEBUG: Native Connection: oracle.jdbc.driver.T4CConnection è 

现在我可以在没有 JMS-112 的 AQ-Factory 中使用它

于 2016-02-19T05:50:33.877 回答
-2

尝试像下面一样投射

WrappedConnectionJDK6 wc = (WrappedConnectionJDK6) connection;
connection = wc.getUnderlyingConnection();
于 2014-12-04T04:39:07.903 回答