Search code examples
javahibernatebooleanhsqldb

Is there a way to stop Hibernate from corrupting boolean literals in @Where annotations?


I'd like to use @Where annotation in Hibernate to remove objects which have been marked 'deleted' by a boolean property on that object. For example the following should prevent any deleted addresses from being loaded by Hibernate:

@OneToMany(mappedBy="contact")
@Where(clause="deleted=FALSE")
private Set<Address> addresses; 

However when I use a clause like deleted=FALSE then Hibernate will mangle the boolean literal by prefixing it with a table name, which causes the query to fail. For example:

select ... from address address0_ where  ( address0_.deleted=address0_.FALSE)  and address0_.contact_id=?

What I expected is something like (address0_.deleted=FALSE) instead of (address0_.deleted=address0_.FALSE).

Is there a way to specify the @Where clause or configure Hibernate to correctly output the boolean value?


PS. Note that it is possible with some databases to specify the boolean value as a string literal like this:

@Where(clause="deleted='FALSE'")

That will get converted to (address0_.deleted='FALSE') which works just fine in, for example, PostgreSQL. However I am using HSQLDB for this project, and HSQLDB does not seem to support boolean string literals. On HSQL I get the following exception when using deleted='FALSE':

org.hsqldb.HsqlException: data exception: invalid character value for cast


Solution

  • I've discovered bug reports about this which have been unresolved for more than six years! The Hibernate issue tracker has HHH-1587, HHH-2775 and ANN-647 on the problem.

    The solution is to create a custom Dialect class which registers true, false and unknown as keywords (these are the official boolean literals in the SQL spec). This causes Hibernate to recognise them as keywords and thus stop prefixing them as if they were columns.

    Here's my custom Dialect class which solved the problem for me:

    public class ImprovedHSQLDialect extends HSQLDialect {
    
        public ImprovedHSQLDialect() {
            super();
            registerKeyword("true");
            registerKeyword("false");
            registerKeyword("unknown");
        }
    }
    

    Once this dialect is in use, @Where(clause="deleted=FALSE") works correctly.