io.ebeaninternal.server.core.DefaultBeanLoader Maven / Gradle / Ivy
package io.ebeaninternal.server.core;
import io.ebean.ExpressionList;
import io.ebean.Transaction;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
import io.ebean.bean.PersistenceContext;
import io.ebeaninternal.api.LoadBeanRequest;
import io.ebeaninternal.api.LoadManyRequest;
import io.ebeaninternal.api.LoadRequest;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.api.SpiQuery.Mode;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.transaction.DefaultPersistenceContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.EntityNotFoundException;
import java.util.List;
/**
* Helper to handle lazy loading and refreshing of beans.
*/
public class DefaultBeanLoader {
private static final Logger logger = LoggerFactory.getLogger(DefaultBeanLoader.class);
private final DefaultServer server;
private final boolean onIterateUseExtraTxn;
protected DefaultBeanLoader(DefaultServer server) {
this.server = server;
this.onIterateUseExtraTxn = server.getDatabasePlatform().useExtraTransactionOnIterateSecondaryQueries();
}
/**
* Return a batch size that might be less than the requestedBatchSize.
*
* This means we can have large and variable requestedBatchSizes.
*
*
* We want to restrict the number of different batch sizes as we want to
* re-use the query plan cache and get DB statement re-use.
*
*/
private int getBatchSize(int batchSize) {
if (batchSize == 1) {
// there is only one bean/collection to load
return 1;
}
if (batchSize <= 5) {
// anything less than 5 becomes 5
return 5;
}
if (batchSize <= 10) {
return 10;
}
if (batchSize <= 20) {
return 20;
}
if (batchSize <= 50) {
return 50;
}
if (batchSize <= 100) {
return 100;
}
return batchSize;
}
public void refreshMany(EntityBean parentBean, String propertyName) {
refreshMany(parentBean, propertyName, null);
}
public void loadMany(LoadManyRequest loadRequest) {
List> batch = loadRequest.getBatch();
int batchSize = getBatchSize(batch.size());
SpiQuery> query = loadRequest.createQuery(server, batchSize);
executeQuery(loadRequest, query);
loadRequest.postLoad();
}
public void loadMany(BeanCollection> bc, boolean onlyIds) {
EntityBean parentBean = bc.getOwnerBean();
String propertyName = bc.getPropertyName();
loadManyInternal(parentBean, propertyName, null, false, onlyIds);
}
public void refreshMany(EntityBean parentBean, String propertyName, Transaction t) {
loadManyInternal(parentBean, propertyName, t, true, false);
}
private void loadManyInternal(EntityBean parentBean, String propertyName, Transaction t, boolean refresh, boolean onlyIds) {
EntityBeanIntercept ebi = parentBean._ebean_getIntercept();
PersistenceContext pc = ebi.getPersistenceContext();
BeanDescriptor> parentDesc = server.getBeanDescriptor(parentBean.getClass());
BeanPropertyAssocMany> many = (BeanPropertyAssocMany>) parentDesc.getBeanProperty(propertyName);
BeanCollection> beanCollection = null;
ExpressionList> filterMany = null;
Object currentValue = many.getValue(parentBean);
if (currentValue instanceof BeanCollection>) {
beanCollection = (BeanCollection>) currentValue;
filterMany = beanCollection.getFilterMany();
}
Object parentId = parentDesc.getId(parentBean);
if (pc == null) {
pc = new DefaultPersistenceContext();
parentDesc.contextPut(pc, parentId, parentBean);
}
boolean useManyIdCache = beanCollection != null && parentDesc.isManyPropCaching();
if (useManyIdCache) {
Boolean readOnly = null;
if (ebi.isReadOnly()) {
readOnly = Boolean.TRUE;
}
if (parentDesc.cacheManyPropLoad(many, beanCollection, parentId, readOnly)) {
return;
}
}
SpiQuery> query = server.createQuery(parentDesc.getBeanType());
if (refresh) {
// populate a new collection
BeanCollection> emptyCollection = many.createEmpty(parentBean);
many.setValue(parentBean, emptyCollection);
query.setLoadDescription("+refresh", null);
} else {
query.setLoadDescription("+lazy", null);
}
String idProperty = parentDesc.getIdBinder().getIdProperty();
query.select(idProperty);
if (onlyIds) {
query.fetch(many.getName(), many.getTargetIdProperty());
} else {
query.fetch(many.getName());
}
if (filterMany != null) {
query.setFilterMany(many.getName(), filterMany);
}
query.where().idEq(parentId);
query.setUseCache(false);
query.setMode(Mode.LAZYLOAD_MANY);
query.setLazyLoadManyPath(many.getName());
query.setPersistenceContext(pc);
if (ebi.isReadOnly()) {
query.setReadOnly(true);
}
server.findOne(query, t);
if (beanCollection != null) {
if (beanCollection.checkEmptyLazyLoad()) {
if (logger.isDebugEnabled()) {
logger.debug("BeanCollection after load was empty. Owner:" + beanCollection.getOwnerBean());
}
} else if (useManyIdCache) {
parentDesc.cacheManyPropPut(many, beanCollection, parentId);
}
}
}
/**
* Load a batch of beans for +query or +lazy loading.
*/
public void loadBean(LoadBeanRequest loadRequest) {
List batch = loadRequest.getBatch();
if (batch.isEmpty()) {
throw new RuntimeException("Nothing in batch?");
}
int batchSize = getBatchSize(batch.size());
List