|
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