BTM
  1. BTM
  2. BTM-56

BTM is not compatible with JDBC 4 API

    Details

    • Type: Improvement Improvement
    • Status: Closed Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 1.3.3
    • Fix Version/s: 2.0.0
    • Labels:
      None
    • Environment:
      Java 1.6.0_12 + Spring 2.5.6 + Tomcat 6.0.18 + Oracle 10r2
    • Number of attachments :
      3

      Description

      Hi,

      I'm getting the following exception when using BTM 1.3.3-RC2 :

      java.lang.AbstractMethodError: bitronix.tm.resource.jdbc.JdbcConnectionHandle.createClob()Ljava/sql/Clob;
      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)
      at org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy$TransactionAwareInvocationHandler.invoke(TransactionAwareDataSourceProxy.java:225)
      at $Proxy16.createClob(Unknown Source)
      at com.a2a.movalys.fwk.transaction.Connection.createClob(Connection.java:459)
      at com.adeuza.movalys.fwk.data.dao.AbstractDao.createClob(AbstractDao.java:132)

      My application code looks like this :

      oPreparedStatement.setLong(1, oObject.getId());

      [...]
      Clob oClob = oConnection.createClob();
      oClob.setString(1, p_sContent);
      oPreparedStatement.setClob(8, oClob);

      This work fine with direct Oracle JDBC Datasource.

      AFAIK, there is missing methods in JdbcConnectionHandle to be uptodate regarding JDBC 4.0 API (included in Java 6)

      Regards,

      1. patch.txt
        86 kB
        Brett Wooldridge
      2. patchJDBC4src.txt
        59 kB
        Brett Wooldridge
      3. patchJDBC4test.txt
        109 kB
        Brett Wooldridge

        Activity

        Hide
        Brett Wooldridge added a comment -

        Ludovic,

        I have found a problem with the JDBC4 isValid()/testQuery logic.

        Currently, we have code something like this:

        JdbcPooledConnection.java
        private void testConnection(Connection connection) throws SQLException {
           ...
           if ("isValid".equals(query) && jdbcVersionDetected >= 4) {
              try {
        	 isValid = (Boolean) isValidMethod.invoke(connection, new Object[] {...});
              } catch (Exception e) {
                 log.warn("dysfunctional JDBC4 Connection.isValid() method...  Falling back to test query.");
                 jdbcVersionDetected = 3;
              }
        }
        

        If the driver is dysfunctional, or the acquisition timeout is negative, we fallback to using the test query. The problem in this case is that we are requiring the test query to be the String "isValid". Because this isn't valid SQL, when (if) we fallback to the test query it will always throw an exception, and thus it would be impossible to obtain a connection.

        Related, but minor is in the detectJdbcVersion() method where we have this logic:

        JdbcPooledConnection.java
        private synchronized void detectJdbcVersion(Connection connection) {
           try {
              ...
              jdbcVersionDetected = 4;
              if (poolingDataSource.getTestQuery() != null) {
                 log.info("test query is configured but DataSource is JDBC4 or newer and supports isValid(), ignoring test query");
              }
           } catch (Exception e) {
              jdbcVersionDetected = 3;
           }
        }
        

        Because we are requiring the test query to be configured as "isValid", it will never be null here and we will always log this informational message.

        I don't really like the idea of semantic overloading – using the test query as both a flag or a test query. And I think that testing via JDBC4 Connection.isValid(), but being able to fallback to a SQL test query, is a valid use case.

        I add a new property, enableJdbc4ConnectionTest. This will allow both a test query and/or JDBC4 connection testing to be configured. enableJdbc4ConnectionTest is disabled by default.

        • If a driver that supports JDBC4 isValid() is detected, but enableJdbc4ConnectionTest is not set, we log an informational.
        • If enableJdbc4ConnectionTest is true and the driver supports isValid(), the connection will be tested first by isValid()
        • If the isValid() test fails, but there is a test query configured, the test query will be used to validate the connection.
        • If only a test query is configured, the behavior is the same as 1.3.3
        Show
        Brett Wooldridge added a comment - Ludovic, I have found a problem with the JDBC4 isValid()/testQuery logic. Currently, we have code something like this: JdbcPooledConnection.java private void testConnection(Connection connection) throws SQLException { ... if ( "isValid" .equals(query) && jdbcVersionDetected >= 4) { try { isValid = ( Boolean ) isValidMethod.invoke(connection, new Object [] {...}); } catch (Exception e) { log.warn( "dysfunctional JDBC4 Connection.isValid() method... Falling back to test query." ); jdbcVersionDetected = 3; } } If the driver is dysfunctional, or the acquisition timeout is negative, we fallback to using the test query. The problem in this case is that we are requiring the test query to be the String "isValid". Because this isn't valid SQL, when (if) we fallback to the test query it will always throw an exception, and thus it would be impossible to obtain a connection. Related, but minor is in the detectJdbcVersion() method where we have this logic: JdbcPooledConnection.java private synchronized void detectJdbcVersion(Connection connection) { try { ... jdbcVersionDetected = 4; if (poolingDataSource.getTestQuery() != null ) { log.info( "test query is configured but DataSource is JDBC4 or newer and supports isValid(), ignoring test query" ); } } catch (Exception e) { jdbcVersionDetected = 3; } } Because we are requiring the test query to be configured as "isValid", it will never be null here and we will always log this informational message. I don't really like the idea of semantic overloading – using the test query as both a flag or a test query. And I think that testing via JDBC4 Connection.isValid(), but being able to fallback to a SQL test query, is a valid use case. I add a new property, enableJdbc4ConnectionTest . This will allow both a test query and/or JDBC4 connection testing to be configured. enableJdbc4ConnectionTest is disabled by default. If a driver that supports JDBC4 isValid() is detected, but enableJdbc4ConnectionTest is not set, we log an informational. If enableJdbc4ConnectionTest is true and the driver supports isValid(), the connection will be tested first by isValid() If the isValid() test fails, but there is a test query configured, the test query will be used to validate the connection. If only a test query is configured, the behavior is the same as 1.3.3
        Hide
        Brett Wooldridge added a comment -

        Checked in code adding new enableJdbc4ConnectionTest flag and semantics.

        Show
        Brett Wooldridge added a comment - Checked in code adding new enableJdbc4ConnectionTest flag and semantics.
        Hide
        Ludovic Orban added a comment -

        Brett,

        I've reviewed your latest changes and removed the serial version UID from the PoolingDataSource and changed the 'isValid disabled' log to debug.

        While reviewing this minor thing there is another more important one I noticed which I missed at first: in JdbcPooledConnection.getConnectionHandle() you added code to force auto-commit to true when in global TX mode. I removed that as the JDBC drivers and DB behaviors in that case is completely random so not only this change could have caused havoc but it also wasn't very consistent as only connection acquired in a global TX context would have run into this.

        I've read your comment about XAER_OUTSIDE but if an end user decides to mix both local and global transactions he'd better know what he's doing and the connection pool shouldn't decide on the user's behalf. if the JDBC driver decides to throw a XAException(XAER_OUTSIDE) then this should be reported and not avoided.

        Show
        Ludovic Orban added a comment - Brett, I've reviewed your latest changes and removed the serial version UID from the PoolingDataSource and changed the 'isValid disabled' log to debug. While reviewing this minor thing there is another more important one I noticed which I missed at first: in JdbcPooledConnection.getConnectionHandle() you added code to force auto-commit to true when in global TX mode. I removed that as the JDBC drivers and DB behaviors in that case is completely random so not only this change could have caused havoc but it also wasn't very consistent as only connection acquired in a global TX context would have run into this. I've read your comment about XAER_OUTSIDE but if an end user decides to mix both local and global transactions he'd better know what he's doing and the connection pool shouldn't decide on the user's behalf. if the JDBC driver decides to throw a XAException(XAER_OUTSIDE) then this should be reported and not avoided.
        Hide
        Brett Wooldridge added a comment -

        I'm cool with that.

        Just one question, why remove the serial version UID? My IDE flags the class with a warning because it doesn't have it. Any class that implements Serializable, even through multiple layers of interfaces, should generally have a serial version UID.

        Show
        Brett Wooldridge added a comment - I'm cool with that. Just one question, why remove the serial version UID? My IDE flags the class with a warning because it doesn't have it. Any class that implements Serializable, even through multiple layers of interfaces, should generally have a serial version UID.
        Hide
        Ludovic Orban added a comment -

        Brett,

        (I think you forgot to commit your latest change.)

        Regarding the serial version UID I prefer to leave it out for now, as stupid this sounds. I'm not sure PoolingDataSource and PoolingConnectionFactory should be serializable at all. I think I added that for full support of JNDI but that seems unnecessary after all.

        I also had bad experiences with fixed serial version UIDs where classes weren't compatible between versions but the UID was not updated and I don't want to go through that hassle.

        Show
        Ludovic Orban added a comment - Brett, (I think you forgot to commit your latest change.) Regarding the serial version UID I prefer to leave it out for now, as stupid this sounds. I'm not sure PoolingDataSource and PoolingConnectionFactory should be serializable at all. I think I added that for full support of JNDI but that seems unnecessary after all. I also had bad experiences with fixed serial version UIDs where classes weren't compatible between versions but the UID was not updated and I don't want to go through that hassle.

          People

          • Assignee:
            Brett Wooldridge
            Reporter:
            Damien Raude-Morvan
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: