Skip to content

Commit

Permalink
NEW: Support for lazy add on BeanList (#81)
Browse files Browse the repository at this point in the history
When just adding new entries to an existing BeanList, there is no need to load the underlying collection

FIX: Issue when batch load occurs with lazily added elements
  • Loading branch information
rPraml committed Apr 26, 2023
1 parent 344255f commit f6b4418
Show file tree
Hide file tree
Showing 9 changed files with 348 additions and 40 deletions.
7 changes: 7 additions & 0 deletions ebean-api/src/main/java/io/ebean/bean/BeanCollection.java
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,13 @@ enum ModifyListenMode {
*/
Collection<?> actualEntries();

/**
* Returns entries, that were lazily added at the end of the list. Might be null.
*/
default Collection<E> getLazyAddedEntries() {
return null;
}

/**
* return true if there are real rows held. Return false is this is using
* Deferred fetch to lazy load the rows and the rows have not yet been
Expand Down
26 changes: 13 additions & 13 deletions ebean-api/src/main/java/io/ebean/common/BeanList.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
/**
* List capable of lazy loading and modification awareness.
*/
public final class BeanList<E> extends AbstractBeanCollection<E> implements List<E>, BeanCollectionAdd {
public class BeanList<E> extends AbstractBeanCollection<E> implements List<E>, BeanCollectionAdd {

private static final long serialVersionUID = 1L;

/**
* The underlying List implementation.
*/
private List<E> list;
List<E> list;

/**
* Specify the underlying List implementation.
Expand Down Expand Up @@ -111,30 +111,30 @@ public boolean checkEmptyLazyLoad() {
}
}

protected void initList(boolean skipLoad, boolean onlyIds) {
if (skipLoad) {
list = new ArrayList<>();
} else {
lazyLoadCollection(onlyIds);
}
}

private void initClear() {
lock.lock();
try {
if (list == null) {
if (!disableLazyLoad && modifyListening) {
lazyLoadCollection(true);
} else {
list = new ArrayList<>();
}
initList(disableLazyLoad || !modifyListening, true);
}
} finally {
lock.unlock();
}
}

private void init() {
void init() {
lock.lock();
try {
if (list == null) {
if (disableLazyLoad) {
list = new ArrayList<>();
} else {
lazyLoadCollection(false);
}
initList(disableLazyLoad, false);
}
} finally {
lock.unlock();
Expand Down
129 changes: 129 additions & 0 deletions ebean-api/src/main/java/io/ebean/common/BeanListLazyAdd.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package io.ebean.common;

import io.ebean.bean.BeanCollection;
import io.ebean.bean.BeanCollectionLoader;
import io.ebean.bean.EntityBean;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
* This bean list can perform additions without populating the list.
* This might be useful, if you just want to add entries to an existing collection.
* Works only for lists and only if there is no order column
*/
public class BeanListLazyAdd<E> extends BeanList<E> {

public BeanListLazyAdd(BeanCollectionLoader loader, EntityBean ownerBean, String propertyName) {
super(loader, ownerBean, propertyName);
}

private List<E> lazyAddedEntries;

@Override
public boolean add(E bean) {
checkReadOnly();

lock.lock();
try {
if (list == null) {
// list is not yet initialized, so we may add elements to a spare list
if (lazyAddedEntries == null) {
lazyAddedEntries = new ArrayList<>();
}
lazyAddedEntries.add(bean);
} else {
list.add(bean);
}
} finally {
lock.unlock();
}

if (modifyListening) {
modifyAddition(bean);
}
return true;
}

@Override
public boolean addAll(Collection<? extends E> beans) {
checkReadOnly();

lock.lock();
try {
if (list == null) {
// list is not yet initialized, so we may add elements to a spare list
if (lazyAddedEntries == null) {
lazyAddedEntries = new ArrayList<>();
}
lazyAddedEntries.addAll(beans);
} else {
list.addAll(beans);
}
} finally {
lock.unlock();
}

if (modifyListening) {
getModifyHolder().modifyAdditionAll(beans);
}

return true;
}


@Override
public void loadFrom(BeanCollection<?> other) {
super.loadFrom(other);
if (lazyAddedEntries != null) {
list.addAll(lazyAddedEntries);
lazyAddedEntries = null;
}
}

/**
* on init, this happens on all accessor methods except on 'add' and addAll,
* we add the lazy added entries at the end of the list
*/
@Override
protected void initList(boolean skipLoad, boolean onlyIds) {
if (skipLoad) {
if (lazyAddedEntries != null) {
list = lazyAddedEntries;
lazyAddedEntries = null;
} else {
list = new ArrayList<>();
}
} else {
lazyLoadCollection(onlyIds);
if (lazyAddedEntries != null) {
list.addAll(lazyAddedEntries);
lazyAddedEntries = null;
}
}
}

@Override
public List<E> getLazyAddedEntries() {
return lazyAddedEntries;
}

@Override
public boolean isSkipSave() {
return lazyAddedEntries == null && super.isSkipSave();
}

public boolean checkEmptyLazyLoad() {
if (list != null) {
return false;
} else if (lazyAddedEntries == null) {
list = new ArrayList<>();
return true;
} else {
list = lazyAddedEntries;
lazyAddedEntries = null;
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
abstract class BaseCollectionHelp<T> implements BeanCollectionHelp<T> {

final BeanPropertyAssocMany<T> many;
private final BeanDescriptor<T> targetDescriptor;
final BeanDescriptor<T> targetDescriptor;
final String propertyName;
BeanCollectionLoader loader;

Expand All @@ -21,12 +21,6 @@ abstract class BaseCollectionHelp<T> implements BeanCollectionHelp<T> {
this.propertyName = many.name();
}

BaseCollectionHelp() {
this.many = null;
this.targetDescriptor = null;
this.propertyName = null;
}

@Override
public final void setLoader(BeanCollectionLoader loader) {
this.loader = loader;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public static Collection<?> getActualEntries(Object o) {
if (o instanceof BeanCollection<?>) {
BeanCollection<?> bc = (BeanCollection<?>) o;
if (!bc.isPopulated()) {
return null;
return bc.getLazyAddedEntries();
}
// For maps this is a collection of Map.Entry, otherwise it
// returns a collection of beans
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import io.ebean.bean.BeanCollectionAdd;
import io.ebean.bean.EntityBean;
import io.ebean.common.BeanList;
import io.ebean.common.BeanListLazyAdd;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.json.SpiJsonWriter;

Expand All @@ -19,12 +20,11 @@
*/
public class BeanListHelp<T> extends BaseCollectionHelp<T> {

private final boolean hasOrderColumn;

BeanListHelp(BeanPropertyAssocMany<T> many) {
super(many);
}

BeanListHelp() {
super();
hasOrderColumn = many.hasOrderColumn();
}

@Override
Expand Down Expand Up @@ -53,7 +53,12 @@ public final BeanCollection<T> createEmptyNoParent() {

@Override
public final BeanCollection<T> createEmpty(EntityBean parentBean) {
BeanList<T> beanList = new BeanList<>(loader, parentBean, propertyName);
BeanList<T> beanList;
if (hasOrderColumn) {
beanList = new BeanList<>(loader, parentBean, propertyName);
} else {
beanList = new BeanListLazyAdd<>(loader, parentBean, propertyName);
}
if (many != null) {
beanList.setModifyListening(many.modifyListenMode());
}
Expand All @@ -62,7 +67,12 @@ public final BeanCollection<T> createEmpty(EntityBean parentBean) {

@Override
public final BeanCollection<T> createReference(EntityBean parentBean) {
BeanList<T> beanList = new BeanList<>(loader, parentBean, propertyName);
BeanList<T> beanList;
if (hasOrderColumn) {
beanList = new BeanList<>(loader, parentBean, propertyName);
} else {
beanList = new BeanListLazyAdd<>(loader, parentBean, propertyName);
}
beanList.setModifyListening(many.modifyListenMode());
return beanList;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,13 @@
*/
public class BeanMapHelp<T> extends BaseCollectionHelp<T> {

private final BeanPropertyAssocMany<T> many;
private final BeanDescriptor<T> targetDescriptor;
private final String propertyName;
private final BeanProperty beanProperty;

/**
* When help is attached to a specific many property.
*/
BeanMapHelp(BeanPropertyAssocMany<T> many) {
this.many = many;
this.targetDescriptor = many.targetDescriptor();
this.propertyName = many.name();
super(many);
this.beanProperty = targetDescriptor.beanProperty(many.mapKey());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,6 @@ public class BeanSetHelp<T> extends BaseCollectionHelp<T> {
super(many);
}

/**
* For a query that returns a set.
*/
BeanSetHelp() {
super();
}

@Override
public final BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) {
if (bc instanceof BeanSet<?>) {
Expand Down
Loading

0 comments on commit f6b4418

Please sign in to comment.