Search code examples
oracle11gslickscala-2.10bonecp

Casting java.sql.Connection to oracle.jdbc.OracleConnection results in compilation error


I would like to cast java.sql.Connection to oracle.jdbc.OracleConnection in order to bind data on ARRAY to my query.

When I try the following on scala 2.10, bonecp 0.8.0 and slick 2.0.0:

import com.jolbox.bonecp.ConnectionHandle
import oracle.jdbc.OracleConnection

def failsWithCompilationError() = {
  Database.forDataSource(ds).withDynTransaction {
    val connection = dynamicSession.conn.asInstanceOf[ConnectionHandle].getInternalConnection
    println(connection.unwrap(classOf[OracleConnection]))
    // When uncommenting following two lines a compilation error "error while loading AQMessage, class file '.../ojdbc6.jar(oracle/jdbc/aq/AQMessage.class)' is broken" will occur
    // val oracleConnection: OracleConnection = connection.unwrap(classOf[OracleConnection])
    // println(oracleConnection)
  }
}

and uncomment the two lines with assignment to a val of type OracleConnection and printlna compilation failure

[error] error while loading AQMessage, class file '.../ojdbc6.jar(oracle/jdbc/aq/AQMessage.class)' is broken will occur.

I already verified that the ojdbc6.jar should not be corrupted by downloading newer version from Oracle.


Solution

  • It seems that the problem was with the Scala compiler.

    As soon as I embedded the functionality that depended on oracle.jdbc.OracleConnection into a plain old Java class, built that into a separate .jar and linked with my Scala code things started to roll.

    Here's how I got this to work:

    OracleArray.java

    package my.application.oracle.collections;
    
    import oracle.jdbc.OracleConnection;
    import oracle.jdbc.OraclePreparedStatement;
    import oracle.sql.ARRAY;
    import scala.Long;
    import scala.Tuple2;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;
    
    /*
    Wraps usage of Oracle ARRAYs since casting java.sql.Connection to oracle.jdbc.Connection does not compile on Scala.
    */
    public class OracleArray {
        public static List<Tuple2<Long, Long>> fetchAssetsByIds(List ids, Connection connection) throws SQLException {
            OracleConnection oracleConnection = (OracleConnection) connection;
            ARRAY oracleArray = oracleConnection.createARRAY("MY_ARRAY_SQL_TYPE", ids.toArray());
            String sql = "SELECT a.id, a.value" +
                    "FROM ASSET a " +
                    "WHERE a.id IN (SELECT COLUMN_VALUE FROM TABLE(?))";
            PreparedStatement statement = oracleConnection.prepareStatement(sql);
            try {
                OraclePreparedStatement oraclePreparedStatement = (OraclePreparedStatement) statement;
                oraclePreparedStatement.setArray(1, oracleArray);
                ResultSet resultSet = oraclePreparedStatement.executeQuery();
                try {
                    ArrayList<Tuple2<Long, Long>> resultTuples = new ArrayList<>();
                    while (resultSet.next()) {
                        long id = resultSet.getLong(1);
                        long value = resultSet.getLong(2);
                        resultTuples.add(new Tuple2(id, value));
                    }
                    return resultTuples;
                } finally {
                    resultSet.close();
                }
            } finally {
                statement.close();
            }
        }
    }
    

    DataUser.scala

    package my.application
    
    import my.application.oracle.collections.OracleArray
    
    import scala.slick.driver.JdbcDriver.backend.Database
    import Database.dynamicSession
    import com.jolbox.bonecp.ConnectionHandle
    
    import java.sql.Connection
    import collection.JavaConversions._
    
    /*
      Uses BoneCP and Slick to connect to database and relays java.sql.Connection to
      OracleArray in order to run operations that use Oracle ARRAYs
    */
    object DataUser {
        def doSomethingWithAssets(ids: Seq[Long]): Unit = {
            Database.forDataSource(ds).withDynTransaction {
                val connection: Connection = dynamicSession.conn.asInstanceOf[ConnectionHandle].getInternalConnection
                val assets: Seq[(Long, Long)] = OracleArray.fetchAssetsByIds(ids, connection)
                println(assets)
            }
        }
    }