views:

968

answers:

3

I'm using Oracle 10g and Hibernate 2.1 (old version, but not allowed to upgrade to fix). I've got a table with a not nullable column named TIN, varchar2(9). This column is used to stage data loaded from flat files and so can store any string of length 9, including 9 spaces (that is: ' ') if the input file had 9 spaces.

What I've noticed is that:

  1. Oracle 10g automatically converts empty strings to NULL. So if you execute:

    SELECT NVL('', 'Input string converted to NULL') FROM dual;
    

    the result is 'Input string converted to NULL', not ''. I think this is relevant to the problem.

  2. When Hibernate reads a record where the TIN value is 9 spaces (or any number of spaces) with no other character, it stores the value in memory as ''. Hibernate then seems to trick itself into thinking, so to speak, that the value has changed from 9 spaces to an empty string. Then if the record is written back to the database, Hibernate tries to to write an empty string rather than 9 spaces and Oracle apparently converts this to null and then throws a not-null constraint violation.

For reference, here's the HBM for this column:

<property
    name="tin"
    type="java.lang.String"
    column="TIN"
    not-null="true"
    length="9">

My question is, how do I instruct Hibernate not to convert values that only contain spaces to empty strings?

Update 1: I just went back and tested strings like ' text ' and found that Hibernate trims these spaces too, making the string 'text' and fooling itself into thinking that the value changed. I must be missing something. This doesn't seem like default behavior.

Update 2: It looks like the hbm2java Ant task that converts the HBM to Java source might be the source of the String trims. Still investigating.

Thanks, Jeff

Edit: Changed question wording to be more precise.

+1  A: 

I don't know about hibernate. But I do know that Oracle does treat empty strings as Null values. And there isn't any workaround for it.

Thomas Jones-Low
+2  A: 

One way to deal with this is to create a Hibernate UserType. There are instructions in the link at hibernate.org with some already-made classes for String escaping. I realize this doesn't directly answer 'how do I tell hibernate not to convert 9 spaces to empty string', but it will solve the storage problem on the database side.

There are 2 implementations on that page - one that escapes all strings (may handle your 9 spaces case in your desired manner), and one that only escapes the value ''. It sounds like the former may be a better fit for you.

For the record - I am utilizing this method myself. All you need to do is have the class in your classpath, and set the 'type' attribute in your HBM.xml to the fully qualified classname (i.e add type="my.fully.qualified.HibernateUserType" to your property element).

On the Java side your classes will still deal directly with java.lang.String and the values you expect to see, but on the hibernate side, they will use the UserType class.

Joshua McKinnon
Thanks Joshua. This seems like a pretty good approach. The biggest problem I see is that everything that accesses that data outside of Hibernate must also be aware that the values are escaped. I guess I'm wondering whether this behavior of Hibernate trimming values is normal/documented.
jlpp
True - if there are other parts of code that access the database outside of hibernate, that is an issue.
Joshua McKinnon
Unfortunately, this is the case. There are stored procedures written to process flat files in batch that read/write the same column. Thanks though. Yours was a solid answer.
jlpp
+1  A: 

Turned out that the issue wasn't with Hibernate. The project I'm working on was set up to generate Java POJOs from the HBM files using the hbm2java Ant task. This in turn references a source template for getters/setters/equals/toString/etc that was written to trim all java.lang.String properties in the setter. To fix the problem I passivated the one class with which I was having problems so that it would no longer be generated on each build and then removed the trim on the TIN property.

jlpp