【问题标题】:Connection cannot be cast to oracle.jdbc.OracleConnection无法将连接强制转换为 oracle.jdbc.OracleConnection
【发布时间】:2013-03-07 04:12:03
【问题描述】:

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

我的主要目标是将新用户名传递给 Oracle 连接并将其保存在“SESSION”表中,例如“osuser”列,因为我想跟踪 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

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

  • 为什么从 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

【问题讨论】:

  • 如果您从 ClassCastException 中打印出异常消息,它将告诉您连接对象的实际类是什么。这将帮助您(和我们)了解演员阵容失败的原因。
  • 查找它是什么对象 - 通常 System.out.println(connection) 会显示它。
  • 连接内的对象是org.jboss.resource.adapter.jdbc.jdk6.WrappedConnectionJDK6

标签: java oracle hibernate jdbc jndi


【解决方案1】:

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

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

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

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

【讨论】:

    【解决方案2】:

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

    我正在使用带有 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 configuration 的某些内容实际上会导致展开失败。不知道为什么。

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

    HikariProxyConnection@1880261898 换行 oracle.jdbc.driver.T4CConnection@6b28f065

    isWrapperFor(OracleConnection) 在这两种情况下都为真

    这发生在 Hikari Connection Pool 和 Bone CP 上。可能是 Oracle JDBC 的错误?

    根据 MANIFEST.MF

    的 Oracle JDBC 驱动程序版本

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

    【讨论】:

      【解决方案3】:

      以下方法可以绕过 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 中使用它

      【讨论】:

        【解决方案4】:

        尝试像下面这样投射

        WrappedConnectionJDK6 wc = (WrappedConnectionJDK6) connection;
        connection = wc.getUnderlyingConnection();
        

        【讨论】:

          【解决方案5】:

          您可以访问 Wrapper 中的内部 OracleObject,在本例中为 wrapper 类型为 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;
           }
          

          【讨论】:

            【解决方案6】:

            这仅适用于通过搜索如何在 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 接线初始化或连接池有关。

            【讨论】:

              【解决方案7】:

              试试下面的

              我也遇到过同样的问题。我们使用的是 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);
              

              【讨论】:

                【解决方案8】:

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

                如果您真的需要获取您应该使用的底层 Oracle 连接:

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

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

                【讨论】:

                  【解决方案9】:

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

                  【讨论】:

                    【解决方案10】:

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

                    你所做的无论如何都行不通,因为只有在建立连接时才会检查属性实例中的参数。由于您的连接已经处于活动状态,因此它不会改变任何东西。

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

                    【讨论】:

                    • 据我了解,为了达到我的目标,我可以通过两种方式做到这一点:1)通过编辑现有连接(通过DBMS_APPLICATION_INFO.SET_CLIENT_INFO() 或 2)通过在连接建立期间添加用户名,对吗?关于第二种方式,我可以在hibernate中实现这种方法,因为我没有手动创建连接(我已经配置了注入休眠bean的EntityManagerFactory)?
                    • @Roman:抱歉,我不知道如何配置 Hibernate。但是如果你使用例如Tomcat 的连接池可以指定额外的属性。手册中记录了具体需要如何完成
                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2018-09-05
                    • 2018-05-12
                    • 2023-03-17
                    • 2017-08-13
                    • 1970-01-01
                    • 2015-06-16
                    • 2021-06-09
                    相关资源
                    最近更新 更多