-
Notifications
You must be signed in to change notification settings - Fork 3.6k
HHH-19542 embeddable property order #10356
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
Open
itsmoonrack
wants to merge
6
commits into
hibernate:main
Choose a base branch
from
itsmoonrack:HHH-19542-embeddable-property-order
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
33cfc7f
HHH-19542: Use either the secondary table of a component, by looking …
itsmoonrack 5308b1b
HHH-19542: Clean-up
itsmoonrack 017ebb6
HHH-19542: Clean-up
itsmoonrack 6b1aed9
HHH-19542: Fixing tests
itsmoonrack be91244
HHH-19542: Use the property.value table instead of the columns table,…
itsmoonrack e455cb3
HHH-19542: Use the property.value table instead of the columns table,…
itsmoonrack File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -221,23 +221,21 @@ public String getEntityName() { | |||||||||||||||
|
||||||||||||||||
@Override | ||||||||||||||||
public void addProperty(Property property, MemberDetails attributeMemberDetails, AnnotatedColumns columns, ClassDetails declaringClass) { | ||||||||||||||||
//Ejb3Column.checkPropertyConsistency( ); //already called earlier | ||||||||||||||||
//AnnotatedColumn.checkPropertyConsistency( ); //already called earlier | ||||||||||||||||
// Check table matches between the component and the columns | ||||||||||||||||
// if not, change the component table if no properties are set | ||||||||||||||||
// if a property is set already the core cannot support that | ||||||||||||||||
if ( columns != null ) { | ||||||||||||||||
final Table table = columns.getTable(); | ||||||||||||||||
if ( !table.equals( getTable() ) ) { | ||||||||||||||||
if ( component.getPropertySpan() == 0 ) { | ||||||||||||||||
component.setTable( table ); | ||||||||||||||||
} | ||||||||||||||||
else { | ||||||||||||||||
throw new AnnotationException( | ||||||||||||||||
"Embeddable class '" + component.getComponentClassName() | ||||||||||||||||
+ "' has properties mapped to two different tables" | ||||||||||||||||
+ " (all properties of the embeddable class must map to the same table)" | ||||||||||||||||
); | ||||||||||||||||
} | ||||||||||||||||
final Table table = property.getValue().getTable(); | ||||||||||||||||
if ( !table.equals( getTable() ) ) { | ||||||||||||||||
if ( component.getPropertySpan() == 0 ) { | ||||||||||||||||
component.setTable( table ); | ||||||||||||||||
} | ||||||||||||||||
else { | ||||||||||||||||
throw new AnnotationException( | ||||||||||||||||
"Embeddable class '" + component.getComponentClassName() | ||||||||||||||||
+ "' has properties mapped to two different tables" | ||||||||||||||||
+ " (all properties of the embeddable class must map to the same table)" | ||||||||||||||||
); | ||||||||||||||||
Comment on lines
+236
to
+238
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||
} | ||||||||||||||||
} | ||||||||||||||||
addProperty( property, attributeMemberDetails, declaringClass ); | ||||||||||||||||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
88 changes: 88 additions & 0 deletions
88
...test/java/org/hibernate/orm/test/records/RecordNestedEmbeddedWithASecondaryTableTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* Copyright Red Hat Inc. and Hibernate Authors | ||
*/ | ||
package org.hibernate.orm.test.records; | ||
|
||
import jakarta.persistence.AttributeOverride; | ||
import jakarta.persistence.Column; | ||
import jakarta.persistence.Embeddable; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.Id; | ||
import jakarta.persistence.SecondaryTable; | ||
import org.hibernate.testing.orm.junit.DomainModel; | ||
import org.hibernate.testing.orm.junit.JiraKey; | ||
import org.hibernate.testing.orm.junit.SessionFactory; | ||
import org.hibernate.testing.orm.junit.SessionFactoryScope; | ||
import org.junit.jupiter.api.BeforeAll; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
@JiraKey("HHH-19542") | ||
@DomainModel(annotatedClasses = { | ||
RecordNestedEmbeddedWithASecondaryTableTest.UserEntity.class | ||
}) | ||
@SessionFactory | ||
public class RecordNestedEmbeddedWithASecondaryTableTest { | ||
|
||
private UserEntity user; | ||
|
||
@BeforeAll | ||
public void prepare(SessionFactoryScope scope) { | ||
scope.inTransaction( session -> { | ||
Person person = new Person( new FullName( "Sylvain", "Lecoy" ), 38 ); | ||
user = new UserEntity( person ); | ||
session.persist( user ); | ||
} ); | ||
} | ||
|
||
@Test | ||
public void test(SessionFactoryScope scope) { | ||
scope.inTransaction(session -> { | ||
UserEntity entity = session.find( UserEntity.class, user.id ); | ||
assertThat( entity ).isNotNull(); | ||
assertThat( entity.id ).isEqualTo( user.id ); | ||
assertThat( entity.person ).isNotNull(); | ||
assertThat( entity.person.age ).isEqualTo( 38 ); | ||
assertThat( entity.person.fullName.firstName ).isEqualTo( "Sylvain" ); | ||
assertThat( entity.person.fullName.lastName ).isEqualTo( "Lecoy" ); | ||
}); | ||
} | ||
|
||
@Entity | ||
@SecondaryTable(name = "Person") | ||
public static class UserEntity { | ||
@Id | ||
@GeneratedValue | ||
private Integer id; | ||
private Person person; | ||
|
||
public UserEntity( | ||
final Person person) { | ||
this.person = person; | ||
} | ||
|
||
protected UserEntity() { | ||
|
||
} | ||
} | ||
|
||
@Embeddable | ||
public record Person( | ||
@AttributeOverride(name = "firstName", column = @Column(table = "Person")) | ||
@AttributeOverride(name = "lastName", column = @Column(table = "Person")) | ||
FullName fullName, | ||
@Column(table = "Person") | ||
Integer age) { | ||
|
||
} | ||
|
||
@Embeddable | ||
public record FullName( | ||
String firstName, | ||
String lastName) { | ||
|
||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
since we are not using columns at all i wonder if in term of design i should keep the logic in this method at all here ? let me know
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.
Hey @itsmoonrack, this solution looks much better!
There's one last thing that makes me wonder: if the
property.getValue().getTable()
method returns the expected result (i.e. the@AttributeOverride
's table for nested embeddables), why was it not the case in theAnnotatedColumns
?Could you please have a look at the binders logic and check if there might be something missing there? It would be great for consistency to understand why this is happening as well.
Uh oh!
There was an error while loading. Please reload this page.
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.
Hey @mbellade, thanks !
That's indeed a good question. From what I have seen earlier the property is already constructed from those columns (higher in the hierarchy call). So in effect the property contains the same informations as the AnnotatedColumns.
What I found however, is that in the special event of an embeddable in an embeddable (our nested use case), that the columns were missing (columns = null), effectively skipping the whole logic of replacing a table. I have ran the full test suit to ensure non regression and it passed. So conceptually I am a bit bugged about this AnnotatedColumns versus Property, for me, after analysing, the later contains the former;
I thought about doing the following,
columns != null ? columns.getTable() : property.getValue().getTable()
But eventually rolled back as I did not find any value of doing so.
In both cases the full test suit passed.
Please correct me if I'm wrong or if I missed something obvious. We can put back the proposed line in this comment if we are more confident with it.
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, you mean why the columns were empty in the first place ?
That's a good question, I will investigate this evening.
Uh oh!
There was an error while loading. Please reload this page.
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.
@mbellade I tried to pass the columns to the EmbeddableBinder.createEmbeddedProperty to see what happen. Now the
AnnotatedColumns
are correctly passed, however thegetTable()
resolve to thecomponent.getTable()
, at this stage, thePerson
component getTable() returns UserEntity, while the property.getValue().getTable() returns Person. The later being correct.So we cannot blatantly passes the columns to the PropertyBinder.
I am also reading that when we have an underlying value we use the
makePropertyAndBind()
, instead of themakePropertyValueAndBind()
//used when the value is provided and the binding is done elsewhere
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 @mbellade, the fact that the columns are null is because in the method
createEmbeddedProperty()
from theEmbeddableBinder
simple don't set columns at all beforemakePropertyAndBind()
is called. But it looks like this is by design since "the value is provided and the binding is done elsewhere".From now since the value is provided, I feel that the code proposed in this PR does solve our issue.
Do you think its acceptable or you want to dig further ? I feel that the changeset would be larger if we go this other route and my (limited) knowledge is impeding me from finding a satisfactory solution other than that.
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 follow this discussion for the last proposition @gavinking
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.
What happens when the column mappings for different fields disagree on the mapped table (via
@AttributeOverride
or otherwise) ?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 from what i've seen, when I remove the first column
@AttributeOverride
mapping, to have a disagrement, we have the exception saying all properties of the component must map to the same table.The propert.getValue().getTable() already resolve to the correct table because the overrides are already made at this point.
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 tried to understand the binding logic upstream but just ended not knowing what to do to solve the issue of having columns null here.