HHH-17947 Bidirectional association management shouldn't ignore maintaining inverse lazy objects

This commit is contained in:
Christian Beikov 2024-04-12 11:31:32 +02:00
parent 6112a1809e
commit 4b863b180e
1 changed files with 18 additions and 46 deletions

View File

@ -357,9 +357,7 @@ class CodeTemplates {
static class OneToOneHandler { static class OneToOneHandler {
@Advice.OnMethodEnter @Advice.OnMethodEnter
static void enter(@FieldValue Object field, @Advice.Argument(0) Object argument, @InverseSide boolean inverseSide) { static void enter(@FieldValue Object field, @Advice.Argument(0) Object argument, @InverseSide boolean inverseSide) {
// Unset the inverse attribute, which possibly initializes the old value, if ( getterSelf() != null ) {
// only if this is the inverse side, or the old value is already initialized
if ( ( inverseSide || Hibernate.isInitialized( field ) ) && getterSelf() != null ) {
// We copy the old value, then set the field to null which we must do before // We copy the old value, then set the field to null which we must do before
// unsetting the inverse attribute, as we'd otherwise run into a stack overflow situation // unsetting the inverse attribute, as we'd otherwise run into a stack overflow situation
// The field is writable, so setting it to null here is actually a field write. // The field is writable, so setting it to null here is actually a field write.
@ -371,9 +369,7 @@ class CodeTemplates {
@Advice.OnMethodExit @Advice.OnMethodExit
static void exit(@Advice.This Object self, @Advice.Argument(0) Object argument, @InverseSide boolean inverseSide) { static void exit(@Advice.This Object self, @Advice.Argument(0) Object argument, @InverseSide boolean inverseSide) {
// Update the inverse attribute, which possibly initializes the argument value, if ( argument != null && getter( argument ) != self ) {
// only if this is the inverse side, or the argument value is already initialized
if ( argument != null && ( inverseSide || Hibernate.isInitialized( argument ) ) && getter( argument ) != self ) {
setterSelf( argument, self ); setterSelf( argument, self );
} }
} }
@ -402,13 +398,10 @@ class CodeTemplates {
static class OneToManyOnCollectionHandler { static class OneToManyOnCollectionHandler {
@Advice.OnMethodEnter @Advice.OnMethodEnter
static void enter(@FieldValue Collection<?> field, @Advice.Argument(0) Collection<?> argument, @InverseSide boolean inverseSide) { static void enter(@FieldValue Collection<?> field, @Advice.Argument(0) Collection<?> argument, @InverseSide boolean inverseSide) {
// If this is the inverse side or the old collection is already initialized, if ( getterSelf() != null ) {
// we must unset the respective ManyToOne of the old collection elements,
// because only the owning side is responsible for persisting the state.
if ( ( inverseSide || Hibernate.isInitialized( field ) ) && getterSelf() != null ) {
Object[] array = field.toArray(); Object[] array = field.toArray();
for ( int i = 0; i < array.length; i++ ) { for ( int i = 0; i < array.length; i++ ) {
if ( ( inverseSide || Hibernate.isInitialized( array[i] ) ) && ( argument == null || !argument.contains( array[i] ) ) ) { if ( argument == null || !argument.contains( array[i] ) ) {
setterNull( array[i], null ); setterNull( array[i], null );
} }
} }
@ -417,13 +410,10 @@ class CodeTemplates {
@Advice.OnMethodExit @Advice.OnMethodExit
static void exit(@Advice.This Object self, @Advice.Argument(0) Collection<?> argument, @InverseSide boolean inverseSide) { static void exit(@Advice.This Object self, @Advice.Argument(0) Collection<?> argument, @InverseSide boolean inverseSide) {
// If this is the inverse side or the new collection is already initialized, if ( argument != null ) {
// we must set the respective ManyToOne on the new collection elements,
// because only the owning side is responsible for persisting the state.
if ( argument != null && ( inverseSide || Hibernate.isInitialized( argument ) ) ) {
Object[] array = argument.toArray(); Object[] array = argument.toArray();
for ( int i = 0; i < array.length; i++ ) { for ( int i = 0; i < array.length; i++ ) {
if ( ( inverseSide || Hibernate.isInitialized( array[i] ) ) && getter( array[i] ) != self ) { if ( getter( array[i] ) != self ) {
setterSelf( array[i], self ); setterSelf( array[i], self );
} }
} }
@ -454,14 +444,10 @@ class CodeTemplates {
static class OneToManyOnMapHandler { static class OneToManyOnMapHandler {
@Advice.OnMethodEnter @Advice.OnMethodEnter
static void enter(@FieldValue Map<?, ?> field, @Advice.Argument(0) Map<?, ?> argument, @InverseSide boolean inverseSide) { static void enter(@FieldValue Map<?, ?> field, @Advice.Argument(0) Map<?, ?> argument, @InverseSide boolean inverseSide) {
// If this is the inverse side or the old collection is already initialized, if ( getterSelf() != null ) {
// we must unset the respective ManyToOne of the old collection elements,
// because only the owning side is responsible for persisting the state.
if ( ( inverseSide || Hibernate.isInitialized( field ) ) && getterSelf() != null ) {
Object[] array = field.values().toArray(); Object[] array = field.values().toArray();
for ( int i = 0; i < array.length; i++ ) { for ( int i = 0; i < array.length; i++ ) {
if ( ( inverseSide || Hibernate.isInitialized( array[i] ) ) if ( argument == null || !argument.containsValue( array[i] ) ) {
&& ( argument == null || !argument.containsValue( array[i] ) ) ) {
setterNull( array[i], null ); setterNull( array[i], null );
} }
} }
@ -470,13 +456,10 @@ class CodeTemplates {
@Advice.OnMethodExit @Advice.OnMethodExit
static void exit(@Advice.This Object self, @Advice.Argument(0) Map<?, ?> argument, @InverseSide boolean inverseSide) { static void exit(@Advice.This Object self, @Advice.Argument(0) Map<?, ?> argument, @InverseSide boolean inverseSide) {
// If this is the inverse side or the new collection is already initialized, if ( argument != null ) {
// we must set the respective ManyToOne on the new collection elements,
// because only the owning side is responsible for persisting the state.
if ( argument != null && ( inverseSide || Hibernate.isInitialized( argument ) ) ) {
Object[] array = argument.values().toArray(); Object[] array = argument.values().toArray();
for ( int i = 0; i < array.length; i++ ) { for ( int i = 0; i < array.length; i++ ) {
if ( ( inverseSide || Hibernate.isInitialized( array[i] ) ) && getter( array[i] ) != self ) { if ( getter( array[i] ) != self ) {
setterSelf( array[i], self ); setterSelf( array[i], self );
} }
} }
@ -507,8 +490,7 @@ class CodeTemplates {
static class ManyToOneHandler { static class ManyToOneHandler {
@Advice.OnMethodEnter @Advice.OnMethodEnter
static void enter(@Advice.This Object self, @FieldValue Object field, @BidirectionalAttribute String inverseAttribute) { static void enter(@Advice.This Object self, @FieldValue Object field, @BidirectionalAttribute String inverseAttribute) {
// This is always the owning side, so we only need to update the inverse side if the collection is initialized if ( getterSelf() != null ) {
if ( getterSelf() != null && Hibernate.isPropertyInitialized( field, inverseAttribute ) ) {
Collection<?> c = getter( field ); Collection<?> c = getter( field );
if ( c != null ) { if ( c != null ) {
c.remove( self ); c.remove( self );
@ -518,8 +500,7 @@ class CodeTemplates {
@Advice.OnMethodExit @Advice.OnMethodExit
static void exit(@Advice.This Object self, @Advice.Argument(0) Object argument, @BidirectionalAttribute String inverseAttribute) { static void exit(@Advice.This Object self, @Advice.Argument(0) Object argument, @BidirectionalAttribute String inverseAttribute) {
// This is always the owning side, so we only need to update the inverse side if the collection is initialized if ( argument != null ) {
if ( argument != null && Hibernate.isPropertyInitialized( argument, inverseAttribute ) ) {
Collection<Object> c = getter( argument ); Collection<Object> c = getter( argument );
if ( c != null && !c.contains( self ) ) { if ( c != null && !c.contains( self ) ) {
c.add( self ); c.add( self );
@ -541,14 +522,10 @@ class CodeTemplates {
static class ManyToManyHandler { static class ManyToManyHandler {
@Advice.OnMethodEnter @Advice.OnMethodEnter
static void enter(@Advice.This Object self, @FieldValue Collection<?> field, @Advice.Argument(0) Collection<?> argument, @InverseSide boolean inverseSide, @BidirectionalAttribute String bidirectionalAttribute) { static void enter(@Advice.This Object self, @FieldValue Collection<?> field, @Advice.Argument(0) Collection<?> argument, @InverseSide boolean inverseSide, @BidirectionalAttribute String bidirectionalAttribute) {
// If this is the inverse side or the old collection is already initialized, if ( getterSelf() != null ) {
// we must remove self from the respective old collection elements inverse collections,
// because only the owning side is responsible for persisting the state.
if ( ( inverseSide || Hibernate.isInitialized( field ) ) && getterSelf() != null ) {
Object[] array = field.toArray(); Object[] array = field.toArray();
for ( int i = 0; i < array.length; i++ ) { for ( int i = 0; i < array.length; i++ ) {
if ( ( inverseSide || Hibernate.isPropertyInitialized( array[i], bidirectionalAttribute ) ) if ( argument == null || !argument.contains( array[i] ) ) {
&& ( argument == null || !argument.contains( array[i] ) ) ) {
getter( array[i] ).remove( self ); getter( array[i] ).remove( self );
} }
} }
@ -557,17 +534,12 @@ class CodeTemplates {
@Advice.OnMethodExit @Advice.OnMethodExit
static void exit(@Advice.This Object self, @Advice.Argument(0) Collection<?> argument, @InverseSide boolean inverseSide, @BidirectionalAttribute String bidirectionalAttribute) { static void exit(@Advice.This Object self, @Advice.Argument(0) Collection<?> argument, @InverseSide boolean inverseSide, @BidirectionalAttribute String bidirectionalAttribute) {
// If this is the inverse side or the new collection is already initialized, if ( argument != null ) {
// we must add self to the respective new collection elements inverse collections,
// because only the owning side is responsible for persisting the state.
if ( argument != null && ( inverseSide || Hibernate.isInitialized( argument ) ) ) {
Object[] array = argument.toArray(); Object[] array = argument.toArray();
for ( Object array1 : array ) { for ( Object array1 : array ) {
if ( inverseSide || Hibernate.isPropertyInitialized( array1, bidirectionalAttribute ) ) { Collection<Object> c = getter( array1 );
Collection<Object> c = getter( array1 ); if ( c != null && !c.contains( self ) ) {
if ( c != null && !c.contains( self ) ) { c.add( self );
c.add( self );
}
} }
} }
} }