|
10 | 10 | import org.hibernate.Session;
|
11 | 11 | import org.hibernate.collection.spi.PersistentCollection;
|
12 | 12 | import org.hibernate.engine.jdbc.spi.JdbcServices;
|
| 13 | +import org.hibernate.engine.spi.CollectionKey; |
13 | 14 | import org.hibernate.engine.spi.EntityEntry;
|
14 | 15 | import org.hibernate.engine.spi.EntityKey;
|
15 | 16 | import org.hibernate.engine.spi.PersistenceContext;
|
@@ -320,6 +321,158 @@ private static InListPredicate applyCompositeCollectionKeyTableLockRestrictions(
|
320 | 321 | return inListPredicate;
|
321 | 322 | }
|
322 | 323 |
|
| 324 | + /** |
| 325 | + * Lock a collection-table. |
| 326 | + * |
| 327 | + * @param attributeMapping The plural attribute whose table needs locked. |
| 328 | + * @param lockMode The lock mode to apply |
| 329 | + * @param lockTimeout A lock timeout to apply, if one. |
| 330 | + * @param collectionKeys Keys of collection-table rows that should be locked. |
| 331 | + */ |
| 332 | + public static void lockCollectionTable( |
| 333 | + PluralAttributeMapping attributeMapping, |
| 334 | + LockMode lockMode, |
| 335 | + Timeout lockTimeout, |
| 336 | + List<CollectionKey> collectionKeys, |
| 337 | + ExecutionContext executionContext) { |
| 338 | + final ForeignKeyDescriptor keyDescriptor = attributeMapping.getKeyDescriptor(); |
| 339 | + final String keyTableName = keyDescriptor.getKeyTable(); |
| 340 | + |
| 341 | + if ( SQL_EXEC_LOGGER.isDebugEnabled() ) { |
| 342 | + SQL_EXEC_LOGGER.debugf( "Follow-on locking for collection table `%s` - %s", keyTableName, attributeMapping.getRootPathName() ); |
| 343 | + } |
| 344 | + |
| 345 | + final QuerySpec querySpec = new QuerySpec( true ); |
| 346 | + |
| 347 | + final NamedTableReference tableReference = new NamedTableReference( keyTableName, "tbl" ); |
| 348 | + final LockingTableGroup tableGroup = new LockingTableGroup( |
| 349 | + tableReference, |
| 350 | + keyTableName, |
| 351 | + attributeMapping, |
| 352 | + keyDescriptor.getKeySide().getModelPart() |
| 353 | + ); |
| 354 | + |
| 355 | + querySpec.getFromClause().addRoot( tableGroup ); |
| 356 | + |
| 357 | + final ValuedModelPart keyPart = keyDescriptor.getKeyPart(); |
| 358 | + final ColumnReference columnReference = new ColumnReference( tableReference, keyPart.getSelectable( 0 ) ); |
| 359 | + |
| 360 | + // NOTE: We add the key column to the selection list, but never create a DomainResult |
| 361 | + // as we won't read the value back. Ideally, we would read the "value column(s)" and |
| 362 | + // update the collection state accordingly much like is done for entity state - |
| 363 | + // however, the concern is minor, so for simplicity we do not. |
| 364 | + final SqlSelectionImpl sqlSelection = new SqlSelectionImpl( columnReference, 0 ); |
| 365 | + querySpec.getSelectClause().addSqlSelection( sqlSelection ); |
| 366 | + |
| 367 | + final int expectedParamCount = collectionKeys.size() * keyDescriptor.getJdbcTypeCount(); |
| 368 | + final JdbcParameterBindingsImpl parameterBindings = new JdbcParameterBindingsImpl( expectedParamCount ); |
| 369 | + |
| 370 | + final InListPredicate restriction; |
| 371 | + if ( keyDescriptor.getJdbcTypeCount() == 1 ) { |
| 372 | + restriction = new InListPredicate( columnReference ); |
| 373 | + applySimpleCollectionKeyTableLockRestrictions( |
| 374 | + attributeMapping, |
| 375 | + keyDescriptor, |
| 376 | + restriction, |
| 377 | + parameterBindings, |
| 378 | + collectionKeys, |
| 379 | + executionContext.getSession() |
| 380 | + ); |
| 381 | + } |
| 382 | + else { |
| 383 | + restriction = applyCompositeCollectionKeyTableLockRestrictions( |
| 384 | + attributeMapping, |
| 385 | + keyDescriptor, |
| 386 | + tableReference, |
| 387 | + parameterBindings, |
| 388 | + collectionKeys, |
| 389 | + executionContext.getSession() |
| 390 | + ); |
| 391 | + } |
| 392 | + querySpec.applyPredicate( restriction ); |
| 393 | + |
| 394 | + final QueryOptionsImpl lockingQueryOptions = new QueryOptionsImpl(); |
| 395 | + lockingQueryOptions.getLockOptions().setLockMode( lockMode ); |
| 396 | + lockingQueryOptions.getLockOptions().setTimeout( lockTimeout ); |
| 397 | + final ExecutionContext lockingExecutionContext = new BaseExecutionContext( executionContext.getSession() ) { |
| 398 | + @Override |
| 399 | + public QueryOptions getQueryOptions() { |
| 400 | + return lockingQueryOptions; |
| 401 | + } |
| 402 | + }; |
| 403 | + |
| 404 | + performLocking( querySpec, parameterBindings, lockingExecutionContext ); |
| 405 | + } |
| 406 | + |
| 407 | + private static void applySimpleCollectionKeyTableLockRestrictions( |
| 408 | + PluralAttributeMapping attributeMapping, |
| 409 | + ForeignKeyDescriptor keyDescriptor, |
| 410 | + InListPredicate restriction, |
| 411 | + JdbcParameterBindingsImpl parameterBindings, |
| 412 | + List<CollectionKey> collectionKeys, |
| 413 | + SharedSessionContractImplementor session) { |
| 414 | + for ( CollectionKey collectionKey : collectionKeys ) { |
| 415 | + final Object collectionKeyValue = collectionKey.getKey(); |
| 416 | + keyDescriptor.breakDownJdbcValues( |
| 417 | + collectionKeyValue, |
| 418 | + (valueIndex, value, jdbcValueMapping) -> { |
| 419 | + final JdbcParameterImpl jdbcParameter = new JdbcParameterImpl( |
| 420 | + jdbcValueMapping.getJdbcMapping() ); |
| 421 | + restriction.addExpression( jdbcParameter ); |
| 422 | + |
| 423 | + parameterBindings.addBinding( |
| 424 | + jdbcParameter, |
| 425 | + new JdbcParameterBindingImpl( jdbcValueMapping.getJdbcMapping(), value ) |
| 426 | + ); |
| 427 | + }, |
| 428 | + session |
| 429 | + ); |
| 430 | + } |
| 431 | + } |
| 432 | + |
| 433 | + private static InListPredicate applyCompositeCollectionKeyTableLockRestrictions( |
| 434 | + PluralAttributeMapping attributeMapping, |
| 435 | + ForeignKeyDescriptor keyDescriptor, |
| 436 | + TableReference tableReference, |
| 437 | + JdbcParameterBindingsImpl parameterBindings, |
| 438 | + List<CollectionKey> collectionKeys, |
| 439 | + SharedSessionContractImplementor session) { |
| 440 | + if ( !session.getDialect().supportsRowValueConstructorSyntaxInInList() ) { |
| 441 | + // for now... |
| 442 | + throw new UnsupportedOperationException( |
| 443 | + "Follow-on collection-table locking with composite keys is not supported for Dialects" |
| 444 | + + " which do not support tuples (row constructor syntax) as part of an in-list" |
| 445 | + ); |
| 446 | + } |
| 447 | + |
| 448 | + final List<ColumnReference> columnReferences = new ArrayList<>( keyDescriptor.getJdbcTypeCount() ); |
| 449 | + keyDescriptor.forEachSelectable( (selectionIndex, selectableMapping) -> { |
| 450 | + columnReferences.add( new ColumnReference( tableReference, selectableMapping ) ); |
| 451 | + } ); |
| 452 | + final InListPredicate inListPredicate = new InListPredicate( new SqlTuple( columnReferences, keyDescriptor ) ); |
| 453 | + |
| 454 | + for ( CollectionKey collectionKey : collectionKeys ) { |
| 455 | + final Object collectionKeyValue = collectionKey.getKey(); |
| 456 | + |
| 457 | + final List<JdbcParameter> jdbcParameters = new ArrayList<>( keyDescriptor.getJdbcTypeCount() ); |
| 458 | + keyDescriptor.breakDownJdbcValues( |
| 459 | + collectionKeyValue, |
| 460 | + (valueIndex, value, jdbcValueMapping) -> { |
| 461 | + final JdbcParameterImpl jdbcParameter = new JdbcParameterImpl( jdbcValueMapping.getJdbcMapping() ); |
| 462 | + jdbcParameters.add( jdbcParameter ); |
| 463 | + parameterBindings.addBinding( |
| 464 | + jdbcParameter, |
| 465 | + new JdbcParameterBindingImpl( jdbcValueMapping.getJdbcMapping(), value ) |
| 466 | + ); |
| 467 | + }, |
| 468 | + session |
| 469 | + ); |
| 470 | + inListPredicate.addExpression( new SqlTuple( jdbcParameters, keyDescriptor ) ); |
| 471 | + } |
| 472 | + |
| 473 | + return inListPredicate; |
| 474 | + } |
| 475 | + |
323 | 476 | private static void performLocking(
|
324 | 477 | QuerySpec querySpec,
|
325 | 478 | JdbcParameterBindings jdbcParameterBindings,
|
@@ -414,6 +567,27 @@ public static void segmentLoadedValues(List<LoadedValuesCollector.LoadedEntityRe
|
414 | 567 | } );
|
415 | 568 | }
|
416 | 569 |
|
| 570 | + public static void segmentLoadedCollections(List<LoadedValuesCollector.LoadedCollectionRegistration> registrations, Map<EntityMappingType, Map<PluralAttributeMapping, List<CollectionKey>>> map) { |
| 571 | + if ( registrations == null ) { |
| 572 | + return; |
| 573 | + } |
| 574 | + |
| 575 | + registrations.forEach( (registration) -> { |
| 576 | + final PluralAttributeMapping pluralAttributeMapping = registration.collectionDescriptor(); |
| 577 | + if ( pluralAttributeMapping.getSeparateCollectionTable() != null ) { |
| 578 | + final Map<PluralAttributeMapping, List<CollectionKey>> attributeKeys = map.computeIfAbsent( |
| 579 | + pluralAttributeMapping.findContainingEntityMapping(), |
| 580 | + entityMappingType -> new HashMap<>() |
| 581 | + ); |
| 582 | + final List<CollectionKey> collectionKeys = attributeKeys.computeIfAbsent( |
| 583 | + pluralAttributeMapping, |
| 584 | + entityMappingType -> new ArrayList<>() |
| 585 | + ); |
| 586 | + collectionKeys.add( registration.collectionKey() ); |
| 587 | + } |
| 588 | + } ); |
| 589 | + } |
| 590 | + |
417 | 591 | public static Map<Object, EntityDetails> resolveEntityKeys(List<EntityKey> entityKeys, ExecutionContext executionContext) {
|
418 | 592 | final Map<Object, EntityDetails> map = new HashMap<>();
|
419 | 593 | final PersistenceContext persistenceContext = executionContext.getSession().getPersistenceContext();
|
|
0 commit comments