Extreme Java When vanilla Java is not enough

17Mar/090

Hibernate JPA and Enums

Assume you have a simple enum:

public enum Status {
  ACCEPTED, REJECTED, NOTREADDEN
}

And your legacy database stores it as ordinal values (ACCEPTED = 0, REJECTED = 1, NOTREADDEN = 2). For reasons lost in time, DBA created the column as "varchar". If you want to use JPA, you will have a bad time using Hibernate:

java.lang.IllegalArgumentException: No enum const class mypkg.Status.1
  java.lang.Enum.valueOf(Enum.java:196)
  org.hibernate.type.EnumType.nullSafeGet(EnumType.java:110)

After digging into EnumType.java, I noticed it is hardcoded "if-varchar-then-name-else-ordinal". I assume they never dealed with ancient, legacy databases.

If I switch to TopLink, it works as expected... One thumb down to Hibernate...

18Jun/080

Changing HSQLDB in-process database default password

HSQLDB is not the kind of database I can use in enterprise systems (with terabytes of data), but it is specially useful if I need a simple RDBMS to small (or temporary) databases.
While trying it with Glassfish, I got a big problem: if you create an in-process database (using "jdbc:hsqldb:mem" or "jdbc:hsqldb:file" or "jdbc:hsqldb:res"), HSQLDB creates a default user named "SA" with an empty password. But Glassfish does not accept data sources without passwords (I get a "No PasswordCredential found").
To solve this, I grab the HSQLDB sources and changed the org.hsqldb.Database class. The "reopen" method has the following command:

if (isNew) {
  sessionManager.getSysSession().sqlExecuteDirectNoPreChecks(
    "CREATE USER SA PASSWORD \"\" ADMIN");
  logger.synchLogForce()
}

I just changed it to:

if (isNew) {
  sessionManager.getSysSession().sqlExecuteDirectNoPreChecks(
    "CREATE USER " + urlProperties.getProperty("user") +
    " PASSWORD \"" + urlProperties.getProperty("password") +
    "\" ADMIN");
  logger.synchLogForce();
}

Compiling with "ant jar" (in build dir), the new hsqldb.jar will now allow you to create the user based on the credentials passed as login. But this will only work if you compile with a JDK 5, because a lot of methods required by JDBC in JDK 6 are implemented by throwing UnsupportedOperationException.

For some reason, Glassfish and HSQLDB with in-process only works with OpenJPA. I've tryed with Hibernate and TopLink, with really strange results. If I find a solution, I'll post later.

1Apr/081

How-(not-)to use Hibernate

Digging into some legacy code, I found the worst way to initialize Hibernate. The first step should be something like:

Configuration cfg = new Configuration();
cfg.addClass(Bean1.class)
   .addClass(Bean2.class)
   .addClass(Bean3.class);

But I found this:

Configuration cfg = new Configuration();
Object[] objs = new Object[] {
    new Bean1(), new Bean2(), new Bean3()
};
for (int i = 0; i < objs.length; i++)
    cfg.addClass(objs[i].getClass());
}

You only need to know the basics of Java to find out the waste of memory and CPU, instantiating a lot of classes (and an array) only to get the class instantiated. And, of course, the unnecessary loop to iterate over the array. It's like baking a cake to find out the brand of the flour.

As if it's not enough, this piece of code is replicated in about 7 spots. Yes, the right way to configure Hibernate is using the "HibernateUtils" pattern, as in documentation.

And, as if it's not enough yet, this piece of code creates a SessionFactory that is sent via constructor to an entire hierarchy of classes. Yes, it is the worst solution, since the Session creation could be on HibernateUtils (as in documentation, again).