I am experiencing some buggy behaviour regarding the TREAT operator on MapJoins. In the following I will only sketch the scenario, for the full detail see the attached maven project. I have an entity
class DBObject {
private Map<DBKey, DBValue> properties;
}
that holds a mapping from keys (entities) to values (also entities). DBValue is an abstract super class for several subclasses that store a specific value, e.g. DBValueWithString or DBValueWithReference (which references other DBObjects). I now want to query for objects, that own specific properties with certain values. Therefore I use the following CriteriaQuery:
final CriteriaBuilder builder = em.getCriteriaBuilder();
final CriteriaQuery<DBObject> query = builder.createQuery(DBObject.class);
final Root<DBObject> from = query.from(DBObject.class);
final MapJoin<DBObject, DBKey, DBValue> valueJoin = from.join(DBObject_.properties);
final MapJoin<DBObject, DBKey, DBValueWithString> stringJoin = builder.treat(valueJoin, DBValueWithString.class);
final Predicate predicate1 = builder.and(
builder.equal(stringJoin.get(DBValueWithString_.key), key1),
builder.equal(stringJoin.get(DBValueWithString_.value), "test")
);
This query generates to reasonable JPQL:
The corresponding SQL seems a little off, however it works:
Note the unnecessary join with DBValueWithReference. According to the doc, the TREAT operator should also filter out the non matching subtypes, therefore entities of type DBValueWithReference can be completely ignored at this point. The problem now arises, when querying for DBValueWithReferences. The JPQL query seems fine once again:
However the translation process to SQL fails with the following error message:
The problem seems to correlate with the unnecessary join, as the type seems to be inferred solely based on the first join (with DBValueWithString), which is unnecessarily part of the second query. It seems, DBValue is joined with every sub class that is encountered in the persistence.xml. If you uncomment the @Entity annotation for DBValueWithBoolean, it will occur in the list of join statements. Moreover, the order of the joins seems to depend on the order of which the classes are listed in the persistence.xml. If you swap the entries for DBValueWithString and DBValueWithReference, the second query executes just fine, whereas the first query now yields:
So is this just a misinterpretation of what the TREAT operator is supposed to do, or is it actually a bug in the SQL generation? |