Skip to content

Commit 1135eb3

Browse files
authored
#3665 Fix for delete() Skips Bean due to Hash Collision (#3669)
Change the underlying storage of deleting beans to be by type by id so: private Map<Class<?>, Set<Object>> deletingBeans;
1 parent 1dccdff commit 1135eb3

File tree

7 files changed

+32
-66
lines changed

7 files changed

+32
-66
lines changed

ebean-core/src/main/java/io/ebeaninternal/api/SpiTransaction.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,12 @@ public interface SpiTransaction extends Transaction {
6565
* <p>
6666
* This is to handle bi-directional relationships where both sides Cascade.
6767
*/
68-
void registerDeleteBean(Integer hash);
68+
void registerDeleteBean(Class<?> type, Object id);
6969

7070
/**
7171
* Return true if this is a bean that has already been saved/deleted.
7272
*/
73-
boolean isRegisteredDeleteBean(Integer hash);
73+
boolean isRegisteredDeleteBean(Class<?> type, Object id);
7474

7575
/**
7676
* Unregister the persisted beans. Expected after persisting top level beans

ebean-core/src/main/java/io/ebeaninternal/api/SpiTransactionProxy.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,13 +195,13 @@ public void registerDeferred(PersistDeferredRelationship derived) {
195195
}
196196

197197
@Override
198-
public void registerDeleteBean(Integer hash) {
199-
transaction.registerDeleteBean(hash);
198+
public void registerDeleteBean(Class<?> type, Object id) {
199+
transaction.registerDeleteBean(type, id);
200200
}
201201

202202
@Override
203-
public boolean isRegisteredDeleteBean(Integer hash) {
204-
return transaction.isRegisteredDeleteBean(hash);
203+
public boolean isRegisteredDeleteBean(Class<?> type, Object id) {
204+
return transaction.isRegisteredDeleteBean(type, id);
205205
}
206206

207207
@Override

ebean-core/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
5454
* The unique id used for logging summary.
5555
*/
5656
private Object idValue;
57-
/**
58-
* Hash value used to handle cascade delete both ways in a relationship.
59-
*/
60-
private Integer beanHash;
6157
private boolean statelessUpdate;
6258
private boolean notifyCache;
6359
/**
@@ -285,7 +281,9 @@ private void onUpdateGeneratedProperties() {
285281
private void onFailedUpdateUndoGeneratedProperties() {
286282
for (BeanProperty prop : beanDescriptor.propertiesGenUpdate()) {
287283
Object oldVal = intercept.origValue(prop.propertyIndex());
288-
prop.setValue(entityBean, oldVal);
284+
if (oldVal != null) {
285+
prop.setValue(entityBean, oldVal);
286+
}
289287
}
290288
}
291289

@@ -553,34 +551,17 @@ public void unRegisterBean() {
553551
}
554552
}
555553

556-
/**
557-
* The hash used to register the bean with the transaction.
558-
* <p>
559-
* Takes into account the class type and id value.
560-
*/
561-
private Integer beanHash() {
562-
if (beanHash == null) {
563-
Object id = beanDescriptor.getId(entityBean);
564-
int hc = 92821 * bean.getClass().getName().hashCode();
565-
if (id != null) {
566-
hc += id.hashCode();
567-
}
568-
beanHash = hc;
569-
}
570-
return beanHash;
571-
}
572-
573554
public void registerDeleteBean() {
574-
Integer hash = beanHash();
575-
transaction.registerDeleteBean(hash);
555+
final Object id = beanDescriptor.id(entityBean);
556+
transaction.registerDeleteBean(beanDescriptor.type(), id);
576557
}
577558

578559
public boolean isRegisteredForDeleteBean() {
579560
if (transaction == null) {
580561
return false;
581562
} else {
582-
Integer hash = beanHash();
583-
return transaction.isRegisteredDeleteBean(hash);
563+
final Object id = beanDescriptor.id(entityBean);
564+
return transaction.isRegisteredDeleteBean(beanDescriptor.type(), id);
584565
}
585566
}
586567

@@ -809,7 +790,9 @@ public void setBoundId(Object idValue) {
809790
public void checkRowCount(int rowCount) {
810791
if (rowCount != 1 && rowCount != Statement.SUCCESS_NO_INFO) {
811792
if (ConcurrencyMode.VERSION == concurrencyMode) {
812-
onFailedUpdateUndoGeneratedProperties();
793+
if (type == Type.UPDATE) {
794+
onFailedUpdateUndoGeneratedProperties();
795+
}
813796
throw new OptimisticLockException("Data has changed. updated row count " + rowCount, null, bean);
814797
} else if (rowCount == 0 && type == Type.UPDATE) {
815798
throw new EntityNotFoundException("No rows updated");

ebean-core/src/main/java/io/ebeaninternal/server/transaction/ImplicitReadOnlyTransaction.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,15 +200,15 @@ public void registerDeferred(PersistDeferredRelationship derived) {
200200
}
201201

202202
@Override
203-
public void registerDeleteBean(Integer persistingBean) {
203+
public void registerDeleteBean(Class<?> type, Object id) {
204204
throw new IllegalStateException(notExpectedMessage);
205205
}
206206

207207
/**
208208
* Return true if this is a bean that has already been saved/deleted.
209209
*/
210210
@Override
211-
public boolean isRegisteredDeleteBean(Integer persistingBean) {
211+
public boolean isRegisteredDeleteBean(Class<?> type, Object id) {
212212
return false;
213213
}
214214

ebean-core/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java

Lines changed: 12 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
6868
private int depth;
6969
private boolean autoCommit;
7070
private IdentityHashMap<Object, Object> persistingBeans;
71-
private HashSet<Integer> deletingBeansHash;
71+
private Map<Class<?>, Set<Object>> deletingBeans;
7272
private HashMap<String, String> m2mIntersectionSave;
7373
private Map<String, Object> userObjects;
7474
private List<TransactionCallback> callbackList;
@@ -328,40 +328,28 @@ public final void registerDeferred(PersistDeferredRelationship derived) {
328328
deferredList.add(derived);
329329
}
330330

331-
/**
332-
* Add a bean to the registed list.
333-
* <p>
334-
* This is to handle bi-directional relationships where both sides Cascade.
335-
* </p>
336-
*/
337331
@Override
338-
public final void registerDeleteBean(Integer persistingBean) {
339-
if (deletingBeansHash == null) {
340-
deletingBeansHash = new HashSet<>();
341-
}
342-
deletingBeansHash.add(persistingBean);
332+
public final void registerDeleteBean(Class<?> type, Object id) {
333+
deleteBeanIds(type).add(id);
343334
}
344335

345-
/**
346-
* Return true if this is a bean that has already been saved/deleted.
347-
*/
348336
@Override
349-
public final boolean isRegisteredDeleteBean(Integer persistingBean) {
350-
return deletingBeansHash != null && deletingBeansHash.contains(persistingBean);
337+
public final boolean isRegisteredDeleteBean(Class<?> type, Object id) {
338+
return deleteBeanIds(type).contains(id);
339+
}
340+
341+
private Set<Object> deleteBeanIds(Class<?> type) {
342+
if (deletingBeans == null) {
343+
deletingBeans = new HashMap<>();
344+
}
345+
return deletingBeans.computeIfAbsent(type, k -> new HashSet<>());
351346
}
352347

353-
/**
354-
* Unregister the persisted beans (when persisting at the top level).
355-
*/
356348
@Override
357349
public final void unregisterBeans() {
358350
persistingBeans.clear();
359351
}
360352

361-
/**
362-
* Return true if this is a bean that has already been saved. This will
363-
* register the bean if it is not already.
364-
*/
365353
@Override
366354
public final boolean isRegisteredBean(Object bean) {
367355
if (persistingBeans == null) {
@@ -370,10 +358,6 @@ public final boolean isRegisteredBean(Object bean) {
370358
return (persistingBeans.put(bean, PLACEHOLDER) != null);
371359
}
372360

373-
/**
374-
* Return true if the m2m intersection save is allowed from a given bean direction.
375-
* This is to stop m2m intersection management via both directions of a m2m.
376-
*/
377361
@Override
378362
public final boolean isSaveAssocManyIntersection(String intersectionTable, String beanName) {
379363
if (m2mIntersectionSave == null) {

ebean-core/src/main/java/io/ebeaninternal/server/transaction/NoTransaction.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,12 +154,12 @@ public void registerDeferred(PersistDeferredRelationship derived) {
154154
}
155155

156156
@Override
157-
public void registerDeleteBean(Integer hash) {
157+
public void registerDeleteBean(Class<?> type, Object id) {
158158

159159
}
160160

161161
@Override
162-
public boolean isRegisteredDeleteBean(Integer hash) {
162+
public boolean isRegisteredDeleteBean(Class<?> type, Object id) {
163163
return false;
164164
}
165165

ebean-test/src/test/java/org/tests/iud/TestCarWheelIud.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ public void test() {
5656
Car car2 = DB.find(Car.class, car.getId());
5757

5858
DB.delete(car2);
59-
6059
}
6160

6261
@Test

0 commit comments

Comments
 (0)