From 90b1cfe2c366192a822a088159afc942e167bdba Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 27 Oct 2022 14:09:05 +0200 Subject: [PATCH] HHH-15634 Lazy basic property does not get updated on change: inline dirty checking, lazy basic properties are not upated when set to null --- .../InlineDirtyCheckerEqualsHelper.java | 132 ++++++++++++++++++ .../bytebuddy/InlineDirtyCheckingHandler.java | 101 +++++++++++--- 2 files changed, 210 insertions(+), 23 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckerEqualsHelper.java diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckerEqualsHelper.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckerEqualsHelper.java new file mode 100644 index 0000000000..8be0d90872 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckerEqualsHelper.java @@ -0,0 +1,132 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.bytecode.enhance.internal.bytebuddy; + +import java.util.Objects; + +import org.hibernate.engine.spi.PersistentAttributeInterceptable; +import org.hibernate.engine.spi.PersistentAttributeInterceptor; + +public final class InlineDirtyCheckerEqualsHelper { + + public static boolean areEquals( + PersistentAttributeInterceptable persistentAttributeInterceptable, + String fieldName, + Object a, + Object b) { + final PersistentAttributeInterceptor persistentAttributeInterceptor = persistentAttributeInterceptable.$$_hibernate_getInterceptor(); + if ( persistentAttributeInterceptor != null + && !persistentAttributeInterceptor.getInitializedLazyAttributeNames().contains( fieldName ) ) { + return false; + } + return Objects.deepEquals( a, b ); + } + + public static boolean areEquals( + PersistentAttributeInterceptable persistentAttributeInterceptable, + String fieldName, + boolean a, + boolean b) { + final PersistentAttributeInterceptor persistentAttributeInterceptor = persistentAttributeInterceptable.$$_hibernate_getInterceptor(); + if ( persistentAttributeInterceptor != null + && !persistentAttributeInterceptor.getInitializedLazyAttributeNames().contains( fieldName ) ) { + return false; + } + return a == b; + } + + public static boolean areEquals( + PersistentAttributeInterceptable persistentAttributeInterceptable, + String fieldName, + byte a, + byte b) { + final PersistentAttributeInterceptor persistentAttributeInterceptor = persistentAttributeInterceptable.$$_hibernate_getInterceptor(); + if ( persistentAttributeInterceptor != null + && !persistentAttributeInterceptor.getInitializedLazyAttributeNames().contains( fieldName ) ) { + return false; + } + return a == b; + } + + public static boolean areEquals( + PersistentAttributeInterceptable persistentAttributeInterceptable, + String fieldName, + short a, + short b) { + final PersistentAttributeInterceptor persistentAttributeInterceptor = persistentAttributeInterceptable.$$_hibernate_getInterceptor(); + if ( persistentAttributeInterceptor != null + && !persistentAttributeInterceptor.getInitializedLazyAttributeNames().contains( fieldName ) ) { + return false; + } + return a == b; + } + + public static boolean areEquals( + PersistentAttributeInterceptable persistentAttributeInterceptable, + String fieldName, + char a, + char b) { + final PersistentAttributeInterceptor persistentAttributeInterceptor = persistentAttributeInterceptable.$$_hibernate_getInterceptor(); + if ( persistentAttributeInterceptor != null + && !persistentAttributeInterceptor.getInitializedLazyAttributeNames().contains( fieldName ) ) { + return false; + } + return a == b; + } + + public static boolean areEquals( + PersistentAttributeInterceptable persistentAttributeInterceptable, + String fieldName, + int a, + int b) { + final PersistentAttributeInterceptor persistentAttributeInterceptor = persistentAttributeInterceptable.$$_hibernate_getInterceptor(); + if ( persistentAttributeInterceptor != null + && !persistentAttributeInterceptor.getInitializedLazyAttributeNames().contains( fieldName ) ) { + return false; + } + return a == b; + } + + public static boolean areEquals( + PersistentAttributeInterceptable persistentAttributeInterceptable, + String fieldName, + long a, + long b) { + final PersistentAttributeInterceptor persistentAttributeInterceptor = persistentAttributeInterceptable.$$_hibernate_getInterceptor(); + if ( persistentAttributeInterceptor != null + && !persistentAttributeInterceptor.getInitializedLazyAttributeNames().contains( fieldName ) ) { + return false; + } + return a == b; + } + + public static boolean areEquals( + PersistentAttributeInterceptable persistentAttributeInterceptable, + String fieldName, + float a, + float b) { + final PersistentAttributeInterceptor persistentAttributeInterceptor = persistentAttributeInterceptable.$$_hibernate_getInterceptor(); + if ( persistentAttributeInterceptor != null + && !persistentAttributeInterceptor.getInitializedLazyAttributeNames().contains( fieldName ) ) { + return false; + } + return a == b; + } + + public static boolean areEquals( + PersistentAttributeInterceptable persistentAttributeInterceptable, + String fieldName, + double a, + double b) { + final PersistentAttributeInterceptor persistentAttributeInterceptor = persistentAttributeInterceptable.$$_hibernate_getInterceptor(); + if ( persistentAttributeInterceptor != null + && !persistentAttributeInterceptor.getInitializedLazyAttributeNames().contains( fieldName ) ) { + return false; + } + return a == b; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckingHandler.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckingHandler.java index 941653c2a7..4330673685 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckingHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckingHandler.java @@ -15,6 +15,7 @@ import jakarta.persistence.Id; import org.hibernate.bytecode.enhance.internal.bytebuddy.EnhancerImpl.AnnotatedFieldDescription; import org.hibernate.bytecode.enhance.spi.EnhancerConstants; +import org.hibernate.engine.spi.PersistentAttributeInterceptable; import net.bytebuddy.ClassFileVersion; import net.bytebuddy.asm.Advice; @@ -36,11 +37,17 @@ final class InlineDirtyCheckingHandler implements Implementation, ByteCodeAppend private final TypeDescription managedCtClass; private final FieldDescription.InDefinedShape persistentField; + private final boolean applyLazyCheck; - private InlineDirtyCheckingHandler(Implementation delegate, TypeDescription managedCtClass, FieldDescription.InDefinedShape persistentField) { + private InlineDirtyCheckingHandler( + Implementation delegate, + TypeDescription managedCtClass, + FieldDescription.InDefinedShape persistentField, + boolean applyLazyCheck) { this.delegate = delegate; this.managedCtClass = managedCtClass; this.persistentField = persistentField; + this.applyLazyCheck = applyLazyCheck; } static Implementation wrap( @@ -57,8 +64,12 @@ final class InlineDirtyCheckingHandler implements Implementation, ByteCodeAppend && !persistentField.hasAnnotation( EmbeddedId.class ) && !( persistentField.getType().asErasure().isAssignableTo( Collection.class ) && enhancementContext.isMappedCollection( persistentField ) ) ) { - implementation = new InlineDirtyCheckingHandler( implementation, managedCtClass, - persistentField.asDefined() ); + implementation = new InlineDirtyCheckingHandler( + implementation, + managedCtClass, + persistentField.asDefined(), + enhancementContext.hasLazyLoadableAttributes( managedCtClass ) + ); } if ( enhancementContext.isCompositeClass( persistentField.getType().asErasure() ) @@ -99,6 +110,11 @@ final class InlineDirtyCheckingHandler implements Implementation, ByteCodeAppend Context implementationContext, MethodDescription instrumentedMethod) { // if (arg != field) { + + if ( applyLazyCheck ) { + methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 ); + methodVisitor.visitLdcInsn( persistentField.getName() ); + } methodVisitor.visitVarInsn( Type.getType( persistentField.getType().asErasure().getDescriptor() ).getOpcode( Opcodes.ILOAD ), 1 ); methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 ); if ( persistentField.getDeclaringType().asErasure().equals( managedCtClass ) ) { @@ -119,30 +135,69 @@ final class InlineDirtyCheckingHandler implements Implementation, ByteCodeAppend ); } int branchCode; - if ( persistentField.getType().isPrimitive() ) { - if ( persistentField.getType().represents( long.class ) ) { - methodVisitor.visitInsn( Opcodes.LCMP ); - } - else if ( persistentField.getType().represents( float.class ) ) { - methodVisitor.visitInsn( Opcodes.FCMPL ); - } - else if ( persistentField.getType().represents( double.class ) ) { - methodVisitor.visitInsn( Opcodes.DCMPL ); + if ( applyLazyCheck ) { + if ( persistentField.getType().isPrimitive() ) { + methodVisitor.visitMethodInsn( + Opcodes.INVOKESTATIC, + Type.getInternalName( InlineDirtyCheckerEqualsHelper.class ), + "areEquals", + Type.getMethodDescriptor( + Type.getType( boolean.class ), + Type.getType( PersistentAttributeInterceptable.class ), + Type.getType( String.class ), + Type.getType( persistentField.getDescriptor() ), + Type.getType( persistentField.getDescriptor() ) + ), + false + ); } else { - methodVisitor.visitInsn( Opcodes.ISUB ); + methodVisitor.visitMethodInsn( + Opcodes.INVOKESTATIC, + Type.getInternalName( InlineDirtyCheckerEqualsHelper.class ), + "areEquals", + Type.getMethodDescriptor( + Type.getType( boolean.class ), + Type.getType( PersistentAttributeInterceptable.class ), + Type.getType( String.class ), + Type.getType( Object.class ), + Type.getType( Object.class ) + ), + false + ); } - branchCode = Opcodes.IFEQ; + branchCode = Opcodes.IFNE; } else { - methodVisitor.visitMethodInsn( - Opcodes.INVOKESTATIC, - Type.getInternalName( Objects.class ), - "deepEquals", - Type.getMethodDescriptor( Type.getType( boolean.class ), Type.getType( Object.class ), Type.getType( Object.class ) ), - false - ); - branchCode = Opcodes.IFNE; + if ( persistentField.getType().isPrimitive() ) { + if ( persistentField.getType().represents( long.class ) ) { + methodVisitor.visitInsn( Opcodes.LCMP ); + } + else if ( persistentField.getType().represents( float.class ) ) { + methodVisitor.visitInsn( Opcodes.FCMPL ); + } + else if ( persistentField.getType().represents( double.class ) ) { + methodVisitor.visitInsn( Opcodes.DCMPL ); + } + else { + methodVisitor.visitInsn( Opcodes.ISUB ); + } + branchCode = Opcodes.IFEQ; + } + else { + methodVisitor.visitMethodInsn( + Opcodes.INVOKESTATIC, + Type.getInternalName( Objects.class ), + "deepEquals", + Type.getMethodDescriptor( + Type.getType( boolean.class ), + Type.getType( Object.class ), + Type.getType( Object.class ) + ), + false + ); + branchCode = Opcodes.IFNE; + } } Label skip = new Label(); methodVisitor.visitJumpInsn( branchCode, skip ); @@ -161,7 +216,7 @@ final class InlineDirtyCheckingHandler implements Implementation, ByteCodeAppend if ( implementationContext.getClassFileVersion().isAtLeast( ClassFileVersion.JAVA_V6 ) ) { methodVisitor.visitFrame( Opcodes.F_SAME, 0, null, 0, null ); } - return new Size( 1 + 2 * persistentField.getType().asErasure().getStackSize().getSize(), instrumentedMethod.getStackSize() ); + return new Size( 3 + 2 * persistentField.getType().asErasure().getStackSize().getSize(), instrumentedMethod.getStackSize() ); } @Override