[hibernate-dev] Treat support

Steve Ebersole steve at hibernate.org
Thu Aug 4 09:52:13 EDT 2016

Hey Christian,

In general terms, one of the items on the docket for SQM is better TREAT
support; but there is a lot that goes into that statement.  One aspect is
what we support in the parser properly in terms of recognition.  Another is
how this translates into the generates SQL query.  All of this is being
looked at with SQM.

Also, relatedly, better support for switching on/off JPQL-compliance
checking is a goal.

TREAT is an explicit downcast operator.  It allows you to down cast a type
reference in order to reference one of its subclass attributes.  HQL
actually supports implicit downcasting (subclass attribute references).  In
a way you can think of HQL as offering TREAT support implicitly.

With all that in mind, see my comments inline...

On Wed, Aug 3, 2016 at 9:43 PM Christian Beikov <christian.beikov at gmail.com>

>   * Missing support for treated paths like "TREAT(...).property" in the
>     parser. This is more or less workaroundable for non-root path
>     treats. Since root paths can't be treat joined to my knowledge, some
>     treated paths simple can't be used. Maybe in 90% of the cases
>     hibernate will just resolve to the right property without needing
>     the treat?

I'm not sure what you mean by "root paths can't be treated".  You mean
literally the query root?  As in "MyEntity e" in "select e from MyEntity
e"?  If so, yes JPA does not allow for that to be TREAT'ed; but really it
makes no sense to allow TREAT there, if you step back and look at it.  A
query like "select e from TREAT(MyEntity as MySub) e" makes no sense; its
ultimately just the same as "select e from MySub e".  Arguably I guess it
*could* make some sense *if* MySub is a sub MappedSuperclass since
technically JPA also allows only entities as query roots (HQL does not have
this restriction).

As discussed above, assuming that "someProp" is a persistent property
defined on MySub, this is a perfectly valid HQL: "select e.someProp from
MyEntity e".  It is not however valid JPQL; in JPQL you'd have to say: "select
treat( e as MySub).someProp from MyEntity e"

All that said, the inability to dereference a TREAT'ed path (the
".property" part) would be a bug, and tbh I am surprised that you say this
does not work with Hibernate.  Which of your tests show this not working?
Is there a related Jira?

>   * Missing support for a special case of treat join which apparently
>     was overseen by the JPA spec which would be "TREAT(TREAT(rootPath AS
>     Subtype).relation AS RelationSubtype)". Eclipselink not only allows
>     this syntax but for inner joins also implements the appropriate
>     logic. I am thinking that this might even work in hibernate without
>     the inner treat, but would fail for attributes with same names.

Do you mean specifically the nesting of TREAT operators?  Again, I am
surprised that this would not work.  Tests?  Jira?

>   * Joins for supertypes and conditions on discriminators are not aware
>     of the join type actually used in the treated join => left join
>     should cascade up the type hierarchy and discriminator checks should
>     contain OR DTYPE IS NULL for left join semantics. If treats are in
>     subconditions the DTYPE checks might even be wrong

This "restriction" aspect is honestly Hibernate's biggest shortcoming in
its TREAT support.  Hibernate's "SQL generation machinery" simply was not
built to support this.  At all.  I hacked together some support for TREAT
on top of that legacy "SQL generation machinery", but it is very limited.

This aspect is specifically what we have discussed in terms of TREAT
support improvements in SQM.  Keeping track of where the TREAT occurs so
that we can later know how to properly handle it, whether that means we can
skip parts of the inheritance join tree or maybe need to render some extra
WHERE-clause restrictions...

>   * Joins are generated for the whole hierarchy not only the types that
>     are treated => this might seem to be "just" a performance problem,
>     but IMO actually is a correctness problem as it makes a difference
>     if you get e.g. 2 rows or 2 * TYPES rows especially for count queries.

Generating the joins for the whole hierarchy is important for HQL's
implicit downcasting support.  However, that said, there are attempts to
restrict that already in place.  IIRC this only works for TREATs that occur
in the FROM-clause specifically.  However, I thought that other TREAT
operations were still handled in terms of adding restrictions when
dictated.  So this might be specific to certain circumstances.

Again, which specific tests show this?  Jira?


Some specifics of the plan...

First, internally within the query AST I want to make sure that we model
*all* downcasts whether they are implicit or explicit.  In other words, in
terms of AST both of these queries would result in the same structure:

   1. select e.someProp from MyEntity
   2. select treat(e as MySub).someProp from MyEntity

Secondly, we need to keep track of various pieces of information pertaining
to a downcast
(see org.hibernate.sqm.query.from.Downcast/org.hibernate.sqm.query.from.Downcastable).
This includes info like:

   1. The downcast "target"
   2. The context in which the downcast occurred
   3. Any/all contexts in which the downcast is used.

The last 2 items there would specifically lead to:

   - Which specific inheritance joins are needed - and may indicate join
   - Any extra restrictions we may need to add

More information about the hibernate-dev mailing list