Thursday, May 19, 2011

Groovy vs Java difference provides a WTF moment

We all (should) know that not ALL Java is valid Groovy, but if you don't spend you day working in Groovy, then sometimes you forget these differences.   These 'gotchas' are documented here but who reads this stuff, right?  :-)

I wanted to get DatabaseMetadata for a database, specifically a list of table names, so I started with a copy of Java code that already did the same thing.

Class.forName(jdbcDriver);
Connection con = DriverManager.getConnection(jdbcURL, "root", "root");
DatabaseMetaData dbmd = con.getMetaData();
String[] tableTypes = { "TABLE" };
ResultSet allTables = dbmd.getTables(null, null, null, tableTypes);
System.out.println("processing tables...");
while (allTables.next()) {
    String table_name = allTables.getString("TABLE_NAME");
    System.out.println("processing table"+table_name);
}

Should be simple, right - drop the semi-colons and look for other opportunities to shorten the code using Groovy.

def db = Sql.newInstance('jdbc:mysql://127.0.0.1:3306/mydb', 'root', 'root', 'com.mysql.jdbc.Driver')
def metaData = db.getConnection().getMetaData()
println("Driver Name: " + metaData.getDriverName())
println("Database Product: " + metaData.getDatabaseProductName())
println("Driver Name: "+ metaData.getDriverName())
println("Driver Version: "+ metaData.getDriverVersion())
String[] tableTypes = { "TABLE" }
ResultSet allTables = metaData.getTables(null, null, null, tableTypes)
while (allTables.next()) {
    String table_name = allTables.getString("TABLE_NAME")
    println "processing table "+table_name
}

And it almost worked!   The thing that threw me was that not only does the code compile, but it also returns correct values for some of the methods in the DatabaseMetadata, but when it came time to iterate across the table names, there were none.  Come on, I just ran almost the exact code in Java and it spit out all the table names in my database.  What the heck did I mess up this time?

My error was the 3rd bullet on the list of gotchas from above - how NOT to initialize an array in Groovy...

String[] tableTypesJava = { "TABLE" }
String[] tableTypesGroovy = [ "TABLE" ]
println tableTypesJava.class
println tableTypesGroovy.class
assertEquals(tableTypesGroovy, tableTypesJava)  // this will fail!

Both variables say they are String[] but tableTypesJava is really closure when compiled in Groovy! The fix to the Groovy code is to use the brackets when initializing the array and not the braces - basically the tableTypesGroovy from above. Guess it's time to RTFM...

Hope this helps!

5 comments:

  1. to me groovy look more confusing and more like a scripting language. Java has that neck of readability and clarity which is not present everywhere, nice compilation by the way.

    Javin
    How classpath works in Java

    ReplyDelete
  2. Well written Groovy code is much more readable. There is a lot less ceremony and cruft to get in the way.

    Poorly written Groovy code looks like Java IMHO.

    ReplyDelete
  3. Not common but tricky moment is when you hit dynamic dispatch. It is really hard to get it in normal life, and even then it probably means that you had error in your Java code, but it is possible that after moving to Groovy different methods will be called than in Java ;)

    ReplyDelete
  4. Hardly a "gotcha", closures are an absolutely basic construct when working with Groovy. You should be able to recognize the closure syntax when you see it.

    ReplyDelete
  5. Thanks, Alexander. Enough of a 'gotcha' to be documented by the Groovy team but I get your point! :-)

    ReplyDelete