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.

14 Comments

  1. 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. 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. 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. 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. @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.

  6. I don’t understand your problem. People who want to keep using an ancient version of Java, will probably also want to use ancient versions of the libraries they’re using.

    Why should the latest release of some library still be compatible with something as old as Java 1.4? Do you honestly think people who still run Java 1.4 will upgrade -anything-?

    Most likely they will just be using the versions of all libraries that were current when Java 1.4 was still new and shiny.

  7. @henk
    If it were just a compatibility problem between Java 1.4 and Java 6, I could go with your reasoning.

    However, in this case the problem is between Java 5 and Java 6. And if you think that everyone has already upgraded all of their source code from working with Java 5 to requiring Java 6 then you are living in a very different world to me.

    If the new types (NClob and so on) had been introduced in Java 5, then the JDBC interfaces changed to use them in Java 6, I would be happy. But introducing both at the same time in Java 6 leads inevitably to the problems I describe above.

  8. Groovy solves this using the Proxy technique similar to that described by Geert. It was the only way to make Groovy DataSets seamlessly work across 1.4, 5 and 6. You can check out the source code if you want some ideas.

  9. @Frank

    I hear you and maybe I am living in a different world, but what we always do is update everything early, so we never have giants leaps to take. When we were on Java 5 for example, we did not upgrade any of the libraries we were depending on to versions that were released after Java 6 was released. After about half a year after Java 6 was released, we started a test procedure to see if our own code could run on it and if all libraries we were using were compatible with it. Backwards compatibility appeared to be really great. After some extensive tests, everything appeared to work and believe me, we are using LOTS of 3rd party libraries.

    After the upgrade to Java 6, we started upgrading our libraries to the latest versions, piece by piece. Everything kept working. That’s why I don’t really see the problem. Of course, sometimes there really are backwards compatibility problems. JBoss 4.2 comes to mind.

    It really seems to me though that some people have the idea that upgrading the JVM is ‘evil’ or ‘dangerous’, but upgrading major libraries can always be done. I don’t really agree with that line of reasoning. If you don’t want to upgrade your VM, why would you want to upgrade to the very latest version of something huge like e.g. Hibernate?

  10. Dynamic Proxies might be of help here. I have not done it, but you might be able to generate classes dynamically that implement the published interface without actually writing the code. This way you could be compatible with whatever version of JDK / JDBC you are using.

  11. Isn’t it possible to create a small jar with missing types. You won’t probably need to deploy it, or if you will then for old runtime only.

  12. I’m also pissed by this change. Not many folks have the luxury to leave in a pure environment where versions aren’t mixed. Anybody tried the stub library for the new types? I’m being forced to do a lot of busywork here that doesn’t contribute to the functionality I’m trying to build.

    I’ll look at the dynamic proxy mechanism, but unless something’s going to generate the code for me, it looks like a ton of overhead and code to write.

    Is there a way to do a mixed version that’s normal static methods for the common operations, and a dynamic proxy for the extensions?

Comments are closed.