HHH-16108 Test a (simple) entity update for models with bytecode enhancement and multiple one-to-one associations (some lazy)

Stack trace:

java.lang.NullPointerException: Cannot invoke "org.hibernate.metamodel.mapping.SelectableMapping.isFormula()" because "selectable" is null

	at org.hibernate.persister.entity.mutation.UpdateCoordinatorStandard.processSet(UpdateCoordinatorStandard.java:665)
	at org.hibernate.persister.entity.mutation.UpdateCoordinatorStandard.processAttribute(UpdateCoordinatorStandard.java:640)
	at org.hibernate.persister.entity.mutation.UpdateCoordinatorStandard.analyzeUpdateValues(UpdateCoordinatorStandard.java:600)
	at org.hibernate.persister.entity.mutation.UpdateCoordinatorStandard.performUpdate(UpdateCoordinatorStandard.java:256)
	at org.hibernate.persister.entity.mutation.UpdateCoordinatorStandard.coordinateUpdate(UpdateCoordinatorStandard.java:203)
	at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2766)
	at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:165)
	at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:616)
	at org.hibernate.engine.spi.ActionQueue.lambda$executeActions$1(ActionQueue.java:487)
	at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:721)
	at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:484)
	at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:358)
	at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
	at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:127)
	at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1412)
	at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:485)
	at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:2277)
	at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:1942)
	at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:426)
	at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:169)
	at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:267)
	at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:101)
	at org.hibernate.testing.transaction.TransactionUtil2.inTransaction(TransactionUtil2.java:128)
	at org.hibernate.testing.transaction.TransactionUtil2.lambda$inTransaction$0(TransactionUtil2.java:76)
	at org.hibernate.testing.transaction.TransactionUtil2.inSession(TransactionUtil2.java:35)
	at org.hibernate.testing.transaction.TransactionUtil2.inTransaction(TransactionUtil2.java:74)
	at org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase.inTransaction(BaseNonConfigCoreFunctionalTestCase.java:590)
	at org.hibernate.orm.test.bytecode.enhancement.lazy.proxy.LazyOneToOneMultiLevelTest.testPersist(LazyOneToOneMultiLevelTest.java:56)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
This commit is contained in:
Yoann Rodière 2023-01-26 14:07:12 +01:00 committed by Andrea Boriero
parent 6c385f7890
commit 6bc1b24443
1 changed files with 189 additions and 0 deletions

View File

@ -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.enhancement.lazy.proxy;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertFalse;
import org.hibernate.Hibernate;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToOne;
@RunWith(BytecodeEnhancerRunner.class)
@EnhancementOptions(lazyLoading = true)
@TestForIssue(jiraKey = "HHH-16108")
public class LazyOneToOneMultiAssociationTest extends BaseNonConfigCoreFunctionalTestCase {
@Override
public Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { EntityA.class, EntityB.class };
}
@Before
public void prepare() {
inTransaction( s -> {
EntityA entityA = new EntityA( 1 );
s.persist( entityA );
} );
}
@After
public void tearDown() {
inTransaction( s -> {
s.createMutationQuery( "delete entityb" ).executeUpdate();
s.createMutationQuery( "delete entitya" ).executeUpdate();
} );
}
@Test
public void testPersist() {
inTransaction( s -> {
EntityA entityA = s.get( EntityA.class, 1 );
EntityB entityB = new EntityB( 2 );
entityA.setMappedAssociation1( entityB );
entityB.setAssociation1( entityA );
s.persist( entityB );
// Without a fix, this ends up failing during the flush:
// java.lang.NullPointerException: Cannot invoke "org.hibernate.metamodel.mapping.SelectableMapping.isFormula()" because "selectable" is null
// at org.hibernate.persister.entity.mutation.UpdateCoordinatorStandard.processSet(UpdateCoordinatorStandard.java:665)
} );
inTransaction( s -> {
EntityA entityA = s.get( EntityA.class, 1 );
assertThat( entityA ).isNotNull();
assertFalse( Hibernate.isPropertyInitialized( entityA, "mappedAssociation1" ) );
EntityB entityB = entityA.getMappedAssociation1();
assertThat( entityB ).isNotNull();
assertThat( entityB.getAssociation1() ).isEqualTo( entityA );
} );
}
@Entity(name = "entitya")
public static class EntityA {
@Id
private Integer id;
@OneToOne
private EntityA selfAssociation;
@OneToOne(mappedBy = "selfAssociation")
private EntityA mappedSelfAssociation;
@OneToOne(mappedBy = "association1", fetch = FetchType.LAZY)
private EntityB mappedAssociation1;
@OneToOne(mappedBy = "association2", fetch = FetchType.LAZY)
private EntityB mappedAssociation2;
protected EntityA() {
}
public EntityA(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public EntityA getSelfAssociation() {
return selfAssociation;
}
public void setSelfAssociation(EntityA selfAssociation) {
this.selfAssociation = selfAssociation;
}
public EntityA getMappedSelfAssociation() {
return mappedSelfAssociation;
}
public void setMappedSelfAssociation(EntityA mappedSelfAssociation) {
this.mappedSelfAssociation = mappedSelfAssociation;
}
public EntityB getMappedAssociation1() {
return mappedAssociation1;
}
public void setMappedAssociation1(EntityB mappedAssociation1) {
this.mappedAssociation1 = mappedAssociation1;
}
public EntityB getMappedAssociation2() {
return mappedAssociation2;
}
public void setMappedAssociation2(EntityB mappedAssociation2) {
this.mappedAssociation2 = mappedAssociation2;
}
}
@Entity(name = "entityb")
public static class EntityB {
@Id
private Integer id;
@OneToOne
private EntityA association1;
@OneToOne
private EntityA association2;
protected EntityB() {
}
public EntityB(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public EntityA getAssociation1() {
return association1;
}
public void setAssociation1(EntityA association1) {
this.association1 = association1;
}
public EntityA getAssociation2() {
return association2;
}
public void setAssociation2(EntityA association2) {
this.association2 = association2;
}
}
}