-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
HHH-18714 add support for inheritance in entity graph #9088
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
.../org/hibernate/orm/test/entitygraph/named/multiple/NamedEntityGraphsWithInheritanceTest.java
Outdated
Show resolved
Hide resolved
...te-core/src/test/java/org/hibernate/orm/test/entitygraph/EntityGraphWithInheritanceTest.java
Outdated
Show resolved
Hide resolved
|
@gavinking I don't understand why h2 build fail. Do I need to relaunch the build for h2? Is it possible that this is coming from Edit: Its work now |
Maybe I had spaces instead of tabs in my suggestion. |
I think it was me, I had forgotten some unused imports. |
|
Hello @gavinking What do you think of this change ? Should I plan to add documentation on how it works, or is it too risky ? |
| super(original, mutable); | ||
| super( original, mutable ); | ||
| this.name = name; | ||
| this.subclassSubgraphs = new HashMap<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should IMO be instantiated lazily.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| PersistentAttribute<?, ?> attribute = managedType.findAttributeInSuperTypes( attributeName ); | ||
|
|
||
| if ( attribute == null ) { | ||
| attribute = managedType.findSubTypesAttribute( attributeName ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please revert this. Implicitly looking into subtypes is not an option. Users will have to explicitly cast the graph to "treat" it and can then access subtype attributes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello @beikov ,
Sorry for the delay. I do understand. I have the impression that the difficulty here is that if I remove this as well as the graph merge below, the subgraphs on which a type is specified will be ignored.
This is because when fields are traversed in StandardEntityGraphTraversalStateImpl, all subtypes attributes are represented as if they belonged to the parent class.
Today on this line, when we have sub-graphs associated with sub-types, we ignore them.
In the case below, for example, the ‘course_sub_graph sub-graph will be ignored:
@NamedEntityGraph(
name = "exemple",
subgraphs = {
@NamedSubgraph(
name = "course_sub_graph",
type = PayingCourse.class,
attributeNodes = { @NamedAttributeNode("moneyReceiver") }
)
}
)
@DiscriminatorValue("student")
public static class Student {
@Id
Long id;
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
List<Course> courses;
}
@Entity(name = "Course")
@DiscriminatorColumn(name = "course_type", discriminatorType = DiscriminatorType.STRING)
public static abstract class Course {
@Id
Long id;
String name;
}
@Entity(name = "FreeCourse")
public static class FreeCourse extends Course {
}
@Entity(name = "PayingCourse")
@DiscriminatorValue("paying")
public static class PayingCourse extends Course {
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
Teacher moneyReceiver;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Asanio06 so what you're saying is that the explicit class = PayingCourse.class is doing some sort of a "treat".
The spec is for this stuff is extremely hard to understand, and I wasn't around when it was added, so I don't know for sure what it's supposed to mean, however, section 10.3.3 and the following Javadoc would appear to support your interpretation:
public @interface NamedSubgraph {
/**
* (Required) The name of the subgraph as referenced from a
* NamedAttributeNode element.
*/
String name();
/**
* (Optional) The type represented by this subgraph. The element
* must be specified when this subgraph is extending a definition
* on behalf of a subclass.
*/
Class<?> type() default void.class;
/**
* (Required) The list of the attributes of the class that must
* be included. If the named subgraph corresponds to a subclass
* of the class referenced by the corresponding attribute node,
* then only subclass-specific attributes are listed.
*/
NamedAttributeNode[] attributeNodes();
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gavinking
Yes, that's exactly it, it's not very well documented.
I have the impression that to support this inheritance functionality on subgraphs, it is necessary to go to the attributes of child classes on these function.
If not, currentGraphContext.findAttributeNode will always return null. And if we don't merge the different sub-graphs, currentGraphContext will correspond to only 1 of the sub-graphs and the others will be ignored.
I have the impression that the other way requires changing the way you browse an entity's attributes to find out if they are to be fetched.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, also read section 10.3.3 of the spec, which tends to imply that you are only allowed to specify directly-declared members of PayingCourse.class in that @NamedSubgraph.
So perhaps that rule helps avoid searching through hierarchies?
The
NamedSubgraphannotation is used to
further define an attribute node. It is referenced by its name from the
subgraphorkeySubgraphelement of aNamedAttributeNodeelement.The
nameelement is the name used to
reference the subgraph from aNamedAttributeNodedefinition. In the
case of entity inheritance, multiple subgraph elements have the same
name.The
typeelement must be specified when the
subgraph corresponds to a subclass of the entity type corresponding to
the referencing attribute node.The
attributeNodeselement lists attributes
of the class that must be included. If the subgraph corresponds to a
subclass of the class referenced by the corresponding attribute node,
only subclass-specific attributes are listed.
Also:
The `subgraph` element is used to refer to a `NamedSubgraph` specification that further characterizes an attribute node corresponding to a managed type (entity or embeddable). The value of the `subgraph` element must correspond to the `name` used for the subgraph in the `NamedSubgraph` element. If the referenced attribute is an entity which has entity subclasses, there may be more than one `NamedSubgraph` element with this name, and the `subgraph` element is considered to refer to all of these.
Thanks for looking into this stuff, but the way, @Asanio06, I know it's a hole we have.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gavinking You're right, these details really help me in my reasoning.
Does this mean that to solve our problem we could potentially make SubGraphImpl contain a ‘subclassSubgraphs’ variable like RootGraphImpl?
So if I create 3 subgraphs of the same name but different types on the same AttributeNode, I'll have 1 subgraph that has a link to the other subgraphs.
Is this correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gavinking @beikov Thanks for all your help
I'm going to work on the changes and I'll get back to you as soon as it's done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, thanks again man!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello @gavinking @beikov ,
I removed that.
Here's a new version.
This time if I consider that each attribute node has only 1 subgraph which itself can have subclassSubgraphs.
This change generates numerous impacts that I've tried to manage as well as possible.
In the StandardEntityGraphTraversalStateImpl, I now use the declaringType of the fetchable to determine whether I should use the graph directly or whether I should use a subclassSubgraph. This is the way I've found to avoid unnecessary attribute node traversals.
| SubGraphImplementor<S> subgraph = new SubGraphImpl<>( subTypeEntityDomainType, true ); | ||
|
|
||
| subclassSubgraphs.putIfAbsent( subTypeEntityDomainType, subgraph ); | ||
|
|
||
| return subgraph; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The sub-graph needs to know about this since the javadoc of addTreatedSubgraph says:
Subclass subgraphs automatically include the specified attributes of superclass subgraphs.
also, I think it's desirable to return an existing instance:
| SubGraphImplementor<S> subgraph = new SubGraphImpl<>( subTypeEntityDomainType, true ); | |
| subclassSubgraphs.putIfAbsent( subTypeEntityDomainType, subgraph ); | |
| return subgraph; | |
| final SubGraphImplementor<?> existingSubgraph = subclassSubgraphs.get( subTypeEntityDomainType ); | |
| if ( existingSubgraph != null ) { | |
| return existingSubgraph; | |
| } | |
| final SubGraphImplementor<S> subgraph = new SubGraphImpl<>( subTypeEntityDomainType, true ); | |
| subclassSubgraphs.put( subTypeEntityDomainType, subgraph ); | |
| return subgraph; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
I've started to link the subgraph to its parent. Does this mean that I have to override the findAttributeNode methods in AbstractGraph just to combine the attributes? Or should I also override attributeMap via a getter ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello @beikov ,
I have the impression that the link with the “superclass” is already somewhat taken into account by the “findAttributeNode” method. Even so, one solution I can see to better comply with the specification would be to use the constructor that takes in the “superclass” to hydrate attrNodeMap. What do you think ?
| ); | ||
| } | ||
| if ( StringHelper.isNotEmpty( namedAttributeNode.keySubgraph() ) ) { | ||
| final SubGraphImplementor<?> subgraph = attributeNode.makeKeySubGraph(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using this method to create a sub-graph for a map-key is vital. This change won't work if a key subgraph uses a subtype subgraph with the same type as a value subgraph.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yes, sorry. I corrected it
| GraphImplementor mergedSubgraph = new SubGraphImpl<>( managedDomainType, true ); | ||
|
|
||
| for ( GraphImplementor subgraph : subgraphMap.values() ) { | ||
| mergedSubgraph.merge( subgraph ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will not be necessary anymore once the SubGraphImpl knows of its parent like the spec requires it. See my previous comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, you're absolutely right
|
Hello @gavinking @beikov I've made the various changes, I'm having a problem with the pipeline but I don't know where it's coming from. I'm going to analyse |
|
This looks good to me. Let's see what CI says |
hibernate-core/src/main/java/org/hibernate/graph/internal/AttributeNodeImpl.java
Fixed
Show fixed
Hide fixed
...rnate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java
Fixed
Show fixed
Hide fixed
c82ceb9 to
3ba9cec
Compare
@sebersole The code build locally and all test pass but the pipeline seems to be waiting for validation before building : |
|
Hello @gavinking , any information? |
|
@Asanio06 I was waiting for @sebersole to make the call on this, because he understands that code better than me. Also @beikov should approve. |
|
@gavinking Thank you very much for your reply 🙂 |
|
Not at all, thank you for your contribution! |
|
Hello @gavinking @sebersole @beikov , Should I point to version 7 to simplify the work and not do it in 2 times? |
Yes, this should target |
|
@Asanio06 I attempted to cherry-pick this onto Would you please try getting your patch working on |
|
Thanks for your pull request! This pull request appears to follow the contribution rules. › This message was automatically generated. |
|
Hello @gavinking , It's done, I've linked it to the main branch. I'm waiting for the pipeline but the code build well locally Happy new year |
|
Thanks man, and Happy NY to you too! |
|
@Asanio06 I am really, really sorry to do this to you, and I feel real bad because you really did do good work on this one. But when I sat down and looked more closely I saw what an entropic mess we had in the So in #9526 I did a major rework of that whole package, filled in missing functionality from JPA 3.2, got rid of lots of unchecked assignments, added some assertions, and by side-effect fixed some of the bugs you fixed in this PR. I deliberately did not fix everything that's fixed by this PR, to leave you the chance to redo at least part of your fix (which will be way simpler and smaller now, I believe) and I did not push your tests. So you still have an chance to send a new PR and have it integrated with your name on the commits. Overall, I ended up going in a similar direction as you did (as I said, your work was good) but I took it quite a bit further as you'll see if you take a look. Again I apologize for this situation, I really don't like pulling the rug out from under people like this. It's just that that package has been in desperate need of a major cleanup for a long time, and I had a couple of days with not many distractions. |
|
@gavinking No worries, it was a necessary cleaning. |
|
Probably much easier for you if it's a new PR, but whatever works is fine. |
|
@gavinking I create a new PR : #9539 |

[Please describe here what your change is about]
This change enables support for inheritance with EntityGraphs as described in the JPA specification.
I've added some functions such as
addTreatedSubgraphto facilitate future integration with Hibernate 7 (Jpa 3.2).The ticket related to my proposal: https://hibernate.atlassian.net/browse/HHH-18714
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license
and can be relicensed under the terms of the LGPL v2.1 license in the future at the maintainers' discretion.
For more information on licensing, please check here.