Skip to content

Commit ca14d46

Browse files
committed
HHH-16573 NPE with embeddable element collection with updateable = false
1 parent aa2f2bc commit ca14d46

File tree

3 files changed

+107
-43
lines changed

3 files changed

+107
-43
lines changed

hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ValuedModelPart.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,18 @@ default void forEachInsertable(SelectableConsumer consumer) {
6464
);
6565
}
6666

67+
default void forEachNonFormula(SelectableConsumer consumer) {
68+
ModelPart.super.forEachSelectable(
69+
(selectionIndex, selectableMapping) -> {
70+
if ( selectableMapping.isFormula() ) {
71+
return;
72+
}
73+
74+
consumer.accept( selectionIndex, selectableMapping );
75+
}
76+
);
77+
}
78+
6779
default void forEachUpdatable(SelectableConsumer consumer) {
6880
ModelPart.super.forEachSelectable(
6981
(selectionIndex, selectableMapping) -> {

hibernate-core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -464,8 +464,32 @@ private RestrictedTableMutation<JdbcMutationOperation> generateUpdateRowAst(Muta
464464
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
465465
// SET
466466

467-
attribute.getElementDescriptor().forEachUpdatable( updateBuilder );
468-
467+
if ( hasIndex() ) {
468+
/*
469+
Given :
470+
471+
class MyEntity {
472+
@ElementCollection(fetch = FetchType.LAZY)
473+
@OrderColumn
474+
private List<MyEmbeddable> myEmbeddables;
475+
}
476+
477+
@Embeddable
478+
public static class MyEmbeddable {
479+
@Column(updatable = false)
480+
private String embeddedProperty;
481+
}
482+
483+
we cannot understand if the update is due to a change in the value embeddedProperty or because a
484+
new element has been added to the list in an existing position (update) shifting the old value (insert).
485+
486+
For this reason we cannot take into consideration the `@Column(updatable = false)`
487+
*/
488+
attribute.getElementDescriptor().forEachNonFormula( updateBuilder );
489+
}
490+
else {
491+
attribute.getElementDescriptor().forEachUpdatable( updateBuilder );
492+
}
469493
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
470494
// WHERE
471495

@@ -501,16 +525,28 @@ private void applyUpdateRowValues(
501525
0,
502526
jdbcValueBindings,
503527
null,
504-
(valueIndex, bindings, y, jdbcValue, jdbcValueMapping) -> {
505-
if ( !jdbcValueMapping.isUpdateable() || jdbcValueMapping.isFormula() ) {
506-
return;
507-
}
508-
bindings.bindValue(
509-
jdbcValue,
510-
jdbcValueMapping,
511-
ParameterUsage.SET
512-
);
513-
},
528+
hasIndex() ?
529+
(valueIndex, bindings, y, jdbcValue, jdbcValueMapping) -> {
530+
if ( jdbcValueMapping.isFormula() ) {
531+
return;
532+
}
533+
bindings.bindValue(
534+
jdbcValue,
535+
jdbcValueMapping,
536+
ParameterUsage.SET
537+
);
538+
}
539+
:
540+
(valueIndex, bindings, y, jdbcValue, jdbcValueMapping) -> {
541+
if ( !jdbcValueMapping.isUpdateable() || jdbcValueMapping.isFormula() ) {
542+
return;
543+
}
544+
bindings.bindValue(
545+
jdbcValue,
546+
jdbcValueMapping,
547+
ParameterUsage.SET
548+
);
549+
},
514550
session
515551
);
516552
}

hibernate-core/src/main/java/org/hibernate/persister/collection/mutation/UpdateRowsCoordinatorStandard.java

Lines changed: 47 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919
import org.hibernate.engine.spi.SharedSessionContractImplementor;
2020
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
2121
import org.hibernate.sql.model.MutationType;
22+
import org.hibernate.sql.model.internal.AbstractMutationOperationGroup;
23+
import org.hibernate.sql.model.internal.MutationOperationGroupNone;
2224
import org.hibernate.sql.model.internal.MutationOperationGroupSingle;
25+
import org.hibernate.sql.model.jdbc.JdbcMutationOperation;
2326

2427
/**
2528
* UpdateRowsCoordinator implementation for cases with a separate collection table
@@ -31,7 +34,7 @@
3134
public class UpdateRowsCoordinatorStandard extends AbstractUpdateRowsCoordinator implements UpdateRowsCoordinator {
3235
private final RowMutationOperations rowMutationOperations;
3336

34-
private MutationOperationGroupSingle operationGroup;
37+
private AbstractMutationOperationGroup operationGroup;
3538

3639
public UpdateRowsCoordinatorStandard(
3740
CollectionMutationTarget mutationTarget,
@@ -43,7 +46,7 @@ public UpdateRowsCoordinatorStandard(
4346

4447
@Override
4548
protected int doUpdate(Object key, PersistentCollection<?> collection, SharedSessionContractImplementor session) {
46-
final MutationOperationGroupSingle operationGroup = getOperationGroup();
49+
final AbstractMutationOperationGroup operationGroup = getOperationGroup();
4750

4851
final MutationExecutorService mutationExecutorService = session
4952
.getFactory()
@@ -115,42 +118,55 @@ private boolean processRow(
115118
int entryPosition,
116119
MutationExecutor mutationExecutor,
117120
SharedSessionContractImplementor session) {
118-
final PluralAttributeMapping attribute = getMutationTarget().getTargetPart();
119-
if ( !collection.needsUpdating( entry, entryPosition, attribute ) ) {
120-
return false;
121-
}
121+
if ( rowMutationOperations.getUpdateRowOperation() != null ) {
122+
final PluralAttributeMapping attribute = getMutationTarget().getTargetPart();
123+
if ( !collection.needsUpdating( entry, entryPosition, attribute ) ) {
124+
return false;
125+
}
122126

123-
rowMutationOperations.getUpdateRowValues().applyValues(
124-
collection,
125-
key,
126-
entry,
127-
entryPosition,
128-
session,
129-
mutationExecutor.getJdbcValueBindings()
130-
);
127+
rowMutationOperations.getUpdateRowValues().applyValues(
128+
collection,
129+
key,
130+
entry,
131+
entryPosition,
132+
session,
133+
mutationExecutor.getJdbcValueBindings()
134+
);
131135

132-
rowMutationOperations.getUpdateRowRestrictions().applyRestrictions(
133-
collection,
134-
key,
135-
entry,
136-
entryPosition,
137-
session,
138-
mutationExecutor.getJdbcValueBindings()
139-
);
136+
rowMutationOperations.getUpdateRowRestrictions().applyRestrictions(
137+
collection,
138+
key,
139+
entry,
140+
entryPosition,
141+
session,
142+
mutationExecutor.getJdbcValueBindings()
143+
);
140144

141-
mutationExecutor.execute( collection, null, null, null, session );
142-
return true;
145+
mutationExecutor.execute( collection, null, null, null, session );
146+
return true;
147+
}
148+
else {
149+
return false;
150+
}
143151
}
144152

145-
protected MutationOperationGroupSingle getOperationGroup() {
153+
protected AbstractMutationOperationGroup getOperationGroup() {
146154
if ( operationGroup == null ) {
147-
operationGroup = new MutationOperationGroupSingle(
148-
MutationType.UPDATE,
149-
getMutationTarget(),
150-
rowMutationOperations.getUpdateRowOperation()
151-
);
155+
final JdbcMutationOperation updateRowOperation = rowMutationOperations.getUpdateRowOperation();
156+
if ( updateRowOperation == null ) {
157+
operationGroup = new MutationOperationGroupNone(
158+
MutationType.UPDATE,
159+
getMutationTarget()
160+
);
161+
}
162+
else {
163+
operationGroup = new MutationOperationGroupSingle(
164+
MutationType.UPDATE,
165+
getMutationTarget(),
166+
updateRowOperation
167+
);
168+
}
152169
}
153-
154170
return operationGroup;
155171
}
156172

0 commit comments

Comments
 (0)