Java 6 breaks JDBC

I’m cross. Very cross. Cross with Sun for releasing a new version of Java which shatters both backward- and forward- compatibility. Cross enough that I cannot see any sensible way of moving my software to Java 6 in the near future.

It all started with an innocuous question in a comment on my Punchbarrel blog. I had posted asking for opinions on a move to Java 5, potentially abandoning Java 1.4, for the core Stringtree codebase. The question in the comment was about skipping Java 5 and moving directly to Java 6. This would normally be too big a leap, but I replied that I would endeavour to continue my policy of ensuring my code works with as wide a range of Java versions as possible.

Then I actually tried to do it, and that’s what made me cross. The more I attempted to produce code which would compile and run on Java 1.4, Java 5 and Java 6, the more impossible it began to seem.

I was already aware of the first hurdle. Sun have added new methods to a lot of key JDBC interfaces by tagging them with the new javax.sql.Wrapper interface. This is actually relatively easy to fix in a compatible way. Just add two new methods to each class which implements one of the affected JDBC interfaces:

    public boolean isWrapperFor(Class clz) {
        return false;
    }
 
    public Object unwrap(Class clz) throws SQLException {
        throw new SQLException("Not a Wrapper for " + clz);
    }

The actual method signatures in the Java 6 Wrapper interface are phrased in terms of Generics, but the above stripped version compiles fine using Java 1.4, Java 5 and Java 6 compiler and libraries.

However, even after adding these arguably pointless methods to all my concrete implementations of affected JDBC interfaces, I still had a bunch of compilation errors when using Java 6 libraries. And this is where Sun have really screwed up.

Several other key JDBC interfaces have also been extended. But this time it has not been done by anything as simple as tagging with a new interface. These interfaces have all gained extra method themselves. This should not be a deal-breaker. It should be feasible to just add implementations of these methods to the existing Java 1.4-compatible code. After all, any class is free to define any methods it likes, not just those from an interface.

Nope.

It is simply impossible to add these new methods to a class and have that class still compile in a Java 1.4 or Java 5 environment.

The reason is that these methods are themselves defined in terms of classes and interfaces which do not exist in earlier Java versions. For example, the java.sql.Connection interface gains methods referring to new interfaces NClob and SQLXML

There is no answer to this. Sun have broken backward and forward compatibility of JDBC in Java 6. It is no longer possible to write an implementation of several key JDBC interfaces in a way which compiles under all the most popular Java versions.

Once again, Sun completely misunderstands the real world. Not everyone is free to upgrade every deployment to the very latest Java version immediately it is released. Even within those who do manage to update all their machines in one go, not everyone can immediately drop real work to spend time messing with old code which should still work to bring it into line with a new fashion.

As I wrote at the start of this rant. I’m cross.

Grrr.

Comments (13) left to “Java 6 breaks JDBC”

  1. anjan bacchu wrote:

    hi there,

    I faced this 2 years ago while trying to see if our java 1.5 app could compile against 1.6. At that time, requirement for 1.6 was not serious, so I easily backed off.

    One other thing to do is to check on the apache, jboss and spring forums to see what others developers/vendors did in the same situation.

    BR,
    ~A

  2. Mark wrote:

    As I understand it Java has never claimed to be forwardly compatible. Its always been backwardly compatible. i.e. that code from a earlier version will run on a later version of the VM but never the other way round?

  3. Guy Korland wrote:

    You’re completely right and we encountered the same problem when we tried to upgrade our JDBC implementation.
    But, did you try using retrotranlator or retroweaver?

  4. Geert Bevin wrote:

    I’ve solved this in RIFE by creating different classes for the different JDBC versions and have the same common base class. So I have DbResultSet, DbResultSet30 and DbResultSet40. Then, when the statement is returning a resultset it checks for the Java version compatibility and selects the right resultset class to create an instance of.

    Hope this helps.

    Geert

    http://svn.rifers.org/rife/trunk/src/framework/com/uwyn/rife/database/DbResultSet.java
    http://svn.rifers.org/rife/trunk/src/framework/com/uwyn/rife/database/DbResultSet30.java
    http://svn.rifers.org/rife/trunk/src/framework/com/uwyn/rife/database/DbResultSet40.java
    http://svn.rifers.org/rife/trunk/src/framework/com/uwyn/rife/database/DbStatement.java (see wrapWithDbResultSet method)

  5. Frank wrote:

    @Guy Korland
    I’d not heard of those. I’ll definitely take a look. Thanks

  6. Frank wrote:

    @Geert Bevin
    That’s the kind of direction I was thinking of.

    The main problem is with managing the source code. I typically develop in Eclipse, which (AFAIK) only supports one Java version setting per project. This implies that in order to edit and compile these different variants I need to set up multiple Eclipse projects for a single final jar.

    I guess it’s doable, but certainly clumsy.