From 0bb04b102136a3703cfbfa6b865169450002eb2e Mon Sep 17 00:00:00 2001 From: Gavin Date: Tue, 24 Jan 2023 22:07:11 +0100 Subject: [PATCH] HHH-16089 allow @Synchronize for a collection --- .../hibernate/annotations/Synchronize.java | 25 +++++++++++-------- .../boot/model/internal/CollectionBinder.java | 25 +++++++++++++++++++ .../org/hibernate/mapping/Collection.java | 4 +++ .../hibernate/query/SynchronizeableQuery.java | 6 ++--- 4 files changed, 47 insertions(+), 13 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/Synchronize.java b/hibernate-core/src/main/java/org/hibernate/annotations/Synchronize.java index d142d1d181..cc77cd9bde 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/Synchronize.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/Synchronize.java @@ -9,23 +9,28 @@ package org.hibernate.annotations; import java.lang.annotation.Retention; import java.lang.annotation.Target; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; /** - * Specifies the tables that hold state mapped by the annotated entity. + * Specifies a table or tables that hold state mapped by the annotated + * entity or collection. *

* If Hibernate is not aware that a certain table holds state mapped - * by an entity class, then {@linkplain org.hibernate.FlushMode#AUTO - * auto-flush} might not occur when it should, and queries against the - * entity might return stale data. + * by an entity class or collection, then modifications might not be + * {@linkplain org.hibernate.FlushMode#AUTO automatically synchronized} + * with the database before a query is executed against that table, and + * the query might return stale data. *

- * This annotation might be necessary if: + * Ordinarily, Hibernate knows the tables containing the state of an + * entity or collection. This annotation might be necessary if: *

*

* By default, the table names specified by this annotation are interpreted @@ -39,7 +44,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; * * @see org.hibernate.query.SynchronizeableQuery */ -@Target(TYPE) +@Target({TYPE, FIELD, METHOD}) @Retention(RUNTIME) public @interface Synchronize { /** diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java index 024e34e4ff..5e44ae4710 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java @@ -68,6 +68,7 @@ import org.hibernate.annotations.SQLSelect; import org.hibernate.annotations.SQLUpdate; import org.hibernate.annotations.SortComparator; import org.hibernate.annotations.SortNatural; +import org.hibernate.annotations.Synchronize; import org.hibernate.annotations.Where; import org.hibernate.annotations.WhereJoinTable; import org.hibernate.annotations.common.reflection.ReflectionManager; @@ -83,6 +84,7 @@ import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.PropertyData; import org.hibernate.boot.spi.SecondPass; import org.hibernate.engine.config.spi.ConfigurationService; +import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.spi.FilterDefinition; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.collections.CollectionHelper; @@ -1642,6 +1644,7 @@ public abstract class CollectionBinder { LOG.debugf( "Mapping collection: %s -> %s", collection.getRole(), collection.getCollectionTable().getName() ); } + bindSynchronize(); bindFilters( false ); handleWhere( false ); @@ -1681,6 +1684,26 @@ public abstract class CollectionBinder { } } + private void bindSynchronize() { + if ( property.isAnnotationPresent( Synchronize.class ) ) { + final JdbcEnvironment jdbcEnvironment = buildingContext.getMetadataCollector().getDatabase().getJdbcEnvironment(); + final Synchronize synchronize = property.getAnnotation(Synchronize.class); + for ( String table : synchronize.value() ) { + String physicalName = synchronize.logical() ? toPhysicalName( jdbcEnvironment, table ) : table; + collection.addSynchronizedTable( physicalName ); + } + } + } + + private String toPhysicalName(JdbcEnvironment jdbcEnvironment, String logicalName) { + return buildingContext.getBuildingOptions().getPhysicalNamingStrategy() + .toPhysicalTableName( + jdbcEnvironment.getIdentifierHelper().toIdentifier( logicalName ), + jdbcEnvironment + ) + .render( jdbcEnvironment.getDialect() ); + } + private void bindFilters(boolean hasAssociationTable) { final Filter simpleFilter = property.getAnnotation( Filter.class ); //set filtering @@ -2084,6 +2107,8 @@ public abstract class CollectionBinder { else { handleOwnedManyToMany( targetEntity, isCollectionOfEntities ); } + + bindSynchronize(); bindFilters( isCollectionOfEntities ); handleWhere( isCollectionOfEntities ); diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Collection.java b/hibernate-core/src/main/java/org/hibernate/mapping/Collection.java index bb1e49169a..08dc774b5d 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Collection.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Collection.java @@ -727,6 +727,10 @@ public abstract class Collection implements Fetchable, Value, Filterable { return synchronizedTables; } + public void addSynchronizedTable(String table) { + synchronizedTables.add( table ); + } + public String getLoaderName() { return loaderName; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/SynchronizeableQuery.java b/hibernate-core/src/main/java/org/hibernate/query/SynchronizeableQuery.java index 4acbd75a81..02602ed395 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/SynchronizeableQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/SynchronizeableQuery.java @@ -16,9 +16,9 @@ import org.hibernate.MappingException; * in a given set of named query spaces. A query space is usually, but not always, * a relational database table, in which case the name of the space is simply the table name. *

- * Each {@linkplain jakarta.persistence.Entity entity type} is understood to store its state - * in one or more query spaces. Usually, the query spaces are automatically determined by - * the mapping, but sometimes they must be specified explicitly using + * Each {@linkplain jakarta.persistence.Entity entity type} or collection is understood to + * store its state in one or more query spaces. Usually, the query spaces are automatically + * determined by the mapping, but sometimes they must be specified explicitly using * {@link org.hibernate.annotations.Synchronize @Synchronize}. *

* Query spaces mediate the interaction between query execution and synchronization of