HHH-15634 Lazy basic property does not get updated on change: inline dirty checking, lazy basic properties are not upated when set to null

This commit is contained in:
Andrea Boriero 2022-10-27 14:09:05 +02:00 committed by Christian Beikov
parent 6d99eb1068
commit 90b1cfe2c3
2 changed files with 210 additions and 23 deletions

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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;
}
}

View File

@ -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