HHH-16832 Reproducer
As far as I can see, the problem is in:
5a63d8758a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckingHandler.java (L88-L90)
`GetterMapping` doesn't seem to work correctly when the getter is
defined in the superclass with a more abstract type.
This commit is contained in:
parent
5d63218deb
commit
d65b74c934
|
@ -0,0 +1,189 @@
|
||||||
|
/*
|
||||||
|
* 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.orm.test.bytecode.enhance.internal.bytebuddy;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||||
|
import static org.assertj.core.extractor.Extractors.resultOf;
|
||||||
|
import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.ENTITY_ENTRY_FIELD_NAME;
|
||||||
|
import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.ENTITY_ENTRY_GETTER_NAME;
|
||||||
|
import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME;
|
||||||
|
import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.NEXT_FIELD_NAME;
|
||||||
|
import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.NEXT_GETTER_NAME;
|
||||||
|
import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.NEXT_SETTER_NAME;
|
||||||
|
import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX;
|
||||||
|
import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX;
|
||||||
|
import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.PREVIOUS_FIELD_NAME;
|
||||||
|
import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.PREVIOUS_GETTER_NAME;
|
||||||
|
import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.PREVIOUS_SETTER_NAME;
|
||||||
|
import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.TRACKER_CLEAR_NAME;
|
||||||
|
import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.TRACKER_COMPOSITE_CLEAR_OWNER;
|
||||||
|
import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME;
|
||||||
|
import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.TRACKER_COMPOSITE_SET_OWNER;
|
||||||
|
import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.TRACKER_FIELD_NAME;
|
||||||
|
import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.TRACKER_GET_NAME;
|
||||||
|
import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.TRACKER_HAS_CHANGED_NAME;
|
||||||
|
import static org.hibernate.bytecode.enhance.spi.EnhancerConstants.TRACKER_SUSPEND_NAME;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import org.hibernate.bytecode.enhance.internal.tracker.CompositeOwnerTracker;
|
||||||
|
import org.hibernate.bytecode.enhance.internal.tracker.SimpleFieldTracker;
|
||||||
|
|
||||||
|
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||||
|
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
|
||||||
|
import org.hibernate.testing.orm.junit.JiraKey;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Embeddable;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
|
||||||
|
@RunWith(BytecodeEnhancerRunner.class)
|
||||||
|
@EnhancementOptions(inlineDirtyChecking = true)
|
||||||
|
public class DirtyCheckingWithEmbeddableAndNonVisibleGenericMappedSuperclassTest {
|
||||||
|
|
||||||
|
@JiraKey("HHH-16832")
|
||||||
|
public void shouldNotBreakConstructor() {
|
||||||
|
// This is the actual reproducer for HHH-16832,
|
||||||
|
// other method are just to be consistent with tests in the same package.
|
||||||
|
assertThatCode( () -> new MyEntity( 0, "Magic the Gathering" ) )
|
||||||
|
.doesNotThrowAnyException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldDeclareFieldsInEntityClass() {
|
||||||
|
assertThat( MyEntity.class )
|
||||||
|
.hasDeclaredFields( ENTITY_ENTRY_FIELD_NAME, PREVIOUS_FIELD_NAME, NEXT_FIELD_NAME, TRACKER_FIELD_NAME );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldDeclareMethodsInEntityClass() {
|
||||||
|
assertThat( MyEntity.class )
|
||||||
|
.hasDeclaredMethods( PERSISTENT_FIELD_READER_PREFIX + "id", PERSISTENT_FIELD_WRITER_PREFIX + "id" )
|
||||||
|
.hasDeclaredMethods( PERSISTENT_FIELD_READER_PREFIX + "embedded", PERSISTENT_FIELD_WRITER_PREFIX + "embedded" )
|
||||||
|
.hasDeclaredMethods( ENTITY_INSTANCE_GETTER_NAME, ENTITY_ENTRY_GETTER_NAME )
|
||||||
|
.hasDeclaredMethods( PREVIOUS_GETTER_NAME, PREVIOUS_SETTER_NAME, NEXT_GETTER_NAME, NEXT_SETTER_NAME )
|
||||||
|
.hasDeclaredMethods( TRACKER_HAS_CHANGED_NAME, TRACKER_CLEAR_NAME, TRACKER_SUSPEND_NAME, TRACKER_GET_NAME );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldDeclareFieldsInEmbeddedClass() {
|
||||||
|
assertThat( MyEmbeddable.class )
|
||||||
|
.hasDeclaredFields( TRACKER_COMPOSITE_FIELD_NAME );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldDeclareMethodsInEmbeddedClass() {
|
||||||
|
assertThat( MyEmbeddable.class )
|
||||||
|
.hasDeclaredMethods( PERSISTENT_FIELD_READER_PREFIX + "text", PERSISTENT_FIELD_WRITER_PREFIX + "text" )
|
||||||
|
.hasDeclaredMethods( TRACKER_COMPOSITE_SET_OWNER, TRACKER_COMPOSITE_CLEAR_OWNER );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldCreateTheTracker() {
|
||||||
|
MyEntity entity = new MyEntity( 0, "value1" );
|
||||||
|
assertThat( entity )
|
||||||
|
.extracting( NEXT_FIELD_NAME ).isNull();
|
||||||
|
assertThat( entity )
|
||||||
|
.extracting( PREVIOUS_FIELD_NAME ).isNull();
|
||||||
|
assertThat( entity )
|
||||||
|
.extracting( ENTITY_ENTRY_FIELD_NAME ).isNull();
|
||||||
|
assertThat( entity )
|
||||||
|
.extracting( TRACKER_FIELD_NAME ).isInstanceOf( SimpleFieldTracker.class );
|
||||||
|
assertThat( entity.getEmbedded() )
|
||||||
|
.extracting( TRACKER_COMPOSITE_FIELD_NAME ).isInstanceOf( CompositeOwnerTracker.class);
|
||||||
|
|
||||||
|
assertThat( entity ).extracting( resultOf( TRACKER_HAS_CHANGED_NAME ) ).isEqualTo( true );
|
||||||
|
assertThat( entity ).extracting( resultOf( TRACKER_GET_NAME ) )
|
||||||
|
.isEqualTo( new String[] { "embedded" } );
|
||||||
|
assertThat( entity.getEmbedded() )
|
||||||
|
.extracting( TRACKER_COMPOSITE_FIELD_NAME + ".names" ).isEqualTo( new String[] { "embedded" } );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldResetTheTracker() throws Exception {
|
||||||
|
MyEntity entity = new MyEntity( 1, "value1" );
|
||||||
|
|
||||||
|
Method trackerClearMethod = MyEntity.class.getMethod( TRACKER_CLEAR_NAME );
|
||||||
|
trackerClearMethod.invoke( entity );
|
||||||
|
|
||||||
|
assertThat( entity ).extracting( resultOf( TRACKER_HAS_CHANGED_NAME ) ).isEqualTo( false );
|
||||||
|
assertThat( entity ).extracting( resultOf( TRACKER_GET_NAME ) ).isEqualTo( new String[0] );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldUpdateTheTracker() throws Exception {
|
||||||
|
MyEntity entity = new MyEntity( 2, "value1" );
|
||||||
|
|
||||||
|
Method trackerClearMethod = MyEntity.class.getMethod( TRACKER_CLEAR_NAME );
|
||||||
|
trackerClearMethod.invoke( entity );
|
||||||
|
|
||||||
|
entity.getEmbedded().setText( "value2" );
|
||||||
|
|
||||||
|
assertThat( entity ).extracting( resultOf( TRACKER_HAS_CHANGED_NAME ) ).isEqualTo( true );
|
||||||
|
assertThat( entity ).extracting( resultOf( TRACKER_GET_NAME ) )
|
||||||
|
.isEqualTo( new String[] { "embedded" } );
|
||||||
|
|
||||||
|
trackerClearMethod.invoke( entity );
|
||||||
|
|
||||||
|
entity.setEmbedded( new MyEmbeddable( "value3" ) );
|
||||||
|
assertThat( entity ).extracting( resultOf( TRACKER_GET_NAME ) )
|
||||||
|
.isEqualTo( new String[] { "embedded" } );
|
||||||
|
assertThat( entity.getEmbedded() )
|
||||||
|
.extracting( TRACKER_COMPOSITE_FIELD_NAME + ".names" ).isEqualTo( new String[] { "embedded" } );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Embeddable
|
||||||
|
public static class MyEmbeddable {
|
||||||
|
|
||||||
|
@Column
|
||||||
|
private String text;
|
||||||
|
|
||||||
|
public MyEmbeddable() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private MyEmbeddable(String text) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setText(String text) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "myentity")
|
||||||
|
public static class MyEntity extends MyNonVisibleGenericMappedSuperclass<MyEmbeddable> {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
public MyEntity() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private MyEntity(Integer id, String text) {
|
||||||
|
this.id = id;
|
||||||
|
setEmbedded( new MyEmbeddable( text ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* 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.orm.test.bytecode.enhance.internal.bytebuddy;
|
||||||
|
|
||||||
|
import jakarta.persistence.Embedded;
|
||||||
|
import jakarta.persistence.MappedSuperclass;
|
||||||
|
|
||||||
|
// This class must not be nested in the test class, otherwise its private fields will be visible
|
||||||
|
// from subclasses and we won't reproduce the bug.
|
||||||
|
@MappedSuperclass
|
||||||
|
public abstract class MyNonVisibleGenericMappedSuperclass<C> {
|
||||||
|
|
||||||
|
@Embedded
|
||||||
|
private C embedded;
|
||||||
|
|
||||||
|
public C getEmbedded() {
|
||||||
|
return embedded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmbedded(C embedded) {
|
||||||
|
this.embedded = embedded;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue