From 6ec996f1d5c8d0d5f6d3c36fe11e9a8aa4d9cfa7 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Sun, 16 Jan 2022 21:47:02 -0600 Subject: [PATCH] WrongClassException tests --- .../inheritance/cache/ExtendedEntity.java | 6 +- .../cache/InheritedNaturalIdCacheTest.java | 175 +++++++++++------- .../cache/InheritedNaturalIdNoCacheTest.java | 108 ++++++----- .../naturalid/inheritance/cache/MyEntity.java | 25 ++- 4 files changed, 175 insertions(+), 139 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/inheritance/cache/ExtendedEntity.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/inheritance/cache/ExtendedEntity.java index 693d2b933a..f5aa2464e9 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/inheritance/cache/ExtendedEntity.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/inheritance/cache/ExtendedEntity.java @@ -11,11 +11,11 @@ import jakarta.persistence.Entity; @Entity public class ExtendedEntity extends MyEntity { - public ExtendedEntity() { + protected ExtendedEntity() { } - public ExtendedEntity(final String uid, final String extendedValue) { - super( uid ); + public ExtendedEntity(Integer id, String uid, String extendedValue) { + super( id, uid ); this.extendedValue = extendedValue; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/inheritance/cache/InheritedNaturalIdCacheTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/inheritance/cache/InheritedNaturalIdCacheTest.java index 17af821afb..3873fa7c5f 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/inheritance/cache/InheritedNaturalIdCacheTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/inheritance/cache/InheritedNaturalIdCacheTest.java @@ -6,8 +6,9 @@ */ package org.hibernate.orm.test.mapping.naturalid.inheritance.cache; -import org.hibernate.WrongClassException; +import org.hibernate.cache.spi.CacheImplementor; import org.hibernate.cfg.AvailableSettings; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.NotImplementedYet; @@ -19,22 +20,122 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; +import static org.assertj.core.api.Assertions.assertThat; @ServiceRegistry( settings = @Setting( name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "true" ) ) @DomainModel( annotatedClasses = { MyEntity.class, ExtendedEntity.class } ) @SessionFactory public class InheritedNaturalIdCacheTest { + + @Test + public void testProperLoadingByNaturalId(SessionFactoryScope scope) { + // load the entities properly by natural-id + scope.inTransaction( + (session) -> { + final MyEntity base = session.bySimpleNaturalId( MyEntity.class ).load( "base" ); + assertThat( base ).isNotNull(); + + final ExtendedEntity extended = session.bySimpleNaturalId( ExtendedEntity.class ).load( "extended" ); + assertThat( extended ).isNotNull(); + } + ); + } + + @Test + public void testLoadWrongClassById(SessionFactoryScope scope) { + clearCaches( scope ); + + // try to load `MyEntity#1` as an ExtendedEntity + scope.inTransaction( (session) -> { + final ExtendedEntity loaded = session.byId( ExtendedEntity.class ).load( 1 ); + assertThat( loaded ).isNull(); + } ); + } + + @Test + @NotImplementedYet( + strict = false, + reason = "Because `MyEntity#1` is stored in the second level cache, the attempt to " + + "access it throws `WrongClassException`. Not consistent with what happens " + + "when the entity is not in the cache - `#testLoadWrongClassById`" + ) + public void testLoadWrongClassByIdFromCache(SessionFactoryScope scope) { + clearCaches( scope ); + + // load `MyEntity#1` into the cache + scope.inTransaction( (session) -> { + final MyEntity loaded = session.byId( MyEntity.class ).load( 1 ); + assertThat( loaded ).isNotNull(); + } ); + + final CacheImplementor cache = scope.getSessionFactory().getCache(); + assertThat( cache.containsEntity( MyEntity.class, 1 ) ).isTrue(); + + // now try to access it as an ExtendedEntity + scope.inTransaction( (session) -> { + final ExtendedEntity loaded = session.byId( ExtendedEntity.class ).load( 1 ); + assertThat( loaded ).isNull(); + } ); + } + + @Test + public void testLoadWrongClassByNaturalId(SessionFactoryScope scope) { + clearCaches( scope ); + + // try to load `MyEntity#1` as an ExtendedEntity + scope.inTransaction( (session) -> { + final ExtendedEntity loaded = session.bySimpleNaturalId( ExtendedEntity.class ).load( "base" ); + assertThat( loaded ).isNull(); + } ); + } + + @Test + @NotImplementedYet( + strict = false, + reason = "Because `MyEntity#1` is stored in the second level cache, the attempt to " + + "access it throws `WrongClassException`. Not consistent with what happens " + + "when the entity is not in the cache - `#testLoadWrongClassByNaturalId`" + ) + public void testLoadWrongClassByNaturalIdFromCache(SessionFactoryScope scope) { + clearCaches( scope ); + + // load `MyEntity#1` into the cache + scope.inTransaction( (session) -> { + final MyEntity loaded = session.bySimpleNaturalId( MyEntity.class ).load( "base" ); + assertThat( loaded ).isNotNull(); + } ); + + final CacheImplementor cache = scope.getSessionFactory().getCache(); + assertThat( cache.containsEntity( MyEntity.class, 1 ) ).isTrue(); + + // now try to access it as an ExtendedEntity + scope.inTransaction( (session) -> { + final ExtendedEntity loaded = session.bySimpleNaturalId( ExtendedEntity.class ).load( "base" ); + assertThat( loaded ).isNull(); + } ); + } + + + private void clearCaches(SessionFactoryScope scope) { + final SessionFactoryImplementor sessionFactory = scope.getSessionFactory(); + final CacheImplementor cache = sessionFactory.getCache(); + + cache.evictEntityData( MyEntity.class ); + cache.evictEntityData( ExtendedEntity.class ); + + cache.evictNaturalIdData( MyEntity.class ); + cache.evictNaturalIdData( ExtendedEntity.class ); + } + @BeforeEach public void createTestData(SessionFactoryScope scope) { // create the data: // MyEntity#1 - // ExtendedEntity#1 + // ExtendedEntity#2 scope.inTransaction( (session) -> { - session.save( new MyEntity( "base" ) ); - session.save( new ExtendedEntity( "extended", "ext" ) ); + session.save( new MyEntity( 1, "base" ) ); + session.save( new ExtendedEntity( 2, "extended", "ext" ) ); } ); } @@ -45,66 +146,4 @@ public class InheritedNaturalIdCacheTest { (session) -> session.createQuery( "delete MyEntity" ).executeUpdate() ); } - - @Test - @NotImplementedYet( - reason = "We do not throw `WrongClassException` atm, we just return null", - strict = false - ) - public void testLoadingInheritedEntitiesByNaturalId(SessionFactoryScope scope) { - // load the entities "properly" by natural-id - scope.inTransaction( - (session) -> { - final MyEntity entity = session.bySimpleNaturalId( MyEntity.class ).load( "base" ); - assertNotNull( entity ); - - final ExtendedEntity extendedEntity = session.bySimpleNaturalId( ExtendedEntity.class ).load( "extended" ); - assertNotNull( extendedEntity ); - } - ); - - // baseline: loading the wrong class by id... - - scope.inTransaction( - (session) -> { - try { - session.byId( ExtendedEntity.class ).load( 1L ); - fail( "Expecting WrongClassException" ); - } - catch (WrongClassException expected) { - // expected outcome - } - catch (Exception other) { - throw new AssertionError( - "Unexpected exception type : " + other.getClass().getName(), - other - ); - } - } - ); - - // finally, attempt to load MyEntity#1 as an ExtendedEntity, which should - // throw a `WrongClassException` same as above with loading by id - // - // NOTE : this is what fails currently - scope.inTransaction( - (session) -> { - try { - session.bySimpleNaturalId( ExtendedEntity.class ).load( "base" ); - fail( "Expecting WrongClassException" ); - } - catch (WrongClassException expected) { - // expected outcome - } - catch (Exception other) { - throw new AssertionError( - "Unexpected exception type : " + other.getClass().getName(), - other - ); - } - } - ); - - } - } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/inheritance/cache/InheritedNaturalIdNoCacheTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/inheritance/cache/InheritedNaturalIdNoCacheTest.java index e12635180a..17a2fdbb6c 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/inheritance/cache/InheritedNaturalIdNoCacheTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/inheritance/cache/InheritedNaturalIdNoCacheTest.java @@ -17,9 +17,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; @ServiceRegistry( settings = @Setting( name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "false" ) ) @DomainModel( annotatedClasses = { MyEntity.class, ExtendedEntity.class } ) @@ -30,8 +28,8 @@ public class InheritedNaturalIdNoCacheTest { public void createTestData(SessionFactoryScope scope) { scope.inTransaction( (session) -> { - session.persist( new MyEntity( "base" ) ); - session.persist( new ExtendedEntity( "extended", "ext" ) ); + session.persist( new MyEntity( 1, "base" ) ); + session.persist( new ExtendedEntity( 2, "extended", "ext" ) ); } ); } @@ -45,69 +43,69 @@ public class InheritedNaturalIdNoCacheTest { @Test public void testLoadRoot(SessionFactoryScope scope) { - scope.inTransaction( - (session) -> { - final MyEntity myEntity = session - .byNaturalId( MyEntity.class ) - .using( "uid", "base" ) - .load(); - assertThat( myEntity, notNullValue() ); - } - ); + scope.inTransaction( (session) -> { + final MyEntity myEntity = session + .byNaturalId( MyEntity.class ) + .using( "uid", "base" ) + .load(); + assertThat( myEntity ).isNotNull(); + } ); - scope.inTransaction( - (session) -> { - final MyEntity myEntity = session - .bySimpleNaturalId( MyEntity.class ) - .load( "base" ); - assertThat( myEntity, notNullValue() ); - } - ); - - scope.inTransaction( - (session) -> { - final MyEntity myEntity = session - .bySimpleNaturalId( MyEntity.class ) - .load( "extended" ); - assertThat( myEntity, notNullValue() ); - } - ); + scope.inTransaction( (session) -> { + final MyEntity myEntity = session + .bySimpleNaturalId( MyEntity.class ) + .load( "base" ); + assertThat( myEntity ).isNotNull(); + } ); } @Test public void testLoadSubclass(SessionFactoryScope scope) { - scope.inTransaction( - (session) -> { - final ExtendedEntity extendedEntity = session - .byNaturalId( ExtendedEntity.class ) - .using( "uid", "extended" ) - .load(); - assertThat( extendedEntity, notNullValue() ); - } - ); - scope.inTransaction( - (session) -> { - final ExtendedEntity extendedEntity = session - .bySimpleNaturalId( ExtendedEntity.class ) - .load( "extended" ); - assertThat( extendedEntity, notNullValue() ); - } - ); + scope.inTransaction( (session) -> { + final ExtendedEntity extendedEntity = session + .byNaturalId( ExtendedEntity.class ) + .using( "uid", "extended" ) + .load(); + assertThat( extendedEntity ).isNotNull(); + } ); + scope.inTransaction( (session) -> { + final ExtendedEntity extendedEntity = session + .bySimpleNaturalId( ExtendedEntity.class ) + .load( "extended" ); + assertThat( extendedEntity ).isNotNull(); + } ); + + scope.inTransaction( (session) -> { + final MyEntity myEntity = session + .bySimpleNaturalId( MyEntity.class ) + .load( "extended" ); + assertThat( myEntity ).isNotNull(); + } ); } @Test - public void testLoadWrongClass(SessionFactoryScope scope) { + public void testLoadWrongClassById(SessionFactoryScope scope) { // try to access the root (base) entity as subclass (extended) // - the outcome is different here depending on whether: // 1) caching is enabled && the natural-id resolution is cached -> WrongClassException // 2) otherwise -> return null - scope.inTransaction( - (session) -> { - final ExtendedEntity loaded = session.bySimpleNaturalId( ExtendedEntity.class ).load( "base" ); - assertThat( loaded, nullValue() ); - } - ); + scope.inTransaction( (session) -> { + final ExtendedEntity loaded = session.byId( ExtendedEntity.class ).load( 1 ); + assertThat( loaded ).isNull(); + } ); + } + + @Test + public void testLoadWrongClassByNaturalId(SessionFactoryScope scope) { + // try to access the root (base) entity as subclass (extended) + // - the outcome is different here depending on whether: + // 1) caching is enabled && the natural-id resolution is cached -> WrongClassException + // 2) otherwise -> return null + scope.inTransaction( (session) -> { + final ExtendedEntity loaded = session.bySimpleNaturalId( ExtendedEntity.class ).load( "base" ); + assertThat( loaded ).isNull(); + } ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/inheritance/cache/MyEntity.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/inheritance/cache/MyEntity.java index b59e3dd4cf..e543fb8fa8 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/inheritance/cache/MyEntity.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/inheritance/cache/MyEntity.java @@ -6,39 +6,38 @@ */ package org.hibernate.orm.test.mapping.naturalid.inheritance.cache; +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.NaturalId; +import org.hibernate.annotations.NaturalIdCache; + import jakarta.persistence.Cacheable; import jakarta.persistence.Column; import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; -import org.hibernate.annotations.Cache; -import org.hibernate.annotations.CacheConcurrencyStrategy; -import org.hibernate.annotations.GenericGenerator; -import org.hibernate.annotations.NaturalId; - @Entity @Cacheable +@NaturalIdCache @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL) public class MyEntity { - private Long id; + private Integer id; private String uid; - public MyEntity() { + protected MyEntity() { } - public MyEntity(String uid) { + public MyEntity(Integer id, String uid) { + this.id = id; this.uid = uid; } @Id - @GeneratedValue(generator = "increment") - @GenericGenerator(name = "increment", strategy = "increment") - public Long getId() { + public Integer getId() { return id; } - public void setId(Long id) { + public void setId(Integer id) { this.id = id; }