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/...
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(a)gmail.com <mailto:christian.beikov@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