diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/FilterDefRegistration.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/FilterDefRegistration.java new file mode 100644 index 0000000..06aa818 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/FilterDefRegistration.java @@ -0,0 +1,42 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.internal; + +import java.util.Map; + +import org.hibernate.models.source.spi.ClassDetails; + +/** + * Global registration of a filter definition + * + * @author Marco Belladelli + */ +public class FilterDefRegistration { + private final String name; + + private final String defaultCondition; + + private final Map parameters; + + public FilterDefRegistration(String name, String defaultCondition, Map parameters) { + this.name = name; + this.defaultCondition = defaultCondition; + this.parameters = parameters; + } + + public String getName() { + return name; + } + + public String getDefaultCondition() { + return defaultCondition; + } + + public Map getParameters() { + return parameters; + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/GlobalRegistrations.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/GlobalRegistrations.java index 49db59b..d1c6e6c 100644 --- a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/GlobalRegistrations.java +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/GlobalRegistrations.java @@ -13,7 +13,10 @@ import java.util.List; import java.util.Map; +import org.hibernate.AnnotationException; +import org.hibernate.annotations.FilterDef; import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.ParamDef; import org.hibernate.annotations.Parameter; import org.hibernate.boot.jaxb.mapping.JaxbCollectionUserTypeRegistration; import org.hibernate.boot.jaxb.mapping.JaxbCompositeUserTypeRegistration; @@ -22,6 +25,7 @@ import org.hibernate.boot.jaxb.mapping.JaxbEmbeddableInstantiatorRegistration; import org.hibernate.boot.jaxb.mapping.JaxbEntityListener; import org.hibernate.boot.jaxb.mapping.JaxbEntityMappings; +import org.hibernate.boot.jaxb.mapping.JaxbFilterDef; import org.hibernate.boot.jaxb.mapping.JaxbGenericIdGenerator; import org.hibernate.boot.jaxb.mapping.JaxbJavaTypeRegistration; import org.hibernate.boot.jaxb.mapping.JaxbJdbcTypeRegistration; @@ -43,10 +47,12 @@ import jakarta.persistence.TableGenerator; import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; import static org.hibernate.models.orm.spi.HibernateAnnotations.COLLECTION_TYPE_REG; import static org.hibernate.models.orm.spi.HibernateAnnotations.COMPOSITE_TYPE_REG; import static org.hibernate.models.orm.spi.HibernateAnnotations.CONVERTER_REG; import static org.hibernate.models.orm.spi.HibernateAnnotations.EMBEDDABLE_INSTANTIATOR_REG; +import static org.hibernate.models.orm.spi.HibernateAnnotations.FILTER_DEF; import static org.hibernate.models.orm.spi.HibernateAnnotations.JAVA_TYPE_REG; import static org.hibernate.models.orm.spi.HibernateAnnotations.JDBC_TYPE_REG; import static org.hibernate.models.orm.spi.HibernateAnnotations.TYPE_REG; @@ -67,6 +73,7 @@ public class GlobalRegistrations { private List compositeUserTypeRegistrations; private List collectionTypeRegistrations; private List embeddableInstantiatorRegistrations; + private Map filterDefRegistrations; private Map sequenceGeneratorRegistrations; private Map tableGeneratorRegistrations; @@ -121,6 +128,9 @@ public List getEmbeddableInstantiatorRegistr return embeddableInstantiatorRegistrations == null ? emptyList() : embeddableInstantiatorRegistrations; } + public Map getFilterDefRegistrations() { + return filterDefRegistrations == null ? emptyMap() : filterDefRegistrations; + } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // JavaTypeRegistration @@ -365,6 +375,62 @@ public void collectEmbeddableInstantiatorRegistration(ClassDetails embeddableCla } + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Filter-defs + + public void collectFilterDefinitions(AnnotationTarget annotationTarget) { + annotationTarget.forEachAnnotationUsage( FILTER_DEF, (usage) -> collectFilterDefinition( + usage.getAttributeValue( "name" ), + usage.getAttributeValue( "defaultCondition" ), + extractFilterParameters( usage ) + ) ); + } + + private Map extractFilterParameters(AnnotationUsage source) { + final List> parameters = source.getAttributeValue( "parameters" ); + final Map result = new HashMap<>( parameters.size() ); + for ( AnnotationUsage parameter : parameters ) { + result.put( parameter.getAttributeValue( "name" ), parameter.getAttributeValue( "type" ) ); + } + return result; + } + + public void collectFilterDefinitions(List filterDefinitions) { + if ( CollectionHelper.isEmpty( filterDefinitions ) ) { + return; + } + + filterDefinitions.forEach( (filterDefinition) -> collectFilterDefinition( + filterDefinition.getName(), + filterDefinition.getCondition(), + extractFilterParameters( filterDefinition ) + ) ); + } + + private Map extractFilterParameters(JaxbFilterDef source) { + final List parameters = source.getFilterParam(); + + // todo : update the mapping.xsd to account for new @ParamDef definition + // todo : handle simplified type names for XML, e.g. "String" instead of "java.lang.String" + + final Map result = new HashMap<>( parameters.size() ); + for ( JaxbFilterDef.JaxbFilterParam parameter : parameters ) { + result.put( parameter.getName(), classDetailsRegistry.resolveClassDetails( parameter.getType() ) ); + } + return result; + } + + public void collectFilterDefinition(String name, String defaultCondition, Map parameters) { + if ( filterDefRegistrations == null ) { + filterDefRegistrations = new HashMap<>(); + } + + if ( filterDefRegistrations.put( name, new FilterDefRegistration( name, defaultCondition, parameters ) ) != null ) { + throw new AnnotationException( "Multiple '@FilterDef' annotations define a filter named '" + name + "'" ); + } + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // EntityListenerRegistration diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ProcessResultCollector.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ProcessResultCollector.java index 3f8ddc5..493b846 100644 --- a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ProcessResultCollector.java +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ProcessResultCollector.java @@ -50,6 +50,7 @@ public void apply(JaxbEntityMappings jaxbRoot) { getGlobalRegistrations().collectCompositeUserTypeRegistrations( jaxbRoot.getCompositeUserTypeRegistrations() ); getGlobalRegistrations().collectCollectionTypeRegistrations( jaxbRoot.getCollectionUserTypeRegistrations() ); getGlobalRegistrations().collectEmbeddableInstantiatorRegistrations( jaxbRoot.getEmbeddableInstantiatorRegistrations() ); + getGlobalRegistrations().collectFilterDefinitions( jaxbRoot.getFilterDefinitions() ); final JaxbPersistenceUnitMetadata persistenceUnitMetadata = jaxbRoot.getPersistenceUnitMetadata(); if ( persistenceUnitMetadata != null ) { @@ -74,6 +75,7 @@ public void apply(ClassDetails classDetails) { getGlobalRegistrations().collectCompositeUserTypeRegistrations( classDetails ); getGlobalRegistrations().collectCollectionTypeRegistrations( classDetails ); getGlobalRegistrations().collectEmbeddableInstantiatorRegistrations( classDetails ); + getGlobalRegistrations().collectFilterDefinitions( classDetails ); if ( areIdGeneratorsGlobal ) { getGlobalRegistrations().collectIdGenerators( classDetails ); @@ -91,6 +93,7 @@ public void apply(PackageDetails packageDetails) { getGlobalRegistrations().collectCompositeUserTypeRegistrations( packageDetails ); getGlobalRegistrations().collectCollectionTypeRegistrations( packageDetails ); getGlobalRegistrations().collectEmbeddableInstantiatorRegistrations( packageDetails ); + getGlobalRegistrations().collectFilterDefinitions( packageDetails ); // todo : others? } @@ -115,6 +118,7 @@ public ProcessResult createResult(Set entityHierarchies) { getGlobalRegistrations().getCompositeUserTypeRegistrations(), getGlobalRegistrations().getCollectionTypeRegistrations(), getGlobalRegistrations().getEmbeddableInstantiatorRegistrations(), + getGlobalRegistrations().getFilterDefRegistrations(), // jpaNamedQueries == null ? emptyMap() : jpaNamedQueries, // hibernateNamedHqlQueries == null ? emptyMap() : hibernateNamedHqlQueries, // hibernateNamedNativeQueries == null ? emptyMap() : hibernateNamedNativeQueries diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ProcessResultImpl.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ProcessResultImpl.java index 2fb0706..bbc892f 100644 --- a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ProcessResultImpl.java +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ProcessResultImpl.java @@ -28,6 +28,7 @@ public class ProcessResultImpl implements ProcessResult { private final List compositeUserTypeRegistrations; private final List collectionTypeRegistrations; private final List embeddableInstantiatorRegistrations; + private final Map filterDefRegistrations; private final Map jpaNamedQueries; private final Map hibernateNamedHqlQueries; private final Map hibernateNamedNativeQueries; @@ -42,6 +43,7 @@ public ProcessResultImpl( List compositeUserTypeRegistrations, List collectionTypeRegistrations, List embeddableInstantiatorRegistrations, + Map filterDefRegistrations, Map jpaNamedQueries, Map hibernateNamedHqlQueries, Map hibernateNamedNativeQueries) { @@ -54,6 +56,7 @@ public ProcessResultImpl( this.compositeUserTypeRegistrations = compositeUserTypeRegistrations; this.collectionTypeRegistrations = collectionTypeRegistrations; this.embeddableInstantiatorRegistrations = embeddableInstantiatorRegistrations; + this.filterDefRegistrations = filterDefRegistrations; this.jpaNamedQueries = jpaNamedQueries; this.hibernateNamedHqlQueries = hibernateNamedHqlQueries; this.hibernateNamedNativeQueries = hibernateNamedNativeQueries; @@ -104,6 +107,11 @@ public List getEmbeddableInstantiatorRegistr return embeddableInstantiatorRegistrations; } + @Override + public Map getFilterDefRegistrations() { + return filterDefRegistrations; + } + @Override public Map getJpaNamedQueries() { return jpaNamedQueries; diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/ProcessResult.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/ProcessResult.java index e7e3cc6..ce9ee9d 100644 --- a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/ProcessResult.java +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/ProcessResult.java @@ -14,6 +14,7 @@ import org.hibernate.models.orm.internal.CompositeUserTypeRegistration; import org.hibernate.models.orm.internal.ConversionRegistration; import org.hibernate.models.orm.internal.EmbeddableInstantiatorRegistration; +import org.hibernate.models.orm.internal.FilterDefRegistration; import org.hibernate.models.orm.internal.JavaTypeRegistration; import org.hibernate.models.orm.internal.JdbcTypeRegistration; import org.hibernate.models.orm.internal.NamedQueryRegistration; @@ -44,6 +45,8 @@ public interface ProcessResult { List getEmbeddableInstantiatorRegistrations(); + Map getFilterDefRegistrations(); + Map getJpaNamedQueries(); Map getHibernateNamedHqlQueries(); diff --git a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/Person.java b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/Person.java index faca3b6..e5a34da 100644 --- a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/Person.java +++ b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/Person.java @@ -9,8 +9,10 @@ import java.util.UUID; import org.hibernate.annotations.ConverterRegistration; +import org.hibernate.annotations.FilterDef; import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.JavaTypeRegistration; +import org.hibernate.annotations.ParamDef; import org.hibernate.id.IncrementGenerator; import org.hibernate.type.descriptor.java.StringJavaType; @@ -38,6 +40,7 @@ @NamedStoredProcedureQuery(name = "jpaCallable", procedureName = "jpa_callable") @org.hibernate.annotations.NamedQuery(name = "ormHql", query = "from Person") @org.hibernate.annotations.NamedNativeQuery(name = "ormNative", query = "select * from persons") +@FilterDef(name = "name_filter", defaultCondition = "name = :name", parameters = {@ParamDef(name = "name", type = String.class)}) public class Person { @Id private Integer id; diff --git a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/SimpleProcessorTests.java b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/SimpleProcessorTests.java index e147048..8225c14 100644 --- a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/SimpleProcessorTests.java +++ b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/SimpleProcessorTests.java @@ -7,7 +7,9 @@ package org.hibernate.models.orm.process; import java.util.Iterator; +import java.util.Map; +import org.hibernate.models.orm.internal.FilterDefRegistration; import org.hibernate.models.orm.internal.ManagedResourcesImpl; import org.hibernate.models.orm.spi.EntityHierarchy; import org.hibernate.models.orm.spi.EntityTypeMetadata; @@ -17,6 +19,7 @@ import org.hibernate.models.orm.spi.Processor; import org.hibernate.models.source.SourceModelTestHelper; import org.hibernate.models.source.internal.SourceModelBuildingContextImpl; +import org.hibernate.models.source.spi.ClassDetails; import org.hibernate.type.CharBooleanConverter; import org.hibernate.type.YesNoConverter; import org.hibernate.type.descriptor.converter.spi.BasicValueConverter; @@ -111,6 +114,8 @@ public boolean shouldIgnoreUnlistedClasses() { validatePersonHierarchy( two ); validateJoinedHierarchy( one ); } + + validateFilterDefs( processResult.getFilterDefRegistrations() ); } private void validatePersonHierarchy(EntityHierarchy hierarchy) { @@ -145,4 +150,15 @@ private void validateJoinedHierarchy(EntityHierarchy hierarchy) { assertThat( subMetadata.hasSubTypes() ).isFalse(); assertThat( subMetadata.getNumberOfSubTypes() ).isEqualTo( 0 ); } + + private void validateFilterDefs(Map filterDefRegistrations) { + assertThat( filterDefRegistrations ).hasSize( 1 ); + assertThat( filterDefRegistrations ).containsKey( "name_filter" ); + final FilterDefRegistration nameFilter = filterDefRegistrations.get( "name_filter" ); + assertThat( nameFilter.getDefaultCondition() ).isEqualTo( "name = :name" ); + final Map parameters = nameFilter.getParameters(); + assertThat( parameters ).hasSize( 1 ); + assertThat( parameters ).containsKey( "name" ); + assertThat( parameters.get( "name" ).getName() ).isEqualTo( String.class.getName() ); + } } diff --git a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/XmlProcessingSmokeTests.java b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/XmlProcessingSmokeTests.java index f0b6f1b..a267f92 100644 --- a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/XmlProcessingSmokeTests.java +++ b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/XmlProcessingSmokeTests.java @@ -7,8 +7,10 @@ package org.hibernate.models.orm.xml; import java.util.List; +import java.util.Map; import org.hibernate.boot.jaxb.mapping.JaxbEntityMappings; +import org.hibernate.models.orm.internal.FilterDefRegistration; import org.hibernate.models.orm.internal.GlobalRegistrations; import org.hibernate.models.orm.internal.ProcessResultCollector; import org.hibernate.models.orm.xml.internal.XmlDocumentImpl; @@ -16,6 +18,7 @@ import org.hibernate.models.orm.xml.spi.PersistenceUnitMetadata; import org.hibernate.models.source.SourceModelTestHelper; import org.hibernate.models.source.internal.StringTypeDescriptor; +import org.hibernate.models.source.spi.ClassDetails; import org.hibernate.models.source.spi.SourceModelBuildingContext; import org.hibernate.type.descriptor.jdbc.ClobJdbcType; @@ -134,5 +137,18 @@ void testSimpleGlobalXmlProcessing() { assertThat( globalRegistrations.getConverterRegistrations() ).hasSize( 1 ); assertThat( globalRegistrations.getConverterRegistrations().get(0).getConverterType().getClassName() ) .isEqualTo( org.hibernate.type.YesNoConverter.class.getName() ); + + validateFilterDefs( globalRegistrations.getFilterDefRegistrations() ); + } + + private void validateFilterDefs(Map filterDefRegistrations) { + assertThat( filterDefRegistrations ).hasSize( 1 ); + assertThat( filterDefRegistrations ).containsKey( "amount_filter" ); + final FilterDefRegistration filterDef = filterDefRegistrations.get( "amount_filter" ); + assertThat( filterDef.getDefaultCondition() ).isEqualTo( "amount = :amount" ); + final Map parameters = filterDef.getParameters(); + assertThat( parameters ).hasSize( 1 ); + assertThat( parameters ).containsKey( "amount" ); + assertThat( parameters.get( "amount" ).getName() ).isEqualTo( Integer.class.getName() ); } } diff --git a/hibernate-models-orm/src/test/resources/mappings/globals.xml b/hibernate-models-orm/src/test/resources/mappings/globals.xml index 6aa0a17..9e5c66b 100644 --- a/hibernate-models-orm/src/test/resources/mappings/globals.xml +++ b/hibernate-models-orm/src/test/resources/mappings/globals.xml @@ -13,4 +13,9 @@ + + + + amount = :amount +