[hibernate-dev] Treat support

Christian Beikov christian.beikov at gmail.com
Tue Aug 23 05:07:22 EDT 2016


so I finished my work on the testsuite 
<https://github.com/beikov/jpa-treat-variations> for the treat operator. 
I also started a forum topic for this discussion: 
If anyone is interested, I also started a discussion in the EclipeLink 
forum about that: 

I hope we can all agree on an interpretation of how treat should work 
and hopefully get a clarification into the spec. Or at least some TCK 
tests, as it doesn't seem to assert anything useful right now.


Am 09.08.2016 um 13:14 schrieb Christian Beikov:
> 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? 
> https://github.com/beikov/jpa-treat-variations/blob/master/src/test/java/jpa/test/TreatVariationsTest.java
> 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