HHH-16089 allow @Synchronize for a collection

This commit is contained in:
Gavin 2023-01-24 22:07:11 +01:00 committed by Gavin King
parent 803a9a0ffb
commit 0bb04b1021
4 changed files with 47 additions and 13 deletions

View File

@ -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.
* <p>
* 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.
* <p>
* 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:
* <ul>
* <li>the entity maps a database view,
* <li>the entity is persisted using handwritten SQL, that is, using
* {@link SQLSelect @SQLSelect} and friends, or
* <li>the entity is mapped using {@link Subselect @Subselect}.
* <li>an entity or collection maps a database view,
* <li>an entity or collection is persisted using handwritten SQL,
* that is, using {@link SQLSelect @SQLSelect} and friends, or
* <li>an entity is mapped using {@link Subselect @Subselect}.
* </ul>
* <p>
* 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 {
/**

View File

@ -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 );

View File

@ -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;
}

View File

@ -16,9 +16,9 @@ import org.hibernate.MappingException;
* in a given set of named <em>query spaces</em>. 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.
* <p>
* 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}.
* <p>
* Query spaces mediate the interaction between query execution and synchronization of