diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/hbm/transform/HbmXmlTransformer.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/hbm/transform/HbmXmlTransformer.java index 28d1dcd674..4dfe09011a 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/hbm/transform/HbmXmlTransformer.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/hbm/transform/HbmXmlTransformer.java @@ -205,7 +205,6 @@ public class HbmXmlTransformer { final JaxbPersistenceUnitMetadata metadata = new JaxbPersistenceUnitMetadata(); ormRoot.setPersistenceUnitMetadata( metadata ); - metadata.setXmlMappingMetadataComplete( new JaxbEmptyType() ); transfer( hbmXmlMapping::getPackage, ormRoot::setPackage ); transfer( hbmXmlMapping::getCatalog, ormRoot::setCatalog ); diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/JPAXMLOverriddenAnnotationReader.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/JPAXMLOverriddenAnnotationReader.java index 74456c0a56..9ab263228b 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/JPAXMLOverriddenAnnotationReader.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/JPAXMLOverriddenAnnotationReader.java @@ -89,6 +89,7 @@ import org.hibernate.boot.jaxb.mapping.JaxbSecondaryTable; import org.hibernate.boot.jaxb.mapping.JaxbSequenceGenerator; import org.hibernate.boot.jaxb.mapping.JaxbSqlResultSetMapping; import org.hibernate.boot.jaxb.mapping.JaxbStoredProcedureParameter; +import org.hibernate.boot.jaxb.mapping.JaxbSynchronizedTable; import org.hibernate.boot.jaxb.mapping.JaxbTable; import org.hibernate.boot.jaxb.mapping.JaxbTableGenerator; import org.hibernate.boot.jaxb.mapping.JaxbUniqueConstraint; @@ -102,6 +103,7 @@ import org.hibernate.cfg.annotations.reflection.PersistentAttributeFilter; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.StringHelper; +import org.hibernate.jpa.AvailableHints; import jakarta.persistence.Access; import jakarta.persistence.AccessType; @@ -2201,7 +2203,7 @@ public class JPAXMLOverriddenAnnotationReader implements AnnotationReader { ann.setValue( "resultSetMappings", element.getResultSetMapping().toArray( new String[0] ) ); - buildQueryHints( element.getHint(), ann ); + buildQueryHints( element.getHint(), ann, Collections.emptyMap() ); namedStoredProcedureQueries.add( AnnotationFactory.create( ann ) ); } return namedStoredProcedureQueries; @@ -2530,14 +2532,26 @@ public class JPAXMLOverriddenAnnotationReader implements AnnotationReader { } } - private static void buildQueryHints(List elements, AnnotationDescriptor ann) { - List queryHints = new ArrayList<>( elements.size() ); - for ( JaxbQueryHint hint : elements ) { + private static void buildQueryHints( + List elements, + AnnotationDescriptor ann, + Map additionalHints) { + List queryHints = new ArrayList<>( elements.size() + additionalHints.size() ); + for ( Map.Entry entry : additionalHints.entrySet() ) { AnnotationDescriptor hintDescriptor = new AnnotationDescriptor( QueryHint.class ); + hintDescriptor.setValue( "name", entry.getKey() ); + hintDescriptor.setValue( "value", entry.getValue() ); + queryHints.add( AnnotationFactory.create( hintDescriptor ) ); + } + for ( JaxbQueryHint hint : elements ) { String value = hint.getName(); if ( value == null ) { throw new AnnotationException( " without name. " + SCHEMA_VALIDATION ); } + if ( additionalHints.containsKey( value ) ) { + continue; + } + AnnotationDescriptor hintDescriptor = new AnnotationDescriptor( QueryHint.class ); hintDescriptor.setValue( "name", value ); value = hint.getValue(); if ( value == null ) { @@ -2558,7 +2572,15 @@ public class JPAXMLOverriddenAnnotationReader implements AnnotationReader { AnnotationDescriptor ann = new AnnotationDescriptor( NamedQuery.class ); copyAttribute( ann, "name", element.getName(), false ); copyAttribute( ann, "query", element.getQuery(), true ); - buildQueryHints( element.getHint(), ann ); + Map additionalHints = new HashMap<>(); + addHint( additionalHints, AvailableHints.HINT_CACHEABLE, element.isCacheable() ); + addHint( additionalHints, AvailableHints.HINT_CACHE_MODE, element.getCacheMode() ); + addHint( additionalHints, AvailableHints.HINT_CACHE_REGION, element.getCacheRegion() ); + addHint( additionalHints, AvailableHints.HINT_COMMENT, element.getComment() ); + addHint( additionalHints, AvailableHints.HINT_FETCH_SIZE, element.getFetchSize() ); + addHint( additionalHints, AvailableHints.HINT_FLUSH_MODE, element.getFlushMode() ); + addHint( additionalHints, AvailableHints.HINT_TIMEOUT, element.getTimeout() ); + buildQueryHints( element.getHint(), ann, additionalHints ); copyAttribute( ann, "lock-mode", element.getLockMode(), false ); namedQueries.add( AnnotationFactory.create( ann ) ); } @@ -2574,7 +2596,16 @@ public class JPAXMLOverriddenAnnotationReader implements AnnotationReader { AnnotationDescriptor ann = new AnnotationDescriptor( NamedNativeQuery.class ); copyAttribute( ann, "name", element.getName(), false ); copyAttribute( ann, "query", element.getQuery(), true ); - buildQueryHints( element.getHint(), ann ); + Map additionalHints = new HashMap<>(); + addHint( additionalHints, AvailableHints.HINT_CACHEABLE, element.isCacheable() ); + addHint( additionalHints, AvailableHints.HINT_CACHE_MODE, element.getCacheMode() ); + addHint( additionalHints, AvailableHints.HINT_CACHE_REGION, element.getCacheRegion() ); + addHint( additionalHints, AvailableHints.HINT_COMMENT, element.getComment() ); + addHint( additionalHints, AvailableHints.HINT_FETCH_SIZE, element.getFetchSize() ); + addHint( additionalHints, AvailableHints.HINT_FLUSH_MODE, element.getFlushMode() ); + addHint( additionalHints, AvailableHints.HINT_TIMEOUT, element.getTimeout() ); + addSynchronizationsHint( additionalHints, element.getSynchronizations() ); + buildQueryHints( element.getHint(), ann, additionalHints ); String clazzName = element.getResultClass(); if ( StringHelper.isNotEmpty( clazzName ) ) { Class clazz; @@ -2594,6 +2625,27 @@ public class JPAXMLOverriddenAnnotationReader implements AnnotationReader { return namedQueries; } + private static void addHint(Map hints, String hint, Object value) { + if ( value != null ) { + hints.put( hint, value.toString() ); + } + } + + private static void addSynchronizationsHint( + Map hints, + List synchronizations) { + if ( synchronizations.isEmpty() ) { + return; + } + StringBuilder sb = new StringBuilder(); + sb.append( synchronizations.get( 0 ).getTable() ); + for ( int i = 1; i < synchronizations.size(); i++ ) { + sb.append( ' ' ); + sb.append( synchronizations.get( i ).getTable() ); + } + hints.put( AvailableHints.HINT_NATIVE_SPACES, sb.toString() ); + } + private TableGenerator getTableGenerator(ManagedType root, XMLContext.Default defaults) { return getTableGenerator( root instanceof JaxbEntity ? ( (JaxbEntity) root ).getTableGenerator() : null, diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hbm/query/Bar.java b/hibernate-core/src/test/java/org/hibernate/orm/test/hbm/query/Bar.java new file mode 100644 index 0000000000..2d917781c8 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/hbm/query/Bar.java @@ -0,0 +1,30 @@ +package org.hibernate.orm.test.hbm.query; + +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.NamedQueries; +import jakarta.persistence.NamedQuery; + +@Entity(name = "Bar") +@NamedQueries({ + @NamedQuery(name = Bar.FIND_ALL, query = "select b from Bar b") +}) +public class Bar { + public static final String FIND_ALL = "Bar.findAll"; + + @EmbeddedId + private BarPK id; + private String name; + + public BarPK getID() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hbm/query/BarPK.java b/hibernate-core/src/test/java/org/hibernate/orm/test/hbm/query/BarPK.java new file mode 100644 index 0000000000..6b8a33fed3 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/hbm/query/BarPK.java @@ -0,0 +1,22 @@ +package org.hibernate.orm.test.hbm.query; + +public class BarPK { + String id1; + String id2; + + public String getId1() { + return id1; + } + + public void setId1(String id1) { + this.id1 = id1; + } + + public String getId2() { + return id2; + } + + public void setId2(String id2) { + this.id2 = id2; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hbm/query/HbmNamedQueryConfigurationTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/hbm/query/HbmNamedQueryConfigurationTest.java new file mode 100644 index 0000000000..9fe8dc692d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/hbm/query/HbmNamedQueryConfigurationTest.java @@ -0,0 +1,51 @@ +/* + * 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.hbm.query; + +import java.util.Map; + +import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase; +import org.hibernate.query.named.NamedObjectRepository; +import org.hibernate.query.sqm.spi.NamedSqmQueryMemento; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.bytecode.enhancement.EnhancementOptions; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@RunWith(BytecodeEnhancerRunner.class) +@EnhancementOptions(inlineDirtyChecking = true, lazyLoading = true, extendedEnhancement = true) +public class HbmNamedQueryConfigurationTest extends BaseEntityManagerFunctionalTestCase { + @Override + protected String[] getMappings() { + return new String[]{ + "org/hibernate/orm/test/hbm/query/HbmOverridesAnnotation.orm.xml", + "org/hibernate/orm/test/hbm/query/HbmOverridesAnnotation.hbm.xml" + }; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + protected void addConfigOptions(Map options) { + options.put( "hibernate.enable_specj_proprietary_syntax", "true" ); + options.put( "hibernate.transform_hbm_xml.enabled", "true" ); + } + + @Test + @TestForIssue( jiraKey = { "HHH-15619", "HHH-15620"} ) + public void testHbmOverride() { + NamedObjectRepository namedObjectRepository = entityManagerFactory() + .getQueryEngine() + .getNamedObjectRepository(); + NamedSqmQueryMemento sqmQueryMemento = namedObjectRepository.getSqmQueryMemento( Bar.FIND_ALL ); + assertTrue( sqmQueryMemento.getCacheable() ); + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hbm/query/HbmOverridesAnnotation.hbm.xml b/hibernate-core/src/test/java/org/hibernate/orm/test/hbm/query/HbmOverridesAnnotation.hbm.xml new file mode 100644 index 0000000000..a121a84273 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/hbm/query/HbmOverridesAnnotation.hbm.xml @@ -0,0 +1,9 @@ + + + + + + select b from Bar b + + diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hbm/query/HbmOverridesAnnotation.orm.xml b/hibernate-core/src/test/java/org/hibernate/orm/test/hbm/query/HbmOverridesAnnotation.orm.xml new file mode 100644 index 0000000000..5686f4466d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/hbm/query/HbmOverridesAnnotation.orm.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + +