package com.avaje.ebeaninternal.server.persist;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.PersistenceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.CallableSql;
import com.avaje.ebean.Query;
import com.avaje.ebean.SqlUpdate;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.Update;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollection.ModifyListenMode;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.api.SpiUpdate;
import com.avaje.ebeaninternal.server.core.Message;
import com.avaje.ebeaninternal.server.core.PersistRequest;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.core.PersistRequestCallableSql;
import com.avaje.ebeaninternal.server.core.PersistRequestOrmUpdate;
import com.avaje.ebeaninternal.server.core.PersistRequestUpdateSql;
import com.avaje.ebeaninternal.server.core.Persister;
import com.avaje.ebeaninternal.server.core.PstmtBatch;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import com.avaje.ebeaninternal.server.deploy.BeanManager;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.deploy.IntersectionRow;
import com.avaje.ebeaninternal.server.deploy.ManyType;
/**
* Persister implementation using DML.
*
* This object uses DmlPersistExecute to perform the actual persist execution.
*
*
* This object:
*
* - Determines insert or update for saved beans
* - Determines the concurrency mode
* - Handles cascading of save and delete
* - Handles the batching and queueing
*
*
* @see com.avaje.ebeaninternal.server.persist.DefaultPersistExecute
*/
public final class DefaultPersister implements Persister {
private static final Logger logger = LoggerFactory.getLogger(DefaultPersister.class);
/**
* Actually does the persisting work.
*/
private final PersistExecute persistExecute;
private final SpiEbeanServer server;
private final BeanDescriptorManager beanDescriptorManager;
public DefaultPersister(SpiEbeanServer server, Binder binder, BeanDescriptorManager descMgr, PstmtBatch pstmtBatch) {
this.server = server;
this.beanDescriptorManager = descMgr;
this.persistExecute = new DefaultPersistExecute(binder, pstmtBatch);
}
/**
* Execute the CallableSql.
*/
public int executeCallable(CallableSql callSql, Transaction t) {
PersistRequestCallableSql request = new PersistRequestCallableSql(server, callSql, (SpiTransaction) t, persistExecute);
try {
request.initTransIfRequired();
int rc = request.executeOrQueue();
request.commitTransIfRequired();
return rc;
} catch (RuntimeException e) {
request.rollbackTransIfRequired();
throw e;
}
}
/**
* Execute the orm update.
*/
public int executeOrmUpdate(Update> update, Transaction t) {
SpiUpdate> ormUpdate = (SpiUpdate>) update;
BeanManager> mgr = beanDescriptorManager.getBeanManager(ormUpdate.getBeanType());
if (mgr == null) {
String msg = "No BeanManager found for type [" + ormUpdate.getBeanType() + "]. Is it an entity?";
throw new PersistenceException(msg);
}
PersistRequestOrmUpdate request = new PersistRequestOrmUpdate(server, mgr, ormUpdate, (SpiTransaction) t, persistExecute);
try {
request.initTransIfRequired();
int rc = request.executeOrQueue();
request.commitTransIfRequired();
return rc;
} catch (RuntimeException e) {
request.rollbackTransIfRequired();
throw e;
}
}
/**
* Execute the updateSql.
*/
public int executeSqlUpdate(SqlUpdate updSql, Transaction t) {
PersistRequestUpdateSql request = new PersistRequestUpdateSql(server, updSql, (SpiTransaction) t, persistExecute);
try {
request.initTransIfRequired();
int rc = request.executeOrQueue();
request.commitTransIfRequired();
return rc;
} catch (RuntimeException e) {
request.rollbackTransIfRequired();
throw e;
}
}
/**
* Recursively delete the bean. This calls back to the EbeanServer.
*/
private void deleteRecurse(Object detailBean, Transaction t) {
// NB: a new PersistRequest is made
server.delete(detailBean, t);
}
/**
* Force an Update using the given bean.
*/
public void forceUpdate(EntityBean entityBean, Transaction t, boolean deleteMissingChildren) {
PersistRequestBean> req = createRequest(entityBean, t, null, PersistRequest.Type.UPDATE);
req.setStatelessUpdate(true, deleteMissingChildren);
try {
req.initTransIfRequired();
update(req);
req.commitTransIfRequired();
// finished a 'normal' update
return;
} catch (RuntimeException ex) {
req.rollbackTransIfRequired();
throw ex;
}
}
public void save(EntityBean bean, Transaction t) {
saveRecurse(bean, t, null);
}
/**
* Explicitly specify to insert this bean.
*/
public void forceInsert(EntityBean bean, Transaction t) {
PersistRequestBean> req = createRequest(bean, t, null, PersistRequest.Type.INSERT);
try {
req.initTransIfRequired();
insert(req);
req.commitTransIfRequired();
} catch (RuntimeException ex) {
req.rollbackTransIfRequired();
throw ex;
}
}
private void saveRecurse(Object bean, Transaction t, Object parentBean) {
if (bean == null) {
throw new NullPointerException(Message.msg("bean.isnull"));
}
if (bean instanceof EntityBean == false) {
throw new IllegalArgumentException("This bean is of type ["+bean.getClass()+"] is not enhanced?");
}
PersistRequestBean> req = createRequest(bean, t, parentBean, PersistRequest.Type.DETERMINE);
try {
req.initTransIfRequired();
saveEnhanced(req);
req.commitTransIfRequired();
} catch (RuntimeException ex) {
req.rollbackTransIfRequired();
throw ex;
}
}
/**
* Insert or update the bean depending on PersistControl and the bean state.
*/
private void saveEnhanced(PersistRequestBean> request) {
EntityBeanIntercept intercept = request.getEntityBeanIntercept();
if (request.isReference()) {
// its a reference...
if (request.isPersistCascade()) {
// save any associated List held beans
intercept.setLoaded();
saveAssocMany(false, request);
intercept.setReference(-1);
}
} else {
if (request.isInsert()) {
insert(request);
} else {
update(request);
}
}
}
/**
* Insert the bean.
*/
private void insert(PersistRequestBean> request) {
if (request.isRegisteredBean()){
// skip as already inserted/updated in this request (recursive cascading)
return;
}
try {
if (request.isPersistCascade()) {
// save associated One beans recursively first
saveAssocOne(request);
}
// set the IDGenerated value if required
setIdGenValue(request);
request.executeOrQueue();
if (request.isPersistCascade()) {
// save any associated List held beans
saveAssocMany(true, request);
}
} finally {
request.unRegisterBean();
}
}
/**
* Update the bean. Return NOT_SAVED if the bean values have not changed.
*/
private void update(PersistRequestBean> request) {
if (request.isRegisteredBean()){
// skip as already inserted/updated in this request (recursive cascading)
return;
}
try {
if (request.isPersistCascade()) {
// save associated One beans recursively first
saveAssocOne(request);
}
if (request.isDirty()) {
request.executeOrQueue();
} else {
// skip validation on unchanged bean
if (logger.isDebugEnabled()) {
logger.debug(Message.msg("persist.update.skipped", request.getBean()));
}
}
if (request.isPersistCascade()) {
// save all the beans in assocMany's after
saveAssocMany(false, request);
}
} finally {
request.unRegisterBean();
}
}
/**
* Delete the bean with the explicit transaction.
*/
public void delete(EntityBean bean, Transaction t) {
PersistRequestBean> req = createRequest(bean, t, null, PersistRequest.Type.DELETE);
if (req.isRegisteredForDeleteBean()) {
// skip deleting bean. Used where cascade is on
// both sides of a relationship
if (logger.isDebugEnabled()) {
logger.debug("skipping delete on alreadyRegistered " + bean);
}
return;
}
try {
req.initTransIfRequired();
delete(req);
req.commitTransIfRequired();
} catch (RuntimeException ex) {
req.rollbackTransIfRequired();
throw ex;
}
}
private void deleteList(List> beanList, Transaction t) {
for (int i = 0; i < beanList.size(); i++) {
EntityBean bean = (EntityBean)beanList.get(i);
delete(bean, t);
}
}
/**
* Delete by a List of Id's.
*/
public void deleteMany(Class> beanType, Collection> ids, Transaction transaction) {
if (ids == null || ids.size() == 0) {
return;
}
BeanDescriptor> descriptor = beanDescriptorManager.getBeanDescriptor(beanType);
ArrayList