diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinFormulaManyToOneLazyFetchingWithIdClassTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinFormulaManyToOneLazyFetchingWithIdClassTest.java new file mode 100644 index 000000000000..e38c665a6821 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinFormulaManyToOneLazyFetchingWithIdClassTest.java @@ -0,0 +1,175 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.annotations.formula; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import org.hibernate.LazyInitializationException; +import org.hibernate.annotations.JoinColumnOrFormula; +import org.hibernate.annotations.JoinFormula; +import org.hibernate.annotations.processing.Exclude; +import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase; +import org.hibernate.testing.orm.junit.JiraKey; +import org.junit.Test; + +import java.io.Serializable; +import java.util.List; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +@JiraKey(value = "HHH-19214") +public class JoinFormulaManyToOneLazyFetchingWithIdClassTest extends BaseEntityManagerFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Stock.class, + StockCode.class, + }; + } + + @Override + protected void afterEntityManagerFactoryBuilt() { + doInJPA( this::entityManagerFactory, entityManager -> { + StockCode code = new StockCode(); + code.setId( 1L ); + code.setCopeType( CodeType.TYPE_A ); + code.setRefNumber( "ABC" ); + entityManager.persist( code ); + + Stock stock1 = new Stock(); + stock1.setId( 1L ); + stock1.setCode( code ); + entityManager.persist( stock1 ); + + Stock stock2 = new Stock(); + stock2.setId( 2L ); + entityManager.persist( stock2 ); + } ); + } + + @Test + public void testLazyLoading() { + List stocks = doInJPA( this::entityManagerFactory, entityManager -> { + return entityManager.createQuery( + "SELECT s FROM Stock s", Stock.class ) + .getResultList(); + } ); + assertEquals( 2, stocks.size() ); + + try { + assertEquals( "ABC", stocks.get( 0 ).getCode().getRefNumber() ); + + fail( "Should have thrown LazyInitializationException" ); + } + catch (LazyInitializationException expected) { + + } + } + + @Test + public void testEagerLoading() { + doInJPA( this::entityManagerFactory, entityManager -> { + List stocks = entityManager.createQuery( + "SELECT s FROM Stock s", Stock.class ) + .getResultList(); + + assertEquals( 2, stocks.size() ); + assertEquals( "ABC", stocks.get( 0 ).getCode().getRefNumber() ); + + // In 5.x, for some reason, we didn't understand that a component is a FK, + // hence a partial null was wrongly handled, but in 6.0 we handle this in a unified way + assertNull( stocks.get( 1 ).getCode() ); + } ); + } + + @Entity(name = "Stock") + @Exclude + public static class Stock implements Serializable { + + @Id + @Column(name = "ID") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumnOrFormula(column = @JoinColumn(name = "CODE_ID", referencedColumnName = "ID")) + @JoinColumnOrFormula(formula = @JoinFormula(referencedColumnName = "TYPE", value = "'TYPE_A'")) + private StockCode code; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public StockCode getCode() { + return code; + } + + public void setCode(StockCode code) { + this.code = code; + } + } + + @Entity(name = "StockCode") + @IdClass(StockCode.ID.class) + @Exclude + public static class StockCode implements Serializable { + + @Id + @Column(name = "ID") + private Long id; + + @Id + @Enumerated(EnumType.STRING) + @Column(name = "TYPE") + private CodeType copeType; + + private String refNumber; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public CodeType getCopeType() { + return copeType; + } + + public void setCopeType(CodeType copeType) { + this.copeType = copeType; + } + + public String getRefNumber() { + return refNumber; + } + + public void setRefNumber(String refNumber) { + this.refNumber = refNumber; + } + + public record ID(Long id, CodeType copeType) {} + } + + public enum CodeType { + TYPE_A, TYPE_B; + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinFormulaManyToOneNotIgnoreLazyFetchingWithIdClassTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinFormulaManyToOneNotIgnoreLazyFetchingWithIdClassTest.java new file mode 100644 index 000000000000..55c2e2ed7a36 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinFormulaManyToOneNotIgnoreLazyFetchingWithIdClassTest.java @@ -0,0 +1,174 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.annotations.formula; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import org.hibernate.annotations.JoinColumnOrFormula; +import org.hibernate.annotations.JoinFormula; +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; +import org.hibernate.annotations.processing.Exclude; +import org.hibernate.boot.model.internal.ToOneBinder; +import org.hibernate.internal.CoreMessageLogger; +import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase; +import org.hibernate.testing.logger.LoggerInspectionRule; +import org.hibernate.testing.logger.Triggerable; +import org.hibernate.testing.orm.junit.JiraKey; +import org.jboss.logging.Logger; +import org.junit.Rule; +import org.junit.Test; + +import java.io.Serializable; +import java.lang.invoke.MethodHandles; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +@JiraKey(value = "HHH-19214") +public class JoinFormulaManyToOneNotIgnoreLazyFetchingWithIdClassTest extends BaseEntityManagerFunctionalTestCase { + + @Rule + public LoggerInspectionRule logInspection = new LoggerInspectionRule( + Logger.getMessageLogger( MethodHandles.lookup(), CoreMessageLogger.class, ToOneBinder.class.getName() ) + ); + + private final Triggerable triggerable = logInspection.watchForLogMessages( "HHH000491" ); + + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Stock.class, + StockCode.class, + }; + } + + @Override + protected void afterEntityManagerFactoryBuilt() { + doInJPA( this::entityManagerFactory, entityManager -> { + StockCode code = new StockCode(); + code.setId( 1L ); + code.setCopeType( CodeType.TYPE_A ); + code.setRefNumber( "ABC" ); + entityManager.persist( code ); + + Stock stock1 = new Stock(); + stock1.setId( 1L ); + stock1.setCode( code ); + entityManager.persist( stock1 ); + + Stock stock2 = new Stock(); + stock2.setId( 2L ); + entityManager.persist( stock2 ); + } ); + } + + @Test + public void testLazyLoading() { + assertThat( triggerable.wasTriggered() ) + .describedAs( "Expecting WARN message to be logged" ) + .isTrue(); + + List stocks = doInJPA( this::entityManagerFactory, entityManager -> { + return entityManager.createQuery( + "SELECT s FROM Stock s", Stock.class ) + .getResultList(); + } ); + assertEquals( 2, stocks.size() ); + + assertEquals( "ABC", stocks.get( 0 ).getCode().getRefNumber() ); + assertNull( stocks.get( 1 ).getCode() ); + } + + @Entity(name = "Stock") + @Exclude + public static class Stock implements Serializable { + + @Id + @Column(name = "ID") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @NotFound(action = NotFoundAction.IGNORE) + @JoinColumnOrFormula(column = @JoinColumn(name = "CODE_ID", referencedColumnName = "ID")) + @JoinColumnOrFormula(formula = @JoinFormula(referencedColumnName = "TYPE", value = "'TYPE_A'")) + private StockCode code; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public StockCode getCode() { + return code; + } + + public void setCode(StockCode code) { + this.code = code; + } + } + + @Entity(name = "StockCode") + @IdClass(StockCode.ID.class) + @Exclude + public static class StockCode implements Serializable { + + @Id + @Column(name = "ID") + private Long id; + + @Id + @Enumerated(EnumType.STRING) + @Column(name = "TYPE") + private CodeType copeType; + + private String refNumber; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public CodeType getCopeType() { + return copeType; + } + + public void setCopeType(CodeType copeType) { + this.copeType = copeType; + } + + public String getRefNumber() { + return refNumber; + } + + public void setRefNumber(String refNumber) { + this.refNumber = refNumber; + } + + public record ID(Long id, CodeType copeType) {} + } + + public enum CodeType { + TYPE_A, TYPE_B; + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinFormulaOneToOneNotIgnoreLazyFetchingWithIdClassTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinFormulaOneToOneNotIgnoreLazyFetchingWithIdClassTest.java new file mode 100644 index 000000000000..bcd4dbff0dd4 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinFormulaOneToOneNotIgnoreLazyFetchingWithIdClassTest.java @@ -0,0 +1,174 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.annotations.formula; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import org.hibernate.annotations.JoinColumnOrFormula; +import org.hibernate.annotations.JoinFormula; +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; +import org.hibernate.annotations.processing.Exclude; +import org.hibernate.boot.model.internal.ToOneBinder; +import org.hibernate.internal.CoreMessageLogger; +import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase; +import org.hibernate.testing.logger.LoggerInspectionRule; +import org.hibernate.testing.logger.Triggerable; +import org.hibernate.testing.orm.junit.JiraKey; +import org.jboss.logging.Logger; +import org.junit.Rule; +import org.junit.Test; + +import java.io.Serializable; +import java.lang.invoke.MethodHandles; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +@JiraKey(value = "HHH-19214") +public class JoinFormulaOneToOneNotIgnoreLazyFetchingWithIdClassTest extends BaseEntityManagerFunctionalTestCase { + + @Rule + public LoggerInspectionRule logInspection = new LoggerInspectionRule( + Logger.getMessageLogger( MethodHandles.lookup(), CoreMessageLogger.class, ToOneBinder.class.getName() ) + ); + + private final Triggerable triggerable = logInspection.watchForLogMessages( "HHH000491" ); + + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Stock.class, + StockCode.class, + }; + } + + @Override + protected void afterEntityManagerFactoryBuilt() { + doInJPA( this::entityManagerFactory, entityManager -> { + StockCode code = new StockCode(); + code.setId( 1L ); + code.setCopeType( CodeType.TYPE_A ); + code.setRefNumber( "ABC" ); + entityManager.persist( code ); + + Stock stock1 = new Stock(); + stock1.setId( 1L ); + stock1.setCode( code ); + entityManager.persist( stock1 ); + + Stock stock2 = new Stock(); + stock2.setId( 2L ); + entityManager.persist( stock2 ); + } ); + } + + @Test + public void testLazyLoading() { + assertThat( triggerable.wasTriggered() ) + .describedAs( "Expecting WARN message to be logged" ) + .isTrue(); + + List stocks = doInJPA( this::entityManagerFactory, entityManager -> { + return entityManager.createQuery( + "SELECT s FROM Stock s", Stock.class ) + .getResultList(); + } ); + assertEquals( 2, stocks.size() ); + + assertEquals( "ABC", stocks.get( 0 ).getCode().getRefNumber() ); + assertNull( stocks.get( 1 ).getCode() ); + } + + @Entity(name = "Stock") + @Exclude + public static class Stock implements Serializable { + + @Id + @Column(name = "ID") + private Long id; + + @OneToOne(fetch = FetchType.LAZY) + @NotFound(action = NotFoundAction.IGNORE) + @JoinColumnOrFormula(column = @JoinColumn(name = "CODE_ID", referencedColumnName = "ID")) + @JoinColumnOrFormula(formula = @JoinFormula(referencedColumnName = "TYPE", value = "'TYPE_A'")) + private StockCode code; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public StockCode getCode() { + return code; + } + + public void setCode(StockCode code) { + this.code = code; + } + } + + @Entity(name = "StockCode") + @IdClass(StockCode.ID.class) + @Exclude + public static class StockCode implements Serializable { + + @Id + @Column(name = "ID") + private Long id; + + @Id + @Enumerated(EnumType.STRING) + @Column(name = "TYPE") + private CodeType copeType; + + private String refNumber; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public CodeType getCopeType() { + return copeType; + } + + public void setCopeType(CodeType copeType) { + this.copeType = copeType; + } + + public String getRefNumber() { + return refNumber; + } + + public void setRefNumber(String refNumber) { + this.refNumber = refNumber; + } + + public record ID(Long id, CodeType copeType) {} + } + + public enum CodeType { + TYPE_A, TYPE_B; + } + +}