HHH-17418 Change field reader for private final fields

This commit is contained in:
marko-bekhta 2023-11-10 18:39:35 +01:00 committed by Christian Beikov
parent ef4609baad
commit 638e8b857a
2 changed files with 131 additions and 48 deletions

View File

@ -54,6 +54,11 @@ abstract class FieldReaderAppender implements ByteCodeAppender {
TypeDescription dispatcherType = persistentFieldAsDefined.getType().isPrimitive() TypeDescription dispatcherType = persistentFieldAsDefined.getType().isPrimitive()
? persistentFieldAsDefined.getType().asErasure() ? persistentFieldAsDefined.getType().asErasure()
: TypeDescription.OBJECT; : TypeDescription.OBJECT;
// From `PersistentAttributeTransformer`:
// Final fields will only be written to from the constructor,
// so there's no point trying to replace final field writes with a method call.
// as a result if a field is final then there will be no write method, and we don't want to have this block:
if ( !persistentField.asDefined().isFinal() ) {
// if ( this.$$_hibernate_getInterceptor() != null ) // if ( this.$$_hibernate_getInterceptor() != null )
methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 ); methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 );
methodVisitor.visitMethodInsn( methodVisitor.visitMethodInsn(
@ -80,6 +85,7 @@ abstract class FieldReaderAppender implements ByteCodeAppender {
methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 ); methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 );
methodVisitor.visitLdcInsn( persistentFieldAsDefined.getName() ); methodVisitor.visitLdcInsn( persistentFieldAsDefined.getName() );
methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 ); methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 );
fieldRead( methodVisitor ); fieldRead( methodVisitor );
methodVisitor.visitMethodInsn( methodVisitor.visitMethodInsn(
Opcodes.INVOKEINTERFACE, Opcodes.INVOKEINTERFACE,
@ -95,7 +101,8 @@ abstract class FieldReaderAppender implements ByteCodeAppender {
); );
// field = (cast) XXX // field = (cast) XXX
if ( !dispatcherType.isPrimitive() ) { if ( !dispatcherType.isPrimitive() ) {
methodVisitor.visitTypeInsn( Opcodes.CHECKCAST, persistentFieldAsDefined.getType().asErasure().getInternalName() ); methodVisitor.visitTypeInsn(
Opcodes.CHECKCAST, persistentFieldAsDefined.getType().asErasure().getInternalName() );
} }
fieldWrite( methodVisitor ); fieldWrite( methodVisitor );
// end if // end if
@ -103,6 +110,8 @@ abstract class FieldReaderAppender implements ByteCodeAppender {
if ( implementationContext.getClassFileVersion().isAtLeast( ClassFileVersion.JAVA_V6 ) ) { if ( implementationContext.getClassFileVersion().isAtLeast( ClassFileVersion.JAVA_V6 ) ) {
methodVisitor.visitFrame( Opcodes.F_SAME, 0, null, 0, null ); methodVisitor.visitFrame( Opcodes.F_SAME, 0, null, 0, null );
} }
}
// return field // return field
methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 ); methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 );
fieldRead( methodVisitor ); fieldRead( methodVisitor );

View File

@ -0,0 +1,74 @@
package org.hibernate.orm.test.bytecode.enhancement.superclass;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import java.time.LocalDateTime;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.hibernate.testing.orm.junit.JiraKey;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
@JiraKey("HHH-17418")
@RunWith(BytecodeEnhancerRunner.class)
public class MappedSuperclassTest extends BaseCoreFunctionalTestCase {
private static final LocalDateTime TEST_DATE_UPDATED_VALUE = LocalDateTime.of( 2023, 11, 10, 0, 0 );
private static final long TEST_ID = 1L;
@Override
public Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { MyEntity.class };
}
@Before
public void prepare() {
doInHibernate( this::sessionFactory, s -> {
MyEntity testEntity = new MyEntity();
testEntity.id = TEST_ID;
s.persist( testEntity );
} );
}
@Test
public void test() {
doInHibernate( this::sessionFactory, s -> {
MyEntity testEntity = s.get( MyEntity.class, TEST_ID );
assertThat( testEntity.value() ).isEqualTo( TEST_DATE_UPDATED_VALUE );
} );
}
@After
public void cleanup() {
doInHibernate( this::sessionFactory, s -> {
MyEntity testEntity = s.get( MyEntity.class, TEST_ID );
s.remove( testEntity );
} );
}
@MappedSuperclass
public static class MappedBase {
// field is private on purpose so that enhancer will not use field access
@Column
private final LocalDateTime updated = TEST_DATE_UPDATED_VALUE;
public LocalDateTime value() {
return updated;
}
}
@Entity
public static class MyEntity extends MappedBase {
@Id
Long id;
}
}