From 3f690eb01c8cea75a16902e3a6fe6181522d88be Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Sun, 28 Mar 2021 15:11:40 -0700 Subject: [PATCH 1/3] HHH-14537 : Added test for issue (cherry picked from commit 9cb9137fb8a7189a8041c714ade0214ebaf6a0b8) --- .../test/notfound/EagerProxyNotFoundTest.java | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/notfound/EagerProxyNotFoundTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/test/notfound/EagerProxyNotFoundTest.java b/hibernate-core/src/test/java/org/hibernate/test/notfound/EagerProxyNotFoundTest.java new file mode 100644 index 000000000000..996559263d4f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/notfound/EagerProxyNotFoundTest.java @@ -0,0 +1,162 @@ +/* + * 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.test.notfound; + +import javax.persistence.Column; +import javax.persistence.ConstraintMode; +import javax.persistence.Entity; +import javax.persistence.EntityNotFoundException; +import javax.persistence.FetchType; +import javax.persistence.ForeignKey; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.After; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +/** + * @author Gail Badner + */ +@TestForIssue(jiraKey = "HHH-14537") +public class EagerProxyNotFoundTest extends BaseNonConfigCoreFunctionalTestCase { + + @Test + public void testNoProxyInSession() { + doInHibernate( this::sessionFactory, session -> { + final Task task = new Task(); + task.id = 1; + task.employeeId = 2; + session.persist( task ); + }); + + doInHibernate( this::sessionFactory, session -> { + final Task task = session.createQuery( "from Task", Task.class ).getSingleResult(); + assertNotNull( task ); + assertEquals( 2, task.employeeId ); + assertNull( task.employee ); + }); + } + + @Test + public void testNonExistingProxyInSession() { + doInHibernate( this::sessionFactory, session -> { + final Task task = new Task(); + task.id = 1; + task.employeeId = 2; + session.persist( task ); + }); + + doInHibernate( this::sessionFactory, session -> { + session.load( Employee.class, 2 ); + final Task task = session.createQuery( "from Task", Task.class ).getSingleResult(); + assertNotNull( task ); + assertEquals( 2, task.employeeId ); + assertNull( task.employee ); + }); + } + + @Test + public void testExistingProxyWithNonExistingAssociation() { + doInHibernate( this::sessionFactory, session -> { + final Employee employee = new Employee(); + employee.id = 1; + session.persist( employee ); + + final Task task = new Task(); + task.id = 2; + task.employee = employee; + task.employeeId = 1; + session.persist( task ); + + session.flush(); + + session.createNativeQuery( "update Employee set locationId = 3 where id = 1" ) + .executeUpdate(); + }); + + try { + doInHibernate( this::sessionFactory, session -> { + session.load( Employee.class, 1 ); + session.createQuery( "from Task", Task.class ).getSingleResult(); + }); + fail( "EntityNotFoundException should have been thrown because Task.employee.location is not found " + + "and is not mapped with @NotFound(IGNORE)" ); + } + catch (EntityNotFoundException expected) { + } + } + + @After + public void deleteData() { + doInHibernate( this::sessionFactory, session -> { + session.createQuery( "delete from Task" ).executeUpdate(); + session.createQuery( "delete from Employee" ).executeUpdate(); + session.createQuery( "delete from Location" ).executeUpdate(); + }); + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Task.class, + Employee.class, + Location.class + }; + } + + @Entity(name = "Task") + public static class Task { + + @Id + private int id; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn( + name = "employeeId", + insertable = false, + updatable = false, + foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT) + ) + @NotFound(action = NotFoundAction.IGNORE) + private Employee employee; + + @Column(name = "employeeId") + private int employeeId; + } + + @Entity(name = "Employee") + public static class Employee { + @Id + private int id; + + private String name; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "locationId", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT)) + private Location location; + } + + @Entity(name = "Location") + public static class Location { + @Id + private int id; + + private String description; + } +} From 19138ef5397f5950de222a7460f1ca007bde4d6b Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Sun, 28 Mar 2021 15:13:00 -0700 Subject: [PATCH 2/3] HHH-14537 EntityNotFoundException thrown when non-existing association with @NotFound(IGNORE) mapped has proxy in PersistenceContext (cherry picked from commit 3c1e16e06ce7232fbe69bf1a3322d3f251a8bfbb) --- .../event/internal/DefaultLoadEventListener.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java index 4c9474f985dc..aec1ebc10c91 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java @@ -385,10 +385,17 @@ private Object returnNarrowedProxy( if ( !options.isAllowProxyCreation() ) { impl = load( event, persister, keyToLoad, options ); if ( impl == null ) { - event.getSession() - .getFactory() - .getEntityNotFoundDelegate() - .handleEntityNotFound( persister.getEntityName(), keyToLoad.getIdentifier() ); + if ( options == LoadEventListener.INTERNAL_LOAD_NULLABLE ) { + // The proxy is for a non-existing association mapped as @NotFound. + // Don't throw an exeption; just return null. + return null; + } + else { + event.getSession() + .getFactory() + .getEntityNotFoundDelegate() + .handleEntityNotFound( persister.getEntityName(), keyToLoad.getIdentifier() ); + } } } From 6bb55764a24738ab2050b311545028b4c6da5cad Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Mon, 29 Mar 2021 14:58:37 -0700 Subject: [PATCH 3/3] HHH-14537 : Updated test as requested; added tests that lazy associations with non-existing entity is unaffected. (cherry picked from commit ad84aaf0bcb161b272731c82742018ad3445742b) --- .../test/notfound/EagerProxyNotFoundTest.java | 78 +++++++++++++++---- 1 file changed, 64 insertions(+), 14 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/test/notfound/EagerProxyNotFoundTest.java b/hibernate-core/src/test/java/org/hibernate/test/notfound/EagerProxyNotFoundTest.java index 996559263d4f..d7b77a29d31f 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/notfound/EagerProxyNotFoundTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/notfound/EagerProxyNotFoundTest.java @@ -6,7 +6,6 @@ */ package org.hibernate.test.notfound; -import javax.persistence.Column; import javax.persistence.ConstraintMode; import javax.persistence.Entity; import javax.persistence.EntityNotFoundException; @@ -18,6 +17,7 @@ import org.hibernate.annotations.NotFound; import org.hibernate.annotations.NotFoundAction; +import org.hibernate.proxy.HibernateProxy; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; @@ -28,6 +28,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** @@ -41,15 +43,14 @@ public void testNoProxyInSession() { doInHibernate( this::sessionFactory, session -> { final Task task = new Task(); task.id = 1; - task.employeeId = 2; + task.employeeEagerNotFoundIgnore = session.load( Employee.class, 2 ); session.persist( task ); }); doInHibernate( this::sessionFactory, session -> { final Task task = session.createQuery( "from Task", Task.class ).getSingleResult(); assertNotNull( task ); - assertEquals( 2, task.employeeId ); - assertNull( task.employee ); + assertNull( task.employeeEagerNotFoundIgnore ); }); } @@ -58,7 +59,7 @@ public void testNonExistingProxyInSession() { doInHibernate( this::sessionFactory, session -> { final Task task = new Task(); task.id = 1; - task.employeeId = 2; + task.employeeEagerNotFoundIgnore = session.load( Employee.class, 2 ); session.persist( task ); }); @@ -66,8 +67,49 @@ public void testNonExistingProxyInSession() { session.load( Employee.class, 2 ); final Task task = session.createQuery( "from Task", Task.class ).getSingleResult(); assertNotNull( task ); - assertEquals( 2, task.employeeId ); - assertNull( task.employee ); + assertNull( task.employeeEagerNotFoundIgnore ); + }); + } + + @Test + public void testEagerIgnoreLazyProxy() { + doInHibernate( this::sessionFactory, session -> { + final Task task = new Task(); + task.id = 1; + task.employeeLazy = session.load( Employee.class, 2 ); + task.employeeEagerNotFoundIgnore = task.employeeLazy; + session.persist( task ); + }); + + doInHibernate( this::sessionFactory, session -> { + final Task task = session.createQuery( "from Task", Task.class ).getSingleResult(); + assertNotNull( task ); + assertNull( task.employeeEagerNotFoundIgnore ); + assertNotNull( task.employeeLazy ); + assertTrue( HibernateProxy.class.isInstance( task.employeeLazy ) ); + assertEquals( 2, task.employeeLazy.getId() ); + }); + } + + @Test + public void testProxyInSessionEagerIgnoreLazyProxy() { + doInHibernate( this::sessionFactory, session -> { + final Task task = new Task(); + task.id = 1; + task.employeeLazy = session.load( Employee.class, 2 ); + task.employeeEagerNotFoundIgnore = task.employeeLazy; + session.persist( task ); + }); + + doInHibernate( this::sessionFactory, session -> { + final Employee employeeProxy = session.load( Employee.class, 2 ); + final Task task = session.createQuery( "from Task", Task.class ).getSingleResult(); + assertNotNull( task ); + assertNull( task.employeeEagerNotFoundIgnore ); + assertNotNull( task.employeeLazy ); + assertTrue( HibernateProxy.class.isInstance( task.employeeLazy ) ); + assertEquals( 2, task.employeeLazy.getId() ); + assertSame( employeeProxy, task.employeeLazy ); }); } @@ -80,8 +122,7 @@ public void testExistingProxyWithNonExistingAssociation() { final Task task = new Task(); task.id = 2; - task.employee = employee; - task.employeeId = 1; + task.employeeEagerNotFoundIgnore = employee; session.persist( task ); session.flush(); @@ -129,15 +170,17 @@ public static class Task { @ManyToOne(fetch = FetchType.EAGER) @JoinColumn( name = "employeeId", - insertable = false, - updatable = false, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT) ) @NotFound(action = NotFoundAction.IGNORE) - private Employee employee; + private Employee employeeEagerNotFoundIgnore; - @Column(name = "employeeId") - private int employeeId; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn( + name = "lazyEmployeeId", + foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT) + ) + private Employee employeeLazy; } @Entity(name = "Employee") @@ -150,6 +193,13 @@ public static class Employee { @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "locationId", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT)) private Location location; + + public int getId() { + return id; + } + public void setId(int id) { + this.id = id; + } } @Entity(name = "Location")