Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPA2 Criteria API .as(String.class) casting to char(1) - How do I work around this?

Using the criteria api, I have a query which does something like:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<SourcePath> pathQuery = cb.createQuery(SourcePath.class);
Root<SourcePath> pathRoot = pathQuery.from(SourcePath.class);
pathQuery.where(cb.equal(cb.literal(0x00010002).as(String.class), (pathRoot.get(SourcePath_.path))));

TypedQuery<SourcePath> query = entityManager.createQuery(pathQuery);
query.getResultList();

The resulting sql query results something like:

select ...snip aliases... from SourcePath where cast(x'00010002', char(1)) = path;

(path would be some nasty old alias, but that's irrelevant).

This query is incorrect. Particularly, the cast: cast(x'00010002', char(1)) is not a cast to as string, as specified by .as(String.class), instead it should be either cast(x'00010002', char), or cast(x'00010002', char(N) where N is a suitably big enough number.

I've isolated the cause of this cast failure to the MySqlDialect provided by org.hibernate. Particularly:

public String getCastTypeName(int code) {
    if ( code==Types.INTEGER ) {
       return "signed";
    }
     else if ( code==Types.VARCHAR ) {
        return "char";
    }
    ...snip...
}

Which farther down the chain is interpreted as a char, which is registered by the dialog: registerColumnType( Types.CHAR, "char(1)" );.

Finally, to my question. How can I work around this behaviour? Do I report it as a bug to Hibernate? Do I extend the Dialog and correct the returned type from getCastTypeName? Is there an alternative to .as which will appropriately cast? Or, should I use strings everywhere I'm currently using hex-values to avoid touching edge cases of hibernate's implementation?

Thanks idb

like image 276
idbentley Avatar asked Jul 27 '11 20:07

idbentley


1 Answers

IMHO, you should use a String literal like cb.literal("\u0001\u0002"). Reasons:

  1. It's the same thing as cb.literal(0x00010002).as(String.class), but less verbose.
  2. Does not hit any "edge case"
  3. It's clearer: does "0x00010002" be treated in Big Endian or LE? Which encoding should be used?
  4. You can improve legibility using constants (e.g.: cb.literal(ASCII_SOH + ASCII_STX) where SOH="\u0001" and STX="\u0002").

EDIT: Adding a better description, since I didn't see the "Or, should I use strings everywhere I'm currently using hex-values"

like image 88
Eduardo Costa Avatar answered Oct 13 '22 23:10

Eduardo Costa