diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/basic/LazyBasicFieldAccessTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/basic/LazyBasicFieldAccessTest.java index 8d3aedc8637e..08579d956a06 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/basic/LazyBasicFieldAccessTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/basic/LazyBasicFieldAccessTest.java @@ -9,6 +9,7 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; +import org.hibernate.testing.TestForIssue; import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Assert; @@ -16,6 +17,8 @@ import org.junit.Test; import org.junit.runner.RunWith; +import jakarta.persistence.Access; +import jakarta.persistence.AccessType; import jakarta.persistence.Basic; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; @@ -36,6 +39,7 @@ public class LazyBasicFieldAccessTest extends BaseCoreFunctionalTestCase { private LazyEntity entity; + private Long entityId; @Override @@ -53,82 +57,98 @@ protected void configure(Configuration configuration) { public void prepare() { doInHibernate( this::sessionFactory, s -> { LazyEntity entity = new LazyEntity(); - entity.setDescription( "desc" ); + entity.description = "desc"; s.persist( entity ); entityId = entity.id; } ); } @Test - public void test() { + public void testAttachedUpdate() { + doInHibernate( this::sessionFactory, s -> { + entity = s.get( LazyEntity.class, entityId ); + + Assert.assertFalse( isPropertyInitialized( entity, "description" ) ); + checkDirtyTracking( entity ); + + assertEquals( "desc", entity.description ); + assertTrue( isPropertyInitialized( entity, "description" ) ); + } ); + + doInHibernate( this::sessionFactory, s -> { + entity = s.get( LazyEntity.class, entityId ); + Assert.assertFalse( isPropertyInitialized( entity, "description" ) ); + entity.description = "desc1"; + + checkDirtyTracking( entity, "description" ); + + assertEquals( "desc1", entity.description ); + assertTrue( isPropertyInitialized( entity, "description" ) ); + } ); + + doInHibernate( this::sessionFactory, s -> { + entity = s.get( LazyEntity.class, entityId ); + assertEquals( "desc1", entity.description ); + } ); + } + + @Test + @TestForIssue(jiraKey = "HHH-11882") + public void testDetachedUpdate() { doInHibernate( this::sessionFactory, s -> { entity = s.get( LazyEntity.class, entityId ); Assert.assertFalse( isPropertyInitialized( entity, "description" ) ); checkDirtyTracking( entity ); - assertEquals( "desc", entity.getDescription() ); + assertEquals( "desc", entity.description ); assertTrue( isPropertyInitialized( entity, "description" ) ); } ); doInHibernate( this::sessionFactory, s -> { - entity.setDescription( "desc1" ); + entity.description = "desc1"; s.update( entity ); - //Assert.assertFalse( Hibernate.isPropertyInitialized( entity, "description" ) ); checkDirtyTracking( entity, "description" ); - assertEquals( "desc1", entity.getDescription() ); + assertEquals( "desc1", entity.description ); assertTrue( isPropertyInitialized( entity, "description" ) ); } ); doInHibernate( this::sessionFactory, s -> { entity = s.get( LazyEntity.class, entityId ); - assertEquals( "desc1", entity.getDescription() ); + assertEquals( "desc1", entity.description ); } ); doInHibernate( this::sessionFactory, s -> { - entity.setDescription( "desc2" ); + entity.description = "desc2"; LazyEntity mergedEntity = (LazyEntity) s.merge( entity ); - // Assert.assertFalse( isPropertyInitialized( entity, "description" ) ); + //Assert.assertFalse( Hibernate.isPropertyInitialized( entity, "description" ) ); checkDirtyTracking( mergedEntity, "description" ); - assertEquals( "desc2", mergedEntity.getDescription() ); + assertEquals( "desc2", mergedEntity.description ); assertTrue( isPropertyInitialized( mergedEntity, "description" ) ); } ); doInHibernate( this::sessionFactory, s -> { - LazyEntity entity = s.get( LazyEntity.class, entityId ); - assertEquals( "desc2", entity.getDescription() ); + entity = s.get( LazyEntity.class, entityId ); + assertEquals( "desc2", entity.description ); } ); } // --- // @Entity - @Table( name = "LAZY_FIELD_ENTITY" ) + @Access( AccessType.FIELD ) + @Table( name = "LAZY_PROPERTY_ENTITY" ) private static class LazyEntity { - Long id; - String description; @Id @GeneratedValue - Long getId() { - return id; - } - - void setId(Long id) { - this.id = id; - } + Long id; @Basic( fetch = FetchType.LAZY ) - String getDescription() { - return description; - } - - void setDescription(String description) { - this.description = description; - } + String description; } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/basic/LazyBasicPropertyAccessTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/basic/LazyBasicPropertyAccessTest.java index 46f068b94f7b..3f627ca8690d 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/basic/LazyBasicPropertyAccessTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/basic/LazyBasicPropertyAccessTest.java @@ -9,6 +9,7 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; +import org.hibernate.testing.TestForIssue; import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Assert; @@ -16,8 +17,6 @@ import org.junit.Test; import org.junit.runner.RunWith; -import jakarta.persistence.Access; -import jakarta.persistence.AccessType; import jakarta.persistence.Basic; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; @@ -38,7 +37,6 @@ public class LazyBasicPropertyAccessTest extends BaseCoreFunctionalTestCase { private LazyEntity entity; - private Long entityId; @Override @@ -56,69 +54,111 @@ protected void configure(Configuration configuration) { public void prepare() { doInHibernate( this::sessionFactory, s -> { LazyEntity entity = new LazyEntity(); - entity.description = "desc"; + entity.setDescription( "desc" ); s.persist( entity ); - entityId = entity.id; + entityId = entity.getId(); + } ); + } + + @Test + public void testAttachedUpdate() { + doInHibernate( this::sessionFactory, s -> { + entity = s.get( LazyEntity.class, entityId ); + + Assert.assertFalse( isPropertyInitialized( entity, "description" ) ); + checkDirtyTracking( entity ); + + assertEquals( "desc", entity.getDescription() ); + assertTrue( isPropertyInitialized( entity, "description" ) ); + } ); + + doInHibernate( this::sessionFactory, s -> { + entity = s.get( LazyEntity.class, entityId ); + Assert.assertFalse( isPropertyInitialized( entity, "description" ) ); + entity.setDescription( "desc1" ); + + checkDirtyTracking( entity, "description" ); + + assertEquals( "desc1", entity.getDescription() ); + assertTrue( isPropertyInitialized( entity, "description" ) ); + } ); + + doInHibernate( this::sessionFactory, s -> { + entity = s.get( LazyEntity.class, entityId ); + assertEquals( "desc1", entity.getDescription() ); } ); } @Test - public void execute() { + @TestForIssue(jiraKey = "HHH-11882") + public void testDetachedUpdate() { doInHibernate( this::sessionFactory, s -> { entity = s.get( LazyEntity.class, entityId ); Assert.assertFalse( isPropertyInitialized( entity, "description" ) ); checkDirtyTracking( entity ); - assertEquals( "desc", entity.description ); + assertEquals( "desc", entity.getDescription() ); assertTrue( isPropertyInitialized( entity, "description" ) ); } ); doInHibernate( this::sessionFactory, s -> { - entity.description = "desc1"; + entity.setDescription( "desc1" ); s.update( entity ); - // Assert.assertFalse( Hibernate.isPropertyInitialized( entity, "description" ) ); checkDirtyTracking( entity, "description" ); - assertEquals( "desc1", entity.description ); + assertEquals( "desc1", entity.getDescription() ); assertTrue( isPropertyInitialized( entity, "description" ) ); } ); doInHibernate( this::sessionFactory, s -> { entity = s.get( LazyEntity.class, entityId ); - assertEquals( "desc1", entity.description ); + assertEquals( "desc1", entity.getDescription() ); } ); doInHibernate( this::sessionFactory, s -> { - entity.description = "desc2"; + entity.setDescription( "desc2" ); LazyEntity mergedEntity = (LazyEntity) s.merge( entity ); - //Assert.assertFalse( Hibernate.isPropertyInitialized( entity, "description" ) ); + // Assert.assertFalse( isPropertyInitialized( entity, "description" ) ); checkDirtyTracking( mergedEntity, "description" ); - assertEquals( "desc2", mergedEntity.description ); + assertEquals( "desc2", mergedEntity.getDescription() ); assertTrue( isPropertyInitialized( mergedEntity, "description" ) ); } ); doInHibernate( this::sessionFactory, s -> { - LazyEntity entity = s.get( LazyEntity.class, entityId ); - assertEquals( "desc2", entity.description ); + entity = s.get( LazyEntity.class, entityId ); + assertEquals( "desc2", entity.getDescription() ); } ); } // --- // @Entity - @Access( AccessType.FIELD ) - @Table( name = "LAZY_PROPERTY_ENTITY" ) + @Table( name = "LAZY_FIELD_ENTITY" ) private static class LazyEntity { + Long id; + String description; @Id @GeneratedValue - Long id; + Long getId() { + return id; + } + + void setId(Long id) { + this.id = id; + } @Basic( fetch = FetchType.LAZY ) - String description; + String getDescription() { + return description; + } + + void setDescription(String description) { + this.description = description; + } } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/basic/MultiLazyBasicUpdateTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/basic/MultiLazyBasicUpdateTest.java new file mode 100644 index 000000000000..a7c6b5b2984b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/basic/MultiLazyBasicUpdateTest.java @@ -0,0 +1,203 @@ +/* + * 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 . + */ +package org.hibernate.orm.test.bytecode.enhancement.lazy.basic; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import jakarta.persistence.Basic; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +@RunWith(BytecodeEnhancerRunner.class) +public class MultiLazyBasicUpdateTest extends BaseCoreFunctionalTestCase { + + private Long entityId; + + @Override + public Class[] getAnnotatedClasses() { + return new Class[] { LazyEntity.class }; + } + + @Before + public void prepare() { + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = new LazyEntity(); + s.persist( entity ); + entityId = entity.getId(); + } ); + } + + @Test + public void updateOneLazyProperty() { + // null -> non-null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( "update1" ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertEquals( "update1", entity.getLazyProperty1() ); + } ); + + // non-null -> non-null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( "update2" ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertEquals( "update2", entity.getLazyProperty1() ); + } ); + + // non-null -> null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( null ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertNull( entity.getLazyProperty1() ); + } ); + } + + @Test + public void updateOneEagerPropertyAndOneLazyProperty() { + // null -> non-null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setEagerProperty( "update1" ); + entity.setLazyProperty1( "update1" ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertEquals( "update1", entity.getEagerProperty() ); + assertEquals( "update1", entity.getLazyProperty1() ); + } ); + + // non-null -> non-null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setEagerProperty( "update2" ); + entity.setLazyProperty1( "update2" ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertEquals( "update2", entity.getEagerProperty() ); + assertEquals( "update2", entity.getLazyProperty1() ); + } ); + + // non-null -> null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setEagerProperty( null ); + entity.setLazyProperty1( null ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertNull( entity.getEagerProperty() ); + assertNull( entity.getLazyProperty1() ); + } ); + } + + @Test + public void updateAllLazyProperties() { + // null -> non-null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( "update1" ); + entity.setLazyProperty2( "update1" ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertEquals( "update1", entity.getLazyProperty1() ); + assertEquals( "update1", entity.getLazyProperty2() ); + } ); + + // non-null -> non-null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( "update2" ); + entity.setLazyProperty2( "update2" ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertEquals( "update2", entity.getLazyProperty1() ); + assertEquals( "update2", entity.getLazyProperty2() ); + } ); + + // non-null -> null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( null ); + entity.setLazyProperty2( null ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertNull( entity.getLazyProperty1() ); + assertNull( entity.getLazyProperty2() ); + } ); + } + + @Entity + @Table(name = "LAZY_ENTITY") + private static class LazyEntity { + @Id + @GeneratedValue + Long id; + // We need at least one eager property to avoid a different problem. + @Basic + String eagerProperty; + @Basic(fetch = FetchType.LAZY) + String lazyProperty1; + // We need multiple lazy properties to reproduce the problem. + @Basic(fetch = FetchType.LAZY) + String lazyProperty2; + + Long getId() { + return id; + } + + void setId(Long id) { + this.id = id; + } + + public String getEagerProperty() { + return eagerProperty; + } + + public void setEagerProperty(String eagerProperty) { + this.eagerProperty = eagerProperty; + } + + public String getLazyProperty1() { + return lazyProperty1; + } + + public void setLazyProperty1(String lazyProperty1) { + this.lazyProperty1 = lazyProperty1; + } + + public String getLazyProperty2() { + return lazyProperty2; + } + + public void setLazyProperty2(String lazyProperty2) { + this.lazyProperty2 = lazyProperty2; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/basic/OnlyLazyBasicUpdateTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/basic/OnlyLazyBasicUpdateTest.java new file mode 100644 index 000000000000..c211b2215af3 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/basic/OnlyLazyBasicUpdateTest.java @@ -0,0 +1,153 @@ +/* + * 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 . + */ +package org.hibernate.orm.test.bytecode.enhancement.lazy.basic; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import jakarta.persistence.Basic; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +@RunWith(BytecodeEnhancerRunner.class) +public class OnlyLazyBasicUpdateTest extends BaseCoreFunctionalTestCase { + + private Long entityId; + + @Override + public Class[] getAnnotatedClasses() { + return new Class[] { LazyEntity.class }; + } + + @Before + public void prepare() { + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = new LazyEntity(); + s.persist( entity ); + entityId = entity.getId(); + } ); + } + + @Test + public void updateSomeLazyProperty() { + // null -> non-null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( "update1" ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertEquals( "update1", entity.getLazyProperty1() ); + } ); + + // non-null -> non-null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( "update2" ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertEquals( "update2", entity.getLazyProperty1() ); + } ); + + // non-null -> null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( null ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertNull( entity.getLazyProperty1() ); + } ); + } + + @Test + public void updateAllLazyProperties() { + // null -> non-null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( "update1" ); + entity.setLazyProperty2( "update1" ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertEquals( "update1", entity.getLazyProperty1() ); + assertEquals( "update1", entity.getLazyProperty2() ); + } ); + + // non-null -> non-null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( "update2" ); + entity.setLazyProperty2( "update2" ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertEquals( "update2", entity.getLazyProperty1() ); + assertEquals( "update2", entity.getLazyProperty2() ); + } ); + + // non-null -> null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( null ); + entity.setLazyProperty2( null ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertNull( entity.getLazyProperty1() ); + assertNull( entity.getLazyProperty2() ); + } ); + } + + @Entity + @Table(name = "LAZY_ENTITY") + private static class LazyEntity { + @Id + @GeneratedValue + Long id; + // ALL properties must be lazy in order to reproduce the problem. + @Basic(fetch = FetchType.LAZY) + String lazyProperty1; + @Basic(fetch = FetchType.LAZY) + String lazyProperty2; + + Long getId() { + return id; + } + + void setId(Long id) { + this.id = id; + } + + public String getLazyProperty1() { + return lazyProperty1; + } + + public void setLazyProperty1(String lazyProperty1) { + this.lazyProperty1 = lazyProperty1; + } + + public String getLazyProperty2() { + return lazyProperty2; + } + + public void setLazyProperty2(String lazyProperty2) { + this.lazyProperty2 = lazyProperty2; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/group/MultiLazyBasicInLazyGroupUpdateTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/group/MultiLazyBasicInLazyGroupUpdateTest.java new file mode 100644 index 000000000000..b27eb3b24f3f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/group/MultiLazyBasicInLazyGroupUpdateTest.java @@ -0,0 +1,207 @@ +/* + * 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 . + */ +package org.hibernate.orm.test.bytecode.enhancement.lazy.group; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.hibernate.annotations.LazyGroup; + +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import jakarta.persistence.Basic; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +@RunWith(BytecodeEnhancerRunner.class) +public class MultiLazyBasicInLazyGroupUpdateTest extends BaseCoreFunctionalTestCase { + + private Long entityId; + + @Override + public Class[] getAnnotatedClasses() { + return new Class[] { LazyEntity.class }; + } + + @Before + public void prepare() { + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = new LazyEntity(); + s.persist( entity ); + entityId = entity.getId(); + } ); + } + + @Test + public void updateOneLazyProperty() { + // null -> non-null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( "update1" ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertEquals( "update1", entity.getLazyProperty1() ); + } ); + + // non-null -> non-null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( "update2" ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertEquals( "update2", entity.getLazyProperty1() ); + } ); + + // non-null -> null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( null ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertNull( entity.getLazyProperty1() ); + } ); + } + + @Test + public void updateOneEagerPropertyAndOneLazyProperty() { + // null -> non-null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setEagerProperty( "update1" ); + entity.setLazyProperty1( "update1" ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertEquals( "update1", entity.getEagerProperty() ); + assertEquals( "update1", entity.getLazyProperty1() ); + } ); + + // non-null -> non-null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setEagerProperty( "update2" ); + entity.setLazyProperty1( "update2" ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertEquals( "update2", entity.getEagerProperty() ); + assertEquals( "update2", entity.getLazyProperty1() ); + } ); + + // non-null -> null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setEagerProperty( null ); + entity.setLazyProperty1( null ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertNull( entity.getEagerProperty() ); + assertNull( entity.getLazyProperty1() ); + } ); + } + + @Test + public void updateAllLazyProperties() { + // null -> non-null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( "update1" ); + entity.setLazyProperty2( "update1" ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertEquals( "update1", entity.getLazyProperty1() ); + assertEquals( "update1", entity.getLazyProperty2() ); + } ); + + // non-null -> non-null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( "update2" ); + entity.setLazyProperty2( "update2" ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertEquals( "update2", entity.getLazyProperty1() ); + assertEquals( "update2", entity.getLazyProperty2() ); + } ); + + // non-null -> null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( null ); + entity.setLazyProperty2( null ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertNull( entity.getLazyProperty1() ); + assertNull( entity.getLazyProperty2() ); + } ); + } + + @Entity + @Table(name = "LAZY_ENTITY") + private static class LazyEntity { + @Id + @GeneratedValue + Long id; + // We need at least one eager property to avoid a different problem. + @Basic + String eagerProperty; + @Basic(fetch = FetchType.LAZY) + @LazyGroup("group1") + String lazyProperty1; + // We need multiple lazy properties to reproduce the problem. + @Basic(fetch = FetchType.LAZY) + @LazyGroup("group2") + String lazyProperty2; + + Long getId() { + return id; + } + + void setId(Long id) { + this.id = id; + } + + public String getEagerProperty() { + return eagerProperty; + } + + public void setEagerProperty(String eagerProperty) { + this.eagerProperty = eagerProperty; + } + + public String getLazyProperty1() { + return lazyProperty1; + } + + public void setLazyProperty1(String lazyProperty1) { + this.lazyProperty1 = lazyProperty1; + } + + public String getLazyProperty2() { + return lazyProperty2; + } + + public void setLazyProperty2(String lazyProperty2) { + this.lazyProperty2 = lazyProperty2; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/group/OnlyLazyBasicInLazyGroupBasicUpdateTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/group/OnlyLazyBasicInLazyGroupBasicUpdateTest.java new file mode 100644 index 000000000000..c1293f44ae10 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/lazy/group/OnlyLazyBasicInLazyGroupBasicUpdateTest.java @@ -0,0 +1,157 @@ +/* + * 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 . + */ +package org.hibernate.orm.test.bytecode.enhancement.lazy.group; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.hibernate.annotations.LazyGroup; + +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import jakarta.persistence.Basic; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +@RunWith(BytecodeEnhancerRunner.class) +public class OnlyLazyBasicInLazyGroupBasicUpdateTest extends BaseCoreFunctionalTestCase { + + private Long entityId; + + @Override + public Class[] getAnnotatedClasses() { + return new Class[] { LazyEntity.class }; + } + + @Before + public void prepare() { + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = new LazyEntity(); + s.persist( entity ); + entityId = entity.getId(); + } ); + } + + @Test + public void updateOneLazyProperty() { + // null -> non-null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( "update1" ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertEquals( "update1", entity.getLazyProperty1() ); + } ); + + // non-null -> non-null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( "update2" ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertEquals( "update2", entity.getLazyProperty1() ); + } ); + + // non-null -> null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( null ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertNull( entity.getLazyProperty1() ); + } ); + } + + @Test + public void updateAllLazyProperties() { + // null -> non-null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( "update1" ); + entity.setLazyProperty2( "update1" ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertEquals( "update1", entity.getLazyProperty1() ); + assertEquals( "update1", entity.getLazyProperty2() ); + } ); + + // non-null -> non-null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( "update2" ); + entity.setLazyProperty2( "update2" ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertEquals( "update2", entity.getLazyProperty1() ); + assertEquals( "update2", entity.getLazyProperty2() ); + } ); + + // non-null -> null + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + entity.setLazyProperty1( null ); + entity.setLazyProperty2( null ); + } ); + doInHibernate( this::sessionFactory, s -> { + LazyEntity entity = s.get( LazyEntity.class, entityId ); + assertNull( entity.getLazyProperty1() ); + assertNull( entity.getLazyProperty2() ); + } ); + } + + @Entity + @Table(name = "LAZY_ENTITY") + private static class LazyEntity { + @Id + @GeneratedValue + Long id; + // ALL properties must be lazy in order to reproduce the problem. + @Basic(fetch = FetchType.LAZY) + @LazyGroup("group1") + String lazyProperty1; + @Basic(fetch = FetchType.LAZY) + @LazyGroup("group2") + String lazyProperty2; + + Long getId() { + return id; + } + + void setId(Long id) { + this.id = id; + } + + public String getLazyProperty1() { + return lazyProperty1; + } + + public void setLazyProperty1(String lazyProperty1) { + this.lazyProperty1 = lazyProperty1; + } + + public String getLazyProperty2() { + return lazyProperty2; + } + + public void setLazyProperty2(String lazyProperty2) { + this.lazyProperty2 = lazyProperty2; + } + } +}