[hibernate-dev] Treat support

Christian Beikov christian.beikov at gmail.com
Tue Aug 9 07:14:14 EDT 2016

Hello again,

I finished the first part of the test cases for root treats and 
many-to-one treats. I also tried to explain how I would expect treat to 
work and present a reduction/translation strategy to be able to reduce 
thinking just about the base cases.
Could you comment on that? 
The tests show various problems with Hibernate as well as EclipseLink. I 
will add findings from Datanucleus as soon as the relation mapping for 
table per class is supported.

Here is a collection of JIRAs for treat problems

  * https://hibernate.atlassian.net/browse/HHH-10988
  * https://hibernate.atlassian.net/browse/HHH-9345
  * https://hibernate.atlassian.net/browse/HHH-9862
  * https://hibernate.atlassian.net/browse/HHH-10768
  * https://hibernate.atlassian.net/browse/HHH-9594

The definition of the semantics according to JPA spec 4.4.9 are a bit 
strange as it does not distinguish between FROM and WHERE clause when 
mentioning filtering on the subtype.
It specifically says "Use of the TREAT operator therefore also has the 
effect of filtering on the specified type (and its subtypes) as well as 
performing the downcast" and I think this is only relevant for treats in 
the FROM clause.
Treats in the WHERE clause should not result in filtering. Depending on 
the join type which is used for the treat path, the result of the treat 
is either the subtype instance, or may be NULL in case of a left join.
Since any comparison(except NULL aware operations) with NULL will 
eventually result in FALSE according to the SQL spec, the requirement 
for the treat operator in the WHERE clause to result in FALSE for 
instances that are not a subtype is also fulfilled.
The section "... and in the case of a restriction, the associated 
predicate is false." didn't consider NULL aware operations so I think 
this is an oversight.

Am 04.08.2016 um 15:52 schrieb Steve Ebersole:
> 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.
> <background>
> 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.
> </background>
> 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 <mailto:christian.beikov at gmail.com>> wrote:
>       * 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 type
>   * Any extra restrictions we may need to add

More information about the hibernate-dev mailing list