Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When delete batching, use number of id properties #3182

Merged
merged 2 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ public interface IdBinder {
*/
boolean isComplexId();

/**
* Return the number of properties that make up the id.
*/
default int size() {
return 1;
}

/**
* Return the default order by that may need to be used if the query includes
* a many property.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ public boolean isComplexId() {
return true;
}

@Override
public int size() {
return props.length;
}

@Override
public String orderBy() {
return orderBy(null, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public DefaultPersister(SpiEbeanServer server, Binder binder, BeanDescriptorMana
* e.g. SqlServer has a 2100 parameter limit, so delete max 2000 for it.
*/
private int initMaxDeleteBatch(int maxInBinding) {
return maxInBinding == 0 ? 1000 : maxInBinding;
return maxInBinding == 0 ? 1000 : Math.min(1000, maxInBinding);
}

@Override
Expand Down Expand Up @@ -652,11 +652,9 @@ public int deleteByIds(BeanDescriptor<?> descriptor, List<Object> idList, Transa
}

private int delete(BeanDescriptor<?> descriptor, List<Object> idList, Transaction transaction, DeleteMode deleteMode) {
if (idList == null || idList.size() <= maxDeleteBatch) {
return new DeleteBatchHelpMultiple(descriptor, idList, transaction, deleteMode).deleteBatch();
}
final int batch = maxDeleteBatch / descriptor.idBinder().size();
int rows = 0;
for (List<Object> batchOfIds : Lists.partition(idList, maxDeleteBatch)) {
for (List<Object> batchOfIds : Lists.partition(idList, batch)) {
rows += new DeleteBatchHelpMultiple(descriptor, batchOfIds, transaction, deleteMode).deleteBatch();
}
return rows;
Expand Down Expand Up @@ -1079,35 +1077,32 @@ private void deleteAssocMany(PersistRequestBean<?> request) {
* For stateless updates this deletes details beans that are no longer in
* the many - the excludeDetailIds holds the detail beans that are in the
* collection (and should not be deleted).
* </p>
*/
void deleteManyDetails(SpiTransaction t, BeanDescriptor<?> desc, EntityBean parentBean,
BeanPropertyAssocMany<?> many, Set<Object> excludeDetailIds, DeleteMode deleteMode) {
if (many.cascadeInfo().isDelete()) {
// cascade delete the beans in the collection
BeanDescriptor<?> targetDesc = many.targetDescriptor();
final BeanDescriptor<?> targetDesc = many.targetDescriptor();
final int batch = maxDeleteBatch / targetDesc.idBinder().size();
if (deleteMode.isHard() || targetDesc.isSoftDelete()) {
if (targetDesc.isDeleteByStatement()
&& (excludeDetailIds == null || excludeDetailIds.size() <= maxDeleteBatch)) { // TODO wait for #3176
&& (excludeDetailIds == null || excludeDetailIds.size() <= batch)) {
// Just delete all the children with one statement
IntersectionRow intRow = many.buildManyDeleteChildren(parentBean, excludeDetailIds);
SqlUpdate sqlDelete = intRow.createDelete(server, deleteMode);
executeSqlUpdate(sqlDelete, t);

} else {
// TODO: Review first checking if many property is loaded and using the loaded beans
// ... and only using findIdsByParentId() when the many property isn't loaded
// Delete recurse using the Id values of the children
Object parentId = desc.getId(parentBean);
List<Object> idsByParentId;
if (excludeDetailIds == null || excludeDetailIds.size() <= maxDeleteBatch) { // TODO: Wait for #3176
if (excludeDetailIds == null || excludeDetailIds.size() <= batch) {
idsByParentId = many.findIdsByParentId(parentId, t, deleteMode.isHard(), excludeDetailIds);
} else {
// if we hit the parameter limit, we must filter that on the java side.
// There is no easy way to batch "not in" queries.
// checkme: We could pass the first 1000-2000 params to the DB and filter the rest
// if we hit the parameter limit, we must filter that on the java side
// as there is no easy way to batch "not in" queries.
idsByParentId = many.findIdsByParentId(parentId, t, deleteMode.isHard(), null);
idsByParentId.removeIf(id -> excludeDetailIds.contains(id));
idsByParentId.removeIf(excludeDetailIds::contains);
}
if (!idsByParentId.isEmpty()) {
deleteChildrenById(t, targetDesc, idsByParentId, deleteMode);
Expand Down