From 955ae4fa6fb9b88e6c7eaa968ce8e4ebf0665105 Mon Sep 17 00:00:00 2001 From: Aleksei Tsvetkov Date: Sat, 12 Oct 2024 09:35:30 +0300 Subject: [PATCH] HHH-18723 Support @SQLRestriction in class marked as @MappedSuperclass --- .../boot/model/internal/EntityBinder.java | 20 +++- .../CustomSqlRestrictionOverridesTest.java | 20 ++-- .../annotations/MappedSuperclassTest.java | 95 +++++++++++++++++++ 3 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/where/annotations/MappedSuperclassTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java index 8734540ad19c..f2eed5fc0b4e 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java @@ -1646,12 +1646,30 @@ private void bindConcreteProxy() { } private void bindWhere() { - final SQLRestriction restriction = getOverridableAnnotation( annotatedClass, SQLRestriction.class, context ); + final SQLRestriction restriction = extractSQLRestriction( annotatedClass, context ); if ( restriction != null ) { this.where = restriction.value(); } } + private static SQLRestriction extractSQLRestriction(ClassDetails classDetails, MetadataBuildingContext context) { + final SourceModelBuildingContext sourceModelContext = context.getMetadataCollector().getSourceModelBuildingContext(); + final SQLRestriction fromClass = getOverridableAnnotation( classDetails, SQLRestriction.class, context ); + if ( fromClass != null ) { + return fromClass; + } + ClassDetails classToCheck = classDetails.getSuperClass(); + while ( classToCheck != null + && classToCheck.hasAnnotationUsage( jakarta.persistence.MappedSuperclass.class, sourceModelContext ) ) { + final SQLRestriction fromSuper = getOverridableAnnotation( classToCheck, SQLRestriction.class, context ); + if ( fromSuper != null ) { + return fromSuper; + } + classToCheck = classToCheck.getSuperClass(); + } + return null; + } + private void bindNaturalIdCache() { final NaturalIdCache naturalIdCacheAnn = annotatedClass.getAnnotationUsage( NaturalIdCache.class, getSourceModelContext() ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/customsql/CustomSqlRestrictionOverridesTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/customsql/CustomSqlRestrictionOverridesTest.java index 18d16f011963..40b8ab4933d1 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/customsql/CustomSqlRestrictionOverridesTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/customsql/CustomSqlRestrictionOverridesTest.java @@ -27,6 +27,7 @@ import java.security.NoSuchAlgorithmException; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; @SessionFactory @DomainModel(annotatedClasses = CustomSqlRestrictionOverridesTest.Secure.class) @@ -39,12 +40,18 @@ public class CustomSqlRestrictionOverridesTest { @Test public void testCustomSql(SessionFactoryScope scope) throws NoSuchAlgorithmException { - Secure sec = new Secure(); - sec.hash = MessageDigest.getInstance( "SHA-256" ).digest("hello".getBytes()); - scope.inTransaction(s -> s.persist(sec) ); - Secure secure = scope.fromTransaction( s -> s.find( Secure.class, sec.id ) ); - assertNotNull(secure); + Secure sec1 = new Secure(); + sec1.hash = MessageDigest.getInstance( "SHA-256" ).digest( "hello".getBytes() ); + scope.inTransaction( s -> s.persist( sec1 ) ); + Secure sec2 = new Secure(); + sec2.hash = MessageDigest.getInstance( "SHA-256" ).digest( "not hello".getBytes() ); + scope.inTransaction( s -> s.persist( sec2 ) ); + Secure secure1 = scope.fromTransaction( s -> s.find( Secure.class, sec1.id ) ); + assertNotNull( secure1 ); + Secure secure2 = scope.fromTransaction( s -> s.find( Secure.class, sec2.id ) ); + assertNull( secure2 ); } + @Entity @Table(name = "SecureTable") @DialectOverride.SQLRestriction(dialect = H2Dialect.class, @@ -60,7 +67,8 @@ public void testCustomSql(SessionFactoryScope scope) throws NoSuchAlgorithmExcep @DialectOverride.SQLRestriction(dialect = OracleDialect.class, override = @SQLRestriction("hash = standard_hash('hello', 'SHA256')")) static class Secure { - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) Long id; byte[] hash; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/where/annotations/MappedSuperclassTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/where/annotations/MappedSuperclassTest.java new file mode 100644 index 000000000000..4506bbf7542f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/where/annotations/MappedSuperclassTest.java @@ -0,0 +1,95 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.where.annotations; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; +import org.hibernate.annotations.SQLRestriction; +import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.Jpa; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@Jpa( + annotatedClasses = { + MappedSuperclassTest.Child.class, + MappedSuperclassTest.SubClass.class + } +) +public class MappedSuperclassTest { + + @AfterEach + public void tearDown(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + entityManager.createQuery( "delete from SubClass" ).executeUpdate(); + entityManager.createQuery( "delete from Child" ).executeUpdate(); + } + ); + } + + @Test + public void testFindParent(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + Child child1 = new SubClass( 1L ); + child1.state = 1; + entityManager.persist( child1 ); + + Child child2 = new Child( 2L ); + child2.state = 0; + entityManager.persist( child2 ); + } + ); + scope.inTransaction( + entityManager -> { + List children = entityManager.createQuery( "select c from Child c", Child.class ) + .getResultList(); + assertThat( children.size() ).isEqualTo( 1 ); + } + ); + } + + @Entity(name = "Child") + public static class Child extends Intermediate { + @Id + private Long id; + + public Child() { + } + + public Child(long id) { + this.id = id; + } + } + + @Entity(name = "SubClass") + public static class SubClass extends Child { + public SubClass() { + } + + public SubClass(long id) { + super( id ); + } + } + + @MappedSuperclass + public static class Intermediate extends Parent { + } + + @MappedSuperclass + @SQLRestriction("state = 0") + public static class Parent { + public Parent() { + } + + int state; + } +}