package io.ebeaninternal.server.persist; import io.ebean.CallableSql; import io.ebean.Query; import io.ebean.SqlUpdate; import io.ebean.Transaction; import io.ebean.Update; import io.ebean.bean.BeanCollection; import io.ebean.bean.BeanCollection.ModifyListenMode; import io.ebean.bean.EntityBean; import io.ebean.bean.PersistenceContext; import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.api.SpiTransaction; import io.ebeaninternal.api.SpiUpdate; import io.ebeaninternal.server.core.Message; import io.ebeaninternal.server.core.PersistRequest; import io.ebeaninternal.server.core.PersistRequest.Type; import io.ebeaninternal.server.core.PersistRequestBean; import io.ebeaninternal.server.core.PersistRequestCallableSql; import io.ebeaninternal.server.core.PersistRequestOrmUpdate; import io.ebeaninternal.server.core.PersistRequestUpdateSql; import io.ebeaninternal.server.core.Persister; import io.ebeaninternal.server.deploy.BeanDescriptor; import io.ebeaninternal.server.deploy.BeanDescriptorManager; import io.ebeaninternal.server.deploy.BeanManager; import io.ebeaninternal.server.deploy.BeanProperty; import io.ebeaninternal.server.deploy.BeanPropertyAssocMany; import io.ebeaninternal.server.deploy.BeanPropertyAssocOne; import io.ebeaninternal.server.deploy.IntersectionRow; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.persistence.PersistenceException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; /** * Persister implementation using DML. *
* This object uses DmlPersistExecute to perform the actual persist execution. *
* This object: *
* Note that preDelete fires before the deletion of children. *
* This will automatically copy over any join properties from the parent * bean to the child beans. *
* This is done via MapBeans. *
* This is called prior to deleting the parent bean. *
* 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). *
* Will use delete by object if the child entity has manyToMany relationships. */ private void deleteChildrenById(SpiTransaction t, BeanDescriptor> targetDesc, List childIds, boolean softDelete) { if (targetDesc.propertiesManyToMany().length > 0) { // convert into a list of reference objects and perform delete by object List refList = new ArrayList<>(childIds.size()); for (Object id : childIds) { refList.add(targetDesc.createReference(null, false, id, null)); } deleteList(refList, t, softDelete); } else { // perform delete by statement if possible delete(targetDesc, null, childIds, t, softDelete); } } /** * Save any associated one beans. */ private void saveAssocOne(PersistRequestBean> request, boolean insertMode) { BeanDescriptor> desc = request.getBeanDescriptor(); // imported ones with save cascade BeanPropertyAssocOne>[] ones = desc.propertiesOneImportedSave(); for (BeanPropertyAssocOne> prop : ones) { // check for partial objects if (request.isLoadedProperty(prop)) { EntityBean detailBean = prop.getValueAsEntityBean(request.getEntityBean()); if (detailBean != null && !prop.isSaveRecurseSkippable(detailBean) && !prop.isReference(detailBean) && !request.isParent(detailBean)) { SpiTransaction t = request.getTransaction(); t.depth(-1); saveRecurse(detailBean, t, null, insertMode, request.isPublish()); t.depth(+1); } } } } /** * Support for loading any Imported Associated One properties that are not * loaded but required for Delete cascade. */ private DeleteUnloadedForeignKeys getDeleteUnloadedForeignKeys(PersistRequestBean> request) { DeleteUnloadedForeignKeys fkeys = null; BeanPropertyAssocOne>[] ones = request.getBeanDescriptor().propertiesOneImportedDelete(); for (BeanPropertyAssocOne> one : ones) { if (!request.isLoadedProperty(one)) { // we have cascade Delete on a partially populated bean and // this property was not loaded (so we are going to have to fetch it) if (fkeys == null) { fkeys = new DeleteUnloadedForeignKeys(server, request); } fkeys.add(one); } } return fkeys; } /** * Delete any associated one beans. */ private void deleteAssocOne(PersistRequestBean> request) { BeanPropertyAssocOne>[] ones = request.getBeanDescriptor().propertiesOneImportedDelete(); for (BeanPropertyAssocOne> prop : ones) { if (request.isLoadedProperty(prop)) { Object detailBean = prop.getValue(request.getEntityBean()); if (detailBean != null) { EntityBean detail = (EntityBean) detailBean; if (prop.hasId(detail)) { deleteRecurse(detail, request.getTransaction(), request.isSoftDelete()); } } } } } /** * Set Id Generated value for insert. */ private void setIdGenValue(PersistRequestBean> request) { BeanDescriptor> desc = request.getBeanDescriptor(); if (!desc.isUseIdGenerator()) { return; } BeanProperty idProp = desc.getIdProperty(); if (idProp == null || idProp.isEmbedded()) { // not supporting IdGeneration for concatenated or Embedded return; } EntityBean bean = request.getEntityBean(); Object uid = idProp.getValue(bean); if (DmlUtil.isNullOrZero(uid)) { // generate the nextId and set it to the property Object nextId = desc.nextId(request.getTransaction()); // cast the data type if required and set it desc.convertSetId(nextId, bean); } } /** * Create the Persist Request Object that wraps all the objects used to * perform an insert, update or delete. */ private PersistRequestBean createRequest(T bean, Transaction t, PersistRequest.Type type) { return createRequestInternal(bean, t, type, false, false); } /** * Create the Persist Request Object additionally specifying the publish status. */ private PersistRequestBean createPublishRequest(T bean, Transaction t, PersistRequest.Type type, boolean publish) { return createRequestInternal(bean, t, type, false, publish); } /** * Create the Persist Request Object additionally specifying the publish status. */ private PersistRequestBean createRequestInternal(T bean, Transaction t, PersistRequest.Type type, boolean saveRecurse, boolean publish) { BeanManager mgr = getBeanManager(bean); if (mgr == null) { throw new PersistenceException(errNotRegistered(bean.getClass())); } return createRequest(bean, t, null, mgr, type, saveRecurse, publish); } /** * Create an Insert or Update PersistRequestBean when cascading. * * This call determines the PersistRequest.Type based on bean state and the insert flag (root persist type). */ private PersistRequestBean createRequestRecurse(T bean, Transaction t, Object parentBean, boolean insertMode, boolean publish) { BeanManager mgr = getBeanManager(bean); if (mgr == null) { throw new PersistenceException(errNotRegistered(bean.getClass())); } BeanDescriptor desc = mgr.getBeanDescriptor(); EntityBean entityBean = (EntityBean) bean; PersistRequest.Type type; if (publish) { // insert if it is a new bean (as publish created it) type = entityBean._ebean_getIntercept().isNew() ? Type.INSERT : Type.UPDATE; } else { // determine Insert or Update based on bean state and insert flag type = desc.isInsertMode(entityBean._ebean_getIntercept(), insertMode) ? Type.INSERT : Type.UPDATE; } return createRequest(bean, t, parentBean, mgr, type, true, publish); } /** * Create the Persist Request Object that wraps all the objects used to * perform an insert, update or delete. */ @SuppressWarnings({"unchecked", "rawtypes"}) private PersistRequestBean createRequest(T bean, Transaction t, Object parentBean, BeanManager> mgr, PersistRequest.Type type, boolean saveRecurse, boolean publish) { if (type == Type.DELETE_PERMANENT) { type = Type.DELETE; } else if (type == Type.DELETE && mgr.getBeanDescriptor().isSoftDelete()) { // automatically convert to soft delete for types that support it type = Type.SOFT_DELETE; } return new PersistRequestBean(server, bean, parentBean, mgr, (SpiTransaction) t, persistExecute, type, saveRecurse, publish); } private String errNotRegistered(Class> beanClass) { String msg = "The type [" + beanClass + "] is not a registered entity?"; msg += " If you don't explicitly list the entity classes to use Ebean will search for them in the classpath."; msg += " If the entity is in a Jar check the ebean.search.jars property in ebean.properties file or check ServerConfig.addJar()."; return msg; } /** * Return the BeanDescriptor for a bean that is being persisted. * * Note that this checks to see if the bean is a MapBean with a tableName. * If so it will return the table based BeanDescriptor. * */ @SuppressWarnings("unchecked") private BeanManager getBeanManager(T bean) { return (BeanManager) beanDescriptorManager.getBeanManager(bean.getClass()); } }
* This call determines the PersistRequest.Type based on bean state and the insert flag (root persist type). */ private PersistRequestBean createRequestRecurse(T bean, Transaction t, Object parentBean, boolean insertMode, boolean publish) { BeanManager mgr = getBeanManager(bean); if (mgr == null) { throw new PersistenceException(errNotRegistered(bean.getClass())); } BeanDescriptor desc = mgr.getBeanDescriptor(); EntityBean entityBean = (EntityBean) bean; PersistRequest.Type type; if (publish) { // insert if it is a new bean (as publish created it) type = entityBean._ebean_getIntercept().isNew() ? Type.INSERT : Type.UPDATE; } else { // determine Insert or Update based on bean state and insert flag type = desc.isInsertMode(entityBean._ebean_getIntercept(), insertMode) ? Type.INSERT : Type.UPDATE; } return createRequest(bean, t, parentBean, mgr, type, true, publish); } /** * Create the Persist Request Object that wraps all the objects used to * perform an insert, update or delete. */ @SuppressWarnings({"unchecked", "rawtypes"}) private PersistRequestBean createRequest(T bean, Transaction t, Object parentBean, BeanManager> mgr, PersistRequest.Type type, boolean saveRecurse, boolean publish) { if (type == Type.DELETE_PERMANENT) { type = Type.DELETE; } else if (type == Type.DELETE && mgr.getBeanDescriptor().isSoftDelete()) { // automatically convert to soft delete for types that support it type = Type.SOFT_DELETE; } return new PersistRequestBean(server, bean, parentBean, mgr, (SpiTransaction) t, persistExecute, type, saveRecurse, publish); } private String errNotRegistered(Class> beanClass) { String msg = "The type [" + beanClass + "] is not a registered entity?"; msg += " If you don't explicitly list the entity classes to use Ebean will search for them in the classpath."; msg += " If the entity is in a Jar check the ebean.search.jars property in ebean.properties file or check ServerConfig.addJar()."; return msg; } /** * Return the BeanDescriptor for a bean that is being persisted. * * Note that this checks to see if the bean is a MapBean with a tableName. * If so it will return the table based BeanDescriptor. * */ @SuppressWarnings("unchecked") private BeanManager getBeanManager(T bean) { return (BeanManager) beanDescriptorManager.getBeanManager(bean.getClass()); } }
* Note that this checks to see if the bean is a MapBean with a tableName. * If so it will return the table based BeanDescriptor. *