Modified: ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java?rev=812692&r1=812691&r2=812692&view=diff ============================================================================== --- ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java (original) +++ ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java Tue Sep 8 20:56:14 2009 @@ -18,25 +18,58 @@ */ package org.ofbiz.entity; +import java.io.FileNotFoundException; +import java.io.IOException; import java.net.URL; +import java.util.Arrays; import java.util.Collection; +import java.util.Iterator; import java.util.List; -import java.util.Locale; +import java.util.ListIterator; import java.util.Map; import java.util.Set; +import java.util.TreeSet; import javax.xml.parsers.ParserConfigurationException; +import javolution.util.FastList; +import javolution.util.FastMap; + +import org.ofbiz.base.util.Debug; +import org.ofbiz.base.util.GeneralRuntimeException; +import org.ofbiz.base.util.UtilDateTime; +import org.ofbiz.base.util.UtilFormatOut; +import org.ofbiz.base.util.UtilMisc; +import org.ofbiz.base.util.UtilValidate; +import org.ofbiz.base.util.UtilXml; +import org.ofbiz.base.util.cache.CacheLine; +import org.ofbiz.base.util.cache.UtilCache; import org.ofbiz.entity.cache.Cache; import org.ofbiz.entity.condition.EntityCondition; +import org.ofbiz.entity.condition.EntityConditionList; +import org.ofbiz.entity.condition.EntityExpr; +import org.ofbiz.entity.condition.EntityOperator; +import org.ofbiz.entity.config.DatasourceInfo; +import org.ofbiz.entity.config.DelegatorInfo; +import org.ofbiz.entity.config.EntityConfigUtil; import org.ofbiz.entity.datasource.GenericHelper; +import org.ofbiz.entity.datasource.GenericHelperFactory; import org.ofbiz.entity.eca.EntityEcaHandler; import org.ofbiz.entity.model.DynamicViewEntity; import org.ofbiz.entity.model.ModelEntity; +import org.ofbiz.entity.model.ModelEntityChecker; +import org.ofbiz.entity.model.ModelField; import org.ofbiz.entity.model.ModelFieldType; import org.ofbiz.entity.model.ModelFieldTypeReader; import org.ofbiz.entity.model.ModelGroupReader; +import org.ofbiz.entity.model.ModelKeyMap; import org.ofbiz.entity.model.ModelReader; +import org.ofbiz.entity.model.ModelRelation; +import org.ofbiz.entity.model.ModelViewEntity; +import org.ofbiz.entity.serialize.SerializeException; +import org.ofbiz.entity.serialize.XmlSerializer; +import org.ofbiz.entity.transaction.GenericTransactionException; +import org.ofbiz.entity.transaction.TransactionUtil; import org.ofbiz.entity.util.DistributedCacheClear; import org.ofbiz.entity.util.EntityCrypto; import org.ofbiz.entity.util.EntityFindOptions; @@ -44,127 +77,618 @@ import org.ofbiz.entity.util.SequenceUtil; import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.w3c.dom.Node; import org.xml.sax.SAXException; /** - * Delegator Interface + * Generic Data Source Delegator Class + * */ -public interface GenericDelegator { +public class GenericDelegator implements DelegatorInterface { - public void clearAllCacheLinesByDummyPK(Collection<GenericPK> dummyPKs); + public static final String module = GenericDelegator.class.getName(); - public void clearAllCacheLinesByValue(Collection<GenericValue> values); + protected ModelReader modelReader = null; + protected ModelGroupReader modelGroupReader = null; - /** This method is a shortcut to completely clear all entity engine caches. - * For performance reasons this should not be called very often. + /** This flag is only here for lower level technical testing, it shouldn't be user configurable (or at least I don't think so yet); when true all operations without a transaction will be wrapped in one; seems to be necessary for some (all?) XA aware connection pools, and should improve overall stability and consistency */ + public static final boolean alwaysUseTransaction = true; + + /** the delegatorCache will now be a HashMap, allowing reload of definitions, + * but the delegator will always be the same object for the given name */ + protected static Map<String, GenericDelegator> delegatorCache = FastMap.newInstance(); + protected String delegatorName = null; + protected DelegatorInfo delegatorInfo = null; + + protected Cache cache = null; + + /** keeps a list of field key sets used in the by and cache, a Set (of Sets of fieldNames) for each entityName */ + protected Map andCacheFieldSets = FastMap.newInstance(); + + protected DistributedCacheClear distributedCacheClear = null; + protected EntityEcaHandler<?> entityEcaHandler = null; + protected SequenceUtil sequencer = null; + protected EntityCrypto crypto = null; + + /** A ThreadLocal variable to allow other methods to specify a user identifier (usually the userLoginId, though technically the Entity Engine doesn't know anything about the UserLogin entity) */ + protected static ThreadLocal<List<Object>> userIdentifierStack = new ThreadLocal<List<Object>>(); + /** A ThreadLocal variable to allow other methods to specify a session identifier (usually the visitId, though technically the Entity Engine doesn't know anything about the Visit entity) */ + protected static ThreadLocal<List<Object>> sessionIdentifierStack = new ThreadLocal<List<Object>>(); + + private boolean testMode = false; + private boolean testRollbackInProgress = false; + private List<TestOperation> testOperations = null; + private enum OperationType {INSERT, UPDATE, DELETE}; + + private String originalDelegatorName = null; + + + public static GenericDelegator getGenericDelegator(String delegatorName) { + if (delegatorName == null) { + delegatorName = "default"; + Debug.logWarning(new Exception("Location where getting delegator with null name"), "Got a getGenericDelegator call with a null delegatorName, assuming default for the name.", module); + } + GenericDelegator delegator = delegatorCache.get(delegatorName); + + if (delegator == null) { + synchronized (GenericDelegator.class) { + // must check if null again as one of the blocked threads can still enter + delegator = delegatorCache.get(delegatorName); + if (delegator == null) { + if (Debug.infoOn()) Debug.logInfo("Creating new delegator [" + delegatorName + "] (" + Thread.currentThread().getName() + ")", module); + //Debug.logInfo(new Exception(), "Showing stack where new delegator is being created...", module); + try { + delegator = new GenericDelegator(delegatorName); + } catch (GenericEntityException e) { + Debug.logError(e, "Error creating delegator", module); + } + if (delegator != null) { + delegatorCache.put(delegatorName, delegator); + } else { + Debug.logError("Could not create delegator with name " + delegatorName + ", constructor failed (got null value) not sure why/how.", module); + } + } + } + } + return delegator; + } + + protected static List<Object> getUserIdentifierStack() { + List<Object> curValList = userIdentifierStack.get(); + if (curValList == null) { + curValList = FastList.newInstance(); + userIdentifierStack.set(curValList); + } + return curValList; + } + + public static String getCurrentUserIdentifier() { + List<Object> curValList = getUserIdentifierStack(); + Object curVal = curValList.size() > 0 ? curValList.get(0) : null; + if (curVal == null) { + return null; + } else { + return curVal.toString(); + } + } + + public static void pushUserIdentifier(String userIdentifier) { + if (userIdentifier == null) { + return; + } + List<Object> curValList = getUserIdentifierStack(); + curValList.add(0, userIdentifier); + } + + public static String popUserIdentifier() { + List<Object> curValList = getUserIdentifierStack(); + if (curValList.size() == 0) { + return null; + } else { + return (String) curValList.remove(0); + } + } + + public static void clearUserIdentifierStack() { + List<Object> curValList = getUserIdentifierStack(); + curValList.clear(); + } + + protected static List<Object> getSessionIdentifierStack() { + List<Object> curValList = sessionIdentifierStack.get(); + if (curValList == null) { + curValList = FastList.newInstance(); + sessionIdentifierStack.set(curValList); + } + return curValList; + } + + public static String getCurrentSessionIdentifier() { + List<Object> curValList = getSessionIdentifierStack(); + Object curVal = curValList.size() > 0 ? curValList.get(0) : null; + if (curVal == null) { + return null; + } else { + return curVal.toString(); + } + } + + public static void pushSessionIdentifier(String sessionIdentifier) { + if (sessionIdentifier == null) { + return; + } + List<Object> curValList = getSessionIdentifierStack(); + curValList.add(0, sessionIdentifier); + } + + public static String popSessionIdentifier() { + List<Object> curValList = getSessionIdentifierStack(); + if (curValList.size() == 0) { + return null; + } else { + return (String) curValList.remove(0); + } + } + + public static void clearSessionIdentifierStack() { + List<Object> curValList = getSessionIdentifierStack(); + curValList.clear(); + } + + /** Only allow creation through the factory method */ + protected GenericDelegator() {} + + /** Only allow creation through the factory method */ + protected GenericDelegator(String delegatorName) throws GenericEntityException { + //if (Debug.infoOn()) Debug.logInfo("Creating new Delegator with name \"" + delegatorName + "\".", module); + + this.delegatorName = delegatorName; + this.modelReader = ModelReader.getModelReader(delegatorName); + this.modelGroupReader = ModelGroupReader.getModelGroupReader(delegatorName); + + cache = new Cache(delegatorName); + + // do the entity model check + List<String> warningList = FastList.newInstance(); + Debug.logImportant("Doing entity definition check...", module); + ModelEntityChecker.checkEntities(this, warningList); + if (warningList.size() > 0) { + Debug.logWarning("=-=-=-=-= Found " + warningList.size() + " warnings when checking the entity definitions:", module); + for (String warning: warningList) { + Debug.logWarning(warning, module); + } + } + + // initialize helpers by group + Set<String> groupNames = getModelGroupReader().getGroupNames(delegatorName); + Iterator<String> groups = UtilMisc.toIterator(groupNames); + while (groups != null && groups.hasNext()) { + String groupName = groups.next(); + String helperName = this.getGroupHelperName(groupName); + + if (Debug.infoOn()) Debug.logInfo("Delegator \"" + delegatorName + "\" initializing helper \"" + + helperName + "\" for entity group \"" + groupName + "\".", module); + TreeSet<String> helpersDone = new TreeSet<String>(); + if (helperName != null && helperName.length() > 0) { + // make sure each helper is only loaded once + if (helpersDone.contains(helperName)) { + if (Debug.infoOn()) Debug.logInfo("Helper \"" + helperName + "\" already initialized, not re-initializing.", module); + continue; + } + helpersDone.add(helperName); + // pre-load field type defs, the return value is ignored + ModelFieldTypeReader.getModelFieldTypeReader(helperName); + // get the helper and if configured, do the datasource check + GenericHelper helper = GenericHelperFactory.getHelper(helperName); + + DatasourceInfo datasourceInfo = EntityConfigUtil.getDatasourceInfo(helperName); + if (datasourceInfo.checkOnStart) { + if (Debug.infoOn()) Debug.logInfo("Doing database check as requested in entityengine.xml with addMissing=" + datasourceInfo.addMissingOnStart, module); + try { + helper.checkDataSource(this.getModelEntityMapByGroup(groupName), null, datasourceInfo.addMissingOnStart); + } catch (GenericEntityException e) { + Debug.logWarning(e, e.getMessage(), module); + } + } + } + } + + // NOTE: doing some things before the ECAs and such to make sure it is in place just in case it is used in a service engine startup thing or something + // put the delegator in the master Map by its name + GenericDelegator.delegatorCache.put(delegatorName, this); + + // setup the crypto class + this.crypto = new EntityCrypto(this); + + //time to do some tricks with manual class loading that resolves circular dependencies, like calling services... + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + + // if useDistributedCacheClear is false do nothing since the + // distributedCacheClear member field with a null value will cause the + // dcc code to do nothing + if (getDelegatorInfo().useDistributedCacheClear) { + // initialize the distributedCacheClear mechanism + String distributedCacheClearClassName = getDelegatorInfo().distributedCacheClearClassName; + + try { + Class dccClass = loader.loadClass(distributedCacheClearClassName); + this.distributedCacheClear = (DistributedCacheClear) dccClass.newInstance(); + this.distributedCacheClear.setDelegator(this, getDelegatorInfo().distributedCacheClearUserLoginId); + } catch (ClassNotFoundException e) { + Debug.logWarning(e, "DistributedCacheClear class with name " + distributedCacheClearClassName + " was not found, distributed cache clearing will be disabled", module); + } catch (InstantiationException e) { + Debug.logWarning(e, "DistributedCacheClear class with name " + distributedCacheClearClassName + " could not be instantiated, distributed cache clearing will be disabled", module); + } catch (IllegalAccessException e) { + Debug.logWarning(e, "DistributedCacheClear class with name " + distributedCacheClearClassName + " could not be accessed (illegal), distributed cache clearing will be disabled", module); + } catch (ClassCastException e) { + Debug.logWarning(e, "DistributedCacheClear class with name " + distributedCacheClearClassName + " does not implement the DistributedCacheClear interface, distributed cache clearing will be disabled", module); + } + } else { + Debug.logInfo("Distributed Cache Clear System disabled for delegator [" + delegatorName + "]", module); + } + + // setup the Entity ECA Handler + initEntityEcaHandler(); + } + + public void initEntityEcaHandler() { + if (getDelegatorInfo().useEntityEca) { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + // initialize the entity eca handler + String entityEcaHandlerClassName = getDelegatorInfo().entityEcaHandlerClassName; + + try { + Class eecahClass = loader.loadClass(entityEcaHandlerClassName); + this.entityEcaHandler = (EntityEcaHandler) eecahClass.newInstance(); + this.entityEcaHandler.setDelegator(this); + } catch (ClassNotFoundException e) { + Debug.logWarning(e, "EntityEcaHandler class with name " + entityEcaHandlerClassName + " was not found, Entity ECA Rules will be disabled", module); + } catch (InstantiationException e) { + Debug.logWarning(e, "EntityEcaHandler class with name " + entityEcaHandlerClassName + " could not be instantiated, Entity ECA Rules will be disabled", module); + } catch (IllegalAccessException e) { + Debug.logWarning(e, "EntityEcaHandler class with name " + entityEcaHandlerClassName + " could not be accessed (illegal), Entity ECA Rules will be disabled", module); + } catch (ClassCastException e) { + Debug.logWarning(e, "EntityEcaHandler class with name " + entityEcaHandlerClassName + " does not implement the EntityEcaHandler interface, Entity ECA Rules will be disabled", module); + } + } else { + Debug.logInfo("Entity ECA Handler disabled for delegator [" + delegatorName + "]", module); + } + } + + public String getDelegatorName() { + return this.delegatorName; + } + + /** Gets the name of the server configuration that corresponds to this delegator + * @return server configuration name */ - public void clearAllCaches(); + public String getOriginalDelegatorName() { + return this.originalDelegatorName == null ? this.delegatorName : this.originalDelegatorName; + } + + protected DelegatorInfo getDelegatorInfo() { + if (this.delegatorInfo == null) { + this.delegatorInfo = EntityConfigUtil.getDelegatorInfo(this.delegatorName); + } + return this.delegatorInfo; + } - public void clearAllCaches(boolean distribute); + /** Gets the instance of ModelReader that corresponds to this delegator + *@return ModelReader that corresponds to this delegator + */ + public ModelReader getModelReader() { + return this.modelReader; + } - /** Remove a CACHED Generic Entity from the cache by its primary key, does NOT - * check to see if the passed GenericPK is a complete primary key. - * Also tries to clear the corresponding all cache entry. - *@param primaryKey The primary key to clear by. + /** Gets the instance of ModelGroupReader that corresponds to this delegator + *@return ModelGroupReader that corresponds to this delegator */ - public void clearCacheLine(GenericPK primaryKey); + public ModelGroupReader getModelGroupReader() { + return this.modelGroupReader; + } - public void clearCacheLine(GenericPK primaryKey, boolean distribute); + /** Gets the instance of ModelEntity that corresponds to this delegator and the specified entityName + *@param entityName The name of the entity to get + *@return ModelEntity that corresponds to this delegator and the specified entityName + */ + public ModelEntity getModelEntity(String entityName) { + try { + return getModelReader().getModelEntity(entityName); + } catch (GenericEntityException e) { + Debug.logError(e, "Error getting entity definition from model", module); + return null; + } + } - /** Remove a CACHED GenericValue from as many caches as it can. Automatically - * tries to remove entries from the all cache, the by primary key cache, and - * the by and cache. This is the ONLY method that tries to clear automatically - * from the by and cache. - *@param value The GenericValue to clear by. + /** Gets the helper name that corresponds to this delegator and the specified entityName + *@param entityName The name of the entity to get the helper for + *@return String with the helper name that corresponds to this delegator and the specified entityName */ - public void clearCacheLine(GenericValue value); + public String getEntityGroupName(String entityName) { + return getModelGroupReader().getEntityGroupName(entityName, getOriginalDelegatorName()); + } - public void clearCacheLine(GenericValue value, boolean distribute); + /** Gets a Map of entity name & entity model pairs that are in the named group + *@param groupName The name of the group + *@return Map of entityName String keys and ModelEntity instance values + */ + public Map<String, ModelEntity> getModelEntityMapByGroup(String groupName) throws GenericEntityException { + Set<String> entityNameSet = getModelGroupReader().getEntityNamesByGroup(groupName); - /** Remove all CACHED Generic Entity (List) from the cache - *@param entityName The Name of the Entity as defined in the entity XML file + if (this.getDelegatorInfo().defaultGroupName.equals(groupName)) { + // add all entities with no group name to the Set + Set<String> allEntityNames = this.getModelReader().getEntityNames(); + for (String entityName: allEntityNames) { + if (this.getDelegatorInfo().defaultGroupName.equals(getModelGroupReader().getEntityGroupName(entityName, getDelegatorName()))) { + entityNameSet.add(entityName); + } + } + } + + Map<String, ModelEntity> entities = FastMap.newInstance(); + if (entityNameSet == null || entityNameSet.size() == 0) { + return entities; + } + + int errorCount = 0; + for (String entityName: entityNameSet) { + try { + ModelEntity entity = getModelReader().getModelEntity(entityName); + if (entity != null) { + entities.put(entity.getEntityName(), entity); + } else { + throw new IllegalStateException("Could not find entity with name " + entityName); + } + } catch (GenericEntityException ex) { + errorCount++; + Debug.logError("Entity [" + entityName + "] named in Entity Group with name " + groupName + " are not defined in any Entity Definition file", module); + } + } + + if (errorCount > 0) { + Debug.logError(errorCount + " entities were named in ModelGroup but not defined in any EntityModel", module); + } + + return entities; + } + + /** Gets the helper name that corresponds to this delegator and the specified entityName + *@param groupName The name of the group to get the helper name for + *@return String with the helper name that corresponds to this delegator and the specified entityName */ - public void clearCacheLine(String entityName); + public String getGroupHelperName(String groupName) { + return this.getDelegatorInfo().groupMap.get(groupName); + } - /** Remove a CACHED Generic Entity (List) from the cache, either a PK, ByAnd, or All - *@param entityName The Name of the Entity as defined in the entity XML file - *@param fields The fields of the named entity to query by with their corresponding values + /** Gets the helper name that corresponds to this delegator and the specified entityName + *@param entityName The name of the entity to get the helper name for + *@return String with the helper name that corresponds to this delegator and the specified entityName */ - public void clearCacheLine(String entityName, Map<String, ? extends Object> fields); + public String getEntityHelperName(String entityName) { + return this.getGroupHelperName(this.getEntityGroupName(entityName)); + } - /** Remove a CACHED Generic Entity (List) from the cache, either a PK, ByAnd, or All - *@param entityName The Name of the Entity as defined in the entity XML file - *@param fields The fields of the named entity to query by with their corresponding values + /** Gets the helper name that corresponds to this delegator and the specified entity + *@param entity The entity to get the helper for + *@return String with the helper name that corresponds to this delegator and the specified entity */ - public void clearCacheLine(String entityName, Object... fields); + public String getEntityHelperName(ModelEntity entity) { + if (entity == null) + return null; + return getEntityHelperName(entity.getEntityName()); + } - public void clearCacheLineByCondition(String entityName, EntityCondition condition); + /** Gets the an instance of helper that corresponds to this delegator and the specified entityName + *@param entityName The name of the entity to get the helper for + *@return GenericHelper that corresponds to this delegator and the specified entityName + */ + public GenericHelper getEntityHelper(String entityName) throws GenericEntityException { + String helperName = getEntityHelperName(entityName); - public void clearCacheLineByCondition(String entityName, EntityCondition condition, boolean distribute); + if (helperName != null && helperName.length() > 0) { + return GenericHelperFactory.getHelper(helperName); + } else { + throw new GenericEntityException("There is no datasource (Helper) configured for the entity-group [" + this.getEntityGroupName(entityName) + "]; was trying to find datesource (helper) for entity [" + entityName + "]"); + } + } - /** Remove a CACHED Generic Entity from the cache by its primary key. - * Checks to see if the passed GenericPK is a complete primary key, if - * it is then the cache line will be removed from the primaryKeyCache; if it - * is NOT a complete primary key it will remove the cache line from the andCache. - * If the fields map is empty, then the allCache for the entity will be cleared. - *@param dummyPK The dummy primary key to clear by. + /** Gets the an instance of helper that corresponds to this delegator and the specified entity + *@param entity The entity to get the helper for + *@return GenericHelper that corresponds to this delegator and the specified entity + */ + public GenericHelper getEntityHelper(ModelEntity entity) throws GenericEntityException { + return getEntityHelper(entity.getEntityName()); + } + + /** Gets a field type instance by name from the helper that corresponds to the specified entity + *@param entity The entity + *@param type The name of the type + *@return ModelFieldType instance for the named type from the helper that corresponds to the specified entity + */ + public ModelFieldType getEntityFieldType(ModelEntity entity, String type) throws GenericEntityException { + return this.getModelFieldTypeReader(entity).getModelFieldType(type); + } + + public ModelFieldTypeReader getModelFieldTypeReader(ModelEntity entity) { + String helperName = getEntityHelperName(entity); + if (helperName == null || helperName.length() <= 0) { + return null; + } + ModelFieldTypeReader modelFieldTypeReader = ModelFieldTypeReader.getModelFieldTypeReader(helperName); + if (modelFieldTypeReader == null) { + throw new IllegalArgumentException("ModelFieldTypeReader not found for entity " + entity.getEntityName() + " with helper name " + helperName); + } + return modelFieldTypeReader; + } + + /** Gets field type names from the helper that corresponds to the specified entity + *@param entity The entity + *@return Collection of field type names from the helper that corresponds to the specified entity */ - public void clearCacheLineFlexible(GenericEntity dummyPK); + public Collection<String> getEntityFieldTypeNames(ModelEntity entity) throws GenericEntityException { + String helperName = getEntityHelperName(entity); + + if (helperName == null || helperName.length() <= 0) + return null; + ModelFieldTypeReader modelFieldTypeReader = ModelFieldTypeReader.getModelFieldTypeReader(helperName); + + if (modelFieldTypeReader == null) { + throw new GenericEntityException("ModelFieldTypeReader not found for entity " + entity.getEntityName() + " with helper name " + helperName); + } + return modelFieldTypeReader.getFieldTypeNames(); + } + + /** Creates a Entity in the form of a GenericValue without persisting it */ + public GenericValue makeValue(String entityName) { + ModelEntity entity = this.getModelEntity(entityName); + if (entity == null) { + throw new IllegalArgumentException("[GenericDelegator.makeValue] could not find entity for entityName: " + entityName); + } + GenericValue value = GenericValue.create(entity); + value.setDelegator(this); + return value; + } + + /** Creates a Entity in the form of a GenericValue without persisting it */ + public GenericValue makeValue(String entityName, Object... fields) { + return makeValue(entityName, UtilMisc.<String, Object>toMap(fields)); + } + + /** Creates a Entity in the form of a GenericValue without persisting it */ + public GenericValue makeValue(String entityName, Map<String, ? extends Object> fields) { + ModelEntity entity = this.getModelEntity(entityName); + if (entity == null) { + throw new IllegalArgumentException("[GenericDelegator.makeValue] could not find entity for entityName: " + entityName); + } + GenericValue value = GenericValue.create(entity, fields); + value.setDelegator(this); + return value; + } - public void clearCacheLineFlexible(GenericEntity dummyPK, boolean distribute); + /** Creates a Entity in the form of a GenericValue without persisting it */ + public GenericValue makeValueSingle(String entityName, Object singlePkValue) { + ModelEntity entity = this.getModelEntity(entityName); + if (entity == null) { + throw new IllegalArgumentException("[GenericDelegator.makeValue] could not find entity for entityName: " + entityName); + } + GenericValue value = GenericValue.create(entity, singlePkValue); + value.setDelegator(this); + return value; + } + + /** Creates a Entity in the form of a GenericValue without persisting it; only valid fields will be pulled from the fields Map */ + public GenericValue makeValidValue(String entityName, Object... fields) { + return makeValidValue(entityName, UtilMisc.<String, Object>toMap(fields)); + } + + /** Creates a Entity in the form of a GenericValue without persisting it; only valid fields will be pulled from the fields Map */ + public GenericValue makeValidValue(String entityName, Map<String, ? extends Object> fields) { + ModelEntity entity = this.getModelEntity(entityName); + if (entity == null) { + throw new IllegalArgumentException("[GenericDelegator.makeValidValue] could not find entity for entityName: " + entityName); + } + GenericValue value = GenericValue.create(entity); + value.setPKFields(fields, true); + value.setNonPKFields(fields, true); + value.setDelegator(this); + return value; + } + + /** Creates a Primary Key in the form of a GenericPK without persisting it */ + public GenericPK makePK(String entityName) { + return this.makePK(entityName, (Map<String, Object>) null); + } + + /** Creates a Primary Key in the form of a GenericPK without persisting it */ + public GenericPK makePK(String entityName, Object... fields) { + return makePK(entityName, UtilMisc.<String, Object>toMap(fields)); + } - public GenericDelegator cloneDelegator(); + /** Creates a Primary Key in the form of a GenericPK without persisting it */ + public GenericPK makePK(String entityName, Map<String, ? extends Object> fields) { + ModelEntity entity = this.getModelEntity(entityName); + if (entity == null) { + throw new IllegalArgumentException("[GenericDelegator.makePK] could not find entity for entityName: " + entityName); + } + GenericPK pk = GenericPK.create(entity, fields); + + pk.setDelegator(this); + return pk; + } - public GenericDelegator cloneDelegator(String delegatorName); + /** Creates a Primary Key in the form of a GenericPK without persisting it */ + public GenericPK makePKSingle(String entityName, Object singlePkValue) { + ModelEntity entity = this.getModelEntity(entityName); + if (entity == null) { + throw new IllegalArgumentException("[GenericDelegator.makePKSingle] could not find entity for entityName: " + entityName); + } + GenericPK pk = GenericPK.create(entity, singlePkValue); + + pk.setDelegator(this); + return pk; + } /** Creates a Entity in the form of a GenericValue and write it to the datasource *@param primaryKey The GenericPK to create a value in the datasource from *@return GenericValue instance containing the new instance */ - public GenericValue create(GenericPK primaryKey) throws GenericEntityException; + public GenericValue create(GenericPK primaryKey) throws GenericEntityException { + return this.create(primaryKey, true); + } /** Creates a Entity in the form of a GenericValue and write it to the datasource *@param primaryKey The GenericPK to create a value in the datasource from *@param doCacheClear boolean that specifies whether to clear related cache entries for this primaryKey to be created *@return GenericValue instance containing the new instance */ - public GenericValue create(GenericPK primaryKey, boolean doCacheClear) throws GenericEntityException; + public GenericValue create(GenericPK primaryKey, boolean doCacheClear) throws GenericEntityException { + if (primaryKey == null) { + throw new GenericEntityException("Cannot create from a null primaryKey"); + } - /** Creates a Entity in the form of a GenericValue and write it to the datasource - *@param value The GenericValue to create a value in the datasource from - *@return GenericValue instance containing the new instance - */ - public GenericValue create(GenericValue value) throws GenericEntityException; + return this.create(GenericValue.create(primaryKey), doCacheClear); + } - /** Creates a Entity in the form of a GenericValue and write it to the datasource - *@param value The GenericValue to create a value in the datasource from - *@param doCacheClear boolean that specifies whether or not to automatically clear cache entries related to this operation + /** Creates a Entity in the form of a GenericValue and write it to the database *@return GenericValue instance containing the new instance */ - public GenericValue create(GenericValue value, boolean doCacheClear) throws GenericEntityException; + public GenericValue create(String entityName, Object... fields) throws GenericEntityException { + return create(entityName, UtilMisc.<String, Object>toMap(fields)); + } /** Creates a Entity in the form of a GenericValue and write it to the database *@return GenericValue instance containing the new instance */ - public GenericValue create(String entityName, Map<String, ? extends Object> fields) throws GenericEntityException; + public GenericValue create(String entityName, Map<String, ? extends Object> fields) throws GenericEntityException { + if (entityName == null || fields == null) { + return null; + } + ModelEntity entity = this.getModelReader().getModelEntity(entityName); + GenericValue genericValue = GenericValue.create(entity, fields); + + return this.create(genericValue, true); + } /** Creates a Entity in the form of a GenericValue and write it to the database *@return GenericValue instance containing the new instance */ - public GenericValue create(String entityName, Object... fields) throws GenericEntityException; + public GenericValue createSingle(String entityName, Object singlePkValue) throws GenericEntityException { + if (entityName == null || singlePkValue == null) { + return null; + } + ModelEntity entity = this.getModelReader().getModelEntity(entityName); + GenericValue genericValue = GenericValue.create(entity, singlePkValue); - /** Creates or stores an Entity - *@param value The GenericValue instance containing the new or existing instance - *@return GenericValue instance containing the new or updated instance - */ - public GenericValue createOrStore(GenericValue value) throws GenericEntityException; + return this.create(genericValue, true); + } - /** Creates or stores an Entity - *@param value The GenericValue instance containing the new or existing instance - *@param doCacheClear boolean that specifies whether or not to automatically clear cache entries related to this operation - *@return GenericValue instance containing the new or updated instance + /** Creates a Entity in the form of a GenericValue and write it to the datasource + *@param value The GenericValue to create a value in the datasource from + *@return GenericValue instance containing the new instance */ - public GenericValue createOrStore(GenericValue value, boolean doCacheClear) throws GenericEntityException; + public GenericValue create(GenericValue value) throws GenericEntityException { + return this.create(value, true); + } /** Sets the sequenced ID (for entity with one primary key field ONLY), and then does a create in the database * as normal. The reason to do it this way is that it will retry and fix the sequence if somehow the sequencer @@ -172,320 +696,958 @@ *@param value The GenericValue to create a value in the datasource from *@return GenericValue instance containing the new instance */ - public GenericValue createSetNextSeqId(GenericValue value) throws GenericEntityException; + public GenericValue createSetNextSeqId(GenericValue value) throws GenericEntityException { + boolean doCacheClear = true; - /** Creates a Entity in the form of a GenericValue and write it to the database + GenericHelper helper = getEntityHelper(value.getEntityName()); + // just make sure it is this delegator... + value.setDelegator(this); + // this will throw an IllegalArgumentException if the entity for the value does not have one pk field, or if it already has a value set for the one pk field + value.setNextSeqId(); + + boolean beganTransaction = false; + try { + if (alwaysUseTransaction) { + beganTransaction = TransactionUtil.begin(); + } + + EntityEcaRuleRunner<?> ecaRunner = this.getEcaRuleRunner(value.getEntityName()); + ecaRunner.evalRules(EntityEcaHandler.EV_VALIDATE, EntityEcaHandler.OP_CREATE, value, false); + + if (value == null) { + throw new GenericEntityException("Cannot create a null value"); + } + + ecaRunner.evalRules(EntityEcaHandler.EV_RUN, EntityEcaHandler.OP_CREATE, value, false); + + value.setDelegator(this); + this.encryptFields(value); + + // if audit log on for any fields, save new value with no old value because it's a create + if (value != null && value.getModelEntity().getHasFieldWithAuditLog()) { + createEntityAuditLogAll(value, false, false); + } + + try { + value = helper.create(value); + + if (testMode) { + storeForTestRollback(new TestOperation(OperationType.INSERT, value)); + } + } catch (GenericEntityException e) { + // see if this was caused by an existing record before resetting the sequencer and trying again + // NOTE: use the helper directly so ECA rules, etc won't be run + GenericValue existingValue = helper.findByPrimaryKey(value.getPrimaryKey()); + if (existingValue == null) { + throw e; + } else { + Debug.logInfo("Error creating entity record with a sequenced value [" + value.getPrimaryKey() + "], trying again about to refresh bank for entity [" + value.getEntityName() + "]", module); + + // found an existing value... was probably a duplicate key, so clean things up and try again + this.sequencer.forceBankRefresh(value.getEntityName(), 1); + + value.setNextSeqId(); + value = helper.create(value); + Debug.logInfo("Successfully created new entity record on retry with a sequenced value [" + value.getPrimaryKey() + "], after getting refreshed bank for entity [" + value.getEntityName() + "]", module); + + if (testMode) { + storeForTestRollback(new TestOperation(OperationType.INSERT, value)); + } + } + } + + if (value != null) { + value.setDelegator(this); + if (value.lockEnabled()) { + refresh(value, doCacheClear); + } else { + if (doCacheClear) { + ecaRunner.evalRules(EntityEcaHandler.EV_CACHE_CLEAR, EntityEcaHandler.OP_CREATE, value, false); + this.clearCacheLine(value); + } + } + } + + ecaRunner.evalRules(EntityEcaHandler.EV_RETURN, EntityEcaHandler.OP_CREATE, value, false); + + return value; + } catch (GenericEntityException e) { + String errMsg = "Failure in create operation for entity [" + value.getEntityName() + "]: " + e.toString() + ". Rolling back transaction."; + Debug.logError(e, errMsg, module); + try { + // only rollback the transaction if we started one... + TransactionUtil.rollback(beganTransaction, errMsg, e); + } catch (GenericEntityException e2) { + Debug.logError(e2, "[GenericDelegator] Could not rollback transaction: " + e2.toString(), module); + } + // after rolling back, rethrow the exception + throw e; + } finally { + // only commit the transaction if we started one... this will throw an exception if it fails + TransactionUtil.commit(beganTransaction); + } + } + + /** Creates a Entity in the form of a GenericValue and write it to the datasource + *@param value The GenericValue to create a value in the datasource from + *@param doCacheClear boolean that specifies whether or not to automatically clear cache entries related to this operation *@return GenericValue instance containing the new instance */ - public GenericValue createSingle(String entityName, Object singlePkValue) throws GenericEntityException; - - public void decryptFields(GenericEntity entity) throws GenericEntityException; - - public void decryptFields(List<? extends GenericEntity> entities) throws GenericEntityException; - - public void encryptFields(GenericEntity entity) throws GenericEntityException; - - public void encryptFields(List<? extends GenericEntity> entities) throws GenericEntityException; + public GenericValue create(GenericValue value, boolean doCacheClear) throws GenericEntityException { + boolean beganTransaction = false; + try { + if (alwaysUseTransaction) { + beganTransaction = TransactionUtil.begin(); + } + + EntityEcaRuleRunner<?> ecaRunner = this.getEcaRuleRunner(value.getEntityName()); + ecaRunner.evalRules(EntityEcaHandler.EV_VALIDATE, EntityEcaHandler.OP_CREATE, value, false); + + if (value == null) { + throw new GenericEntityException("Cannot create a null value"); + } + GenericHelper helper = getEntityHelper(value.getEntityName()); + + ecaRunner.evalRules(EntityEcaHandler.EV_RUN, EntityEcaHandler.OP_CREATE, value, false); + + value.setDelegator(this); + this.encryptFields(value); + + // if audit log on for any fields, save new value with no old value because it's a create + if (value != null && value.getModelEntity().getHasFieldWithAuditLog()) { + createEntityAuditLogAll(value, false, false); + } + + value = helper.create(value); + + if (testMode) { + storeForTestRollback(new TestOperation(OperationType.INSERT, value)); + } + if (value != null) { + value.setDelegator(this); + if (value.lockEnabled()) { + refresh(value, doCacheClear); + } else { + if (doCacheClear) { + ecaRunner.evalRules(EntityEcaHandler.EV_CACHE_CLEAR, EntityEcaHandler.OP_CREATE, value, false); + this.clearCacheLine(value); + } + } + } + + ecaRunner.evalRules(EntityEcaHandler.EV_RETURN, EntityEcaHandler.OP_CREATE, value, false); + + return value; + } catch (GenericEntityException e) { + String errMsg = "Failure in create operation for entity [" + value.getEntityName() + "]: " + e.toString() + ". Rolling back transaction."; + Debug.logError(e, errMsg, module); + try { + // only rollback the transaction if we started one... + TransactionUtil.rollback(beganTransaction, errMsg, e); + } catch (GenericEntityException e2) { + Debug.logError(e2, "[GenericDelegator] Could not rollback transaction: " + e2.toString(), module); + } + // after rolling back, rethrow the exception + throw e; + } finally { + // only commit the transaction if we started one... this will throw an exception if it fails + TransactionUtil.commit(beganTransaction); + } + } - public Object encryptFieldValue(String entityName, Object fieldValue) throws EntityCryptoException; + /** Creates or stores an Entity + *@param value The GenericValue instance containing the new or existing instance + *@param doCacheClear boolean that specifies whether or not to automatically clear cache entries related to this operation + *@return GenericValue instance containing the new or updated instance + */ + public GenericValue createOrStore(GenericValue value, boolean doCacheClear) throws GenericEntityException { + boolean beganTransaction = false; + try { + if (alwaysUseTransaction) { + beganTransaction = TransactionUtil.begin(); + } + + GenericValue checkValue = this.findOne(value.getEntityName(), value.getPrimaryKey(), false); + if (checkValue != null) { + this.store(value, doCacheClear); + } else { + this.create(value, doCacheClear); + } + if (value.lockEnabled()) { + this.refresh(value); + } + + return value; + } catch (GenericEntityException e) { + String errMsg = "Failure in createOrStore operation for entity [" + value.getEntityName() + "]: " + e.toString() + ". Rolling back transaction."; + Debug.logError(e, errMsg, module); + try { + // only rollback the transaction if we started one... + TransactionUtil.rollback(beganTransaction, errMsg, e); + } catch (GenericEntityException e2) { + Debug.logError(e2, "[GenericDelegator] Could not rollback transaction: " + e2.toString(), module); + } + // after rolling back, rethrow the exception + throw e; + } finally { + // only commit the transaction if we started one... this will throw an exception if it fails + TransactionUtil.commit(beganTransaction); + } + } - /** Finds GenericValues by the conditions specified in the EntityCondition object, the the EntityCondition javadoc for more details. - * NOTE 20080502: 3 references - *@param entityName The name of the Entity as defined in the entity XML file - *@param whereEntityCondition The EntityCondition object that specifies how to constrain this query before any groupings are done (if this is a view entity with group-by aliases) - *@param havingEntityCondition The EntityCondition object that specifies how to constrain this query after any groupings are done (if this is a view entity with group-by aliases) - *@param fieldsToSelect The fields of the named entity to get from the database; if empty or null all fields will be retreived - *@param orderBy The fields of the named entity to order the query by; optionally add a " ASC" for ascending or " DESC" for descending - *@param findOptions An instance of EntityFindOptions that specifies advanced query options. See the EntityFindOptions JavaDoc for more details. - *@return EntityListIterator representing the result of the query: NOTE THAT THIS MUST BE CLOSED (preferably in a finally block) WHEN YOU ARE - * DONE WITH IT, AND DON'T LEAVE IT OPEN TOO LONG BEACUSE IT WILL MAINTAIN A DATABASE CONNECTION. + /** Creates or stores an Entity + *@param value The GenericValue instance containing the new or existing instance + *@return GenericValue instance containing the new or updated instance */ - public EntityListIterator find(String entityName, EntityCondition whereEntityCondition, EntityCondition havingEntityCondition, Set<String> fieldsToSelect, List<String> orderBy, EntityFindOptions findOptions) throws GenericEntityException; + public GenericValue createOrStore(GenericValue value) throws GenericEntityException { + return createOrStore(value, true); + } + + protected void saveEntitySyncRemoveInfo(GenericEntity dummyPK) throws GenericEntityException { + // don't store remove info on entities where it is disabled + if (dummyPK.getModelEntity().getNoAutoStamp() || this.testRollbackInProgress) { + return; + } + + // don't store remove info on things removed on an entity sync + if (dummyPK.getIsFromEntitySync()) { + return; + } + + String serializedPK = null; + try { + serializedPK = XmlSerializer.serialize(dummyPK); + } catch (SerializeException e) { + Debug.logError(e, "Could not serialize primary key to save EntitySyncRemove", module); + } catch (FileNotFoundException e) { + Debug.logError(e, "Could not serialize primary key to save EntitySyncRemove", module); + } catch (IOException e) { + Debug.logError(e, "Could not serialize primary key to save EntitySyncRemove", module); + } + + if (serializedPK != null) { + GenericValue entitySyncRemove = this.makeValue("EntitySyncRemove"); + entitySyncRemove.set("primaryKeyRemoved", serializedPK); + this.createSetNextSeqId(entitySyncRemove); + } + } - /** Finds all Generic entities - * NOTE 20080502: 14 references; all changed to findList - *@param entityName The Name of the Entity as defined in the entity XML file - *@return List containing all Generic entities - *@deprecated Use findList() instead + /** Remove a Generic Entity corresponding to the primaryKey + *@param primaryKey The primary key of the entity to remove. + *@return int representing number of rows effected by this operation */ - @Deprecated - public List<GenericValue> findAll(String entityName) throws GenericEntityException; + public int removeByPrimaryKey(GenericPK primaryKey) throws GenericEntityException { + int retVal = this.removeByPrimaryKey(primaryKey, true); + return retVal; + } - /** Finds all Generic entities - * NOTE 20080502: 10 references; all changed to findList - *@param entityName The Name of the Entity as defined in the entity XML file - *@param orderBy The fields of the named entity to order the query by; optionally add a " ASC" for ascending or " DESC" for descending - *@return List containing all Generic entities - *@deprecated Use findList() instead + /** Remove a Generic Entity corresponding to the primaryKey + *@param primaryKey The primary key of the entity to remove. + *@param doCacheClear boolean that specifies whether to clear cache entries for this primaryKey to be removed + *@return int representing number of rows effected by this operation */ - @Deprecated - public List<GenericValue> findAll(String entityName, List<String> orderBy) throws GenericEntityException; + public int removeByPrimaryKey(GenericPK primaryKey, boolean doCacheClear) throws GenericEntityException { + boolean beganTransaction = false; + try { + if (alwaysUseTransaction) { + beganTransaction = TransactionUtil.begin(); + } + + EntityEcaRuleRunner<?> ecaRunner = this.getEcaRuleRunner(primaryKey.getEntityName()); + ecaRunner.evalRules(EntityEcaHandler.EV_VALIDATE, EntityEcaHandler.OP_REMOVE, primaryKey, false); + + GenericHelper helper = getEntityHelper(primaryKey.getEntityName()); + + if (doCacheClear) { + // always clear cache before the operation + ecaRunner.evalRules(EntityEcaHandler.EV_CACHE_CLEAR, EntityEcaHandler.OP_REMOVE, primaryKey, false); + this.clearCacheLine(primaryKey); + } + + ecaRunner.evalRules(EntityEcaHandler.EV_RUN, EntityEcaHandler.OP_REMOVE, primaryKey, false); + + // if audit log on for any fields, save old value before removing so it's still there + if (primaryKey != null && primaryKey.getModelEntity().getHasFieldWithAuditLog()) { + createEntityAuditLogAll(this.findOne(primaryKey.getEntityName(), primaryKey, false), true, true); + } + + GenericValue removedEntity = null; + if (testMode) { + removedEntity = this.findOne(primaryKey.entityName, primaryKey, false); + } + int num = helper.removeByPrimaryKey(primaryKey); + this.saveEntitySyncRemoveInfo(primaryKey); + + if (testMode) { + storeForTestRollback(new TestOperation(OperationType.DELETE, removedEntity)); + } + + ecaRunner.evalRules(EntityEcaHandler.EV_RETURN, EntityEcaHandler.OP_REMOVE, primaryKey, false); + + return num; + } catch (GenericEntityException e) { + String errMsg = "Failure in removeByPrimaryKey operation for entity [" + primaryKey.getEntityName() + "]: " + e.toString() + ". Rolling back transaction."; + Debug.logError(e, errMsg, module); + try { + // only rollback the transaction if we started one... + TransactionUtil.rollback(beganTransaction, errMsg, e); + } catch (GenericEntityException e2) { + Debug.logError(e2, "[GenericDelegator] Could not rollback transaction: " + e2.toString(), module); + } + // after rolling back, rethrow the exception + throw e; + } finally { + // only commit the transaction if we started one... this will throw an exception if it fails + TransactionUtil.commit(beganTransaction); + } + } - /** Finds all Generic entities - * NOTE 20080502: 0 references - *@param entityName The Name of the Entity as defined in the entity XML file - *@param orderBy The fields of the named entity to order the query by; optionally add a " ASC" for ascending or " DESC" for descending - *@return List containing all Generic entities - *@deprecated Use findList() instead + /** Remove a Generic Value from the database + *@param value The GenericValue object of the entity to remove. + *@return int representing number of rows effected by this operation */ - @Deprecated - public List<GenericValue> findAll(String entityName, String... orderBy) throws GenericEntityException; + public int removeValue(GenericValue value) throws GenericEntityException { + return this.removeValue(value, true); + } - /** Find a number of Generic Value objects by their Primary Keys, all at once - * NOTE 20080502: 0 references - *@param primaryKeys A Collection of primary keys to find by. - *@return List of GenericValue objects corresponding to the passed primaryKey objects - *@deprecated + /** Remove a Generic Value from the database + *@param value The GenericValue object of the entity to remove. + *@param doCacheClear boolean that specifies whether to clear cache entries for this value to be removed + *@return int representing number of rows effected by this operation */ - @Deprecated - public List<GenericValue> findAllByPrimaryKeys(Collection<GenericPK> primaryKeys) throws GenericEntityException; + public int removeValue(GenericValue value, boolean doCacheClear) throws GenericEntityException { + // NOTE: this does not call the GenericDelegator.removeByPrimaryKey method because it has more information to pass to the ECA rule hander + boolean beganTransaction = false; + try { + if (alwaysUseTransaction) { + beganTransaction = TransactionUtil.begin(); + } + + EntityEcaRuleRunner<?> ecaRunner = this.getEcaRuleRunner(value.getEntityName()); + ecaRunner.evalRules(EntityEcaHandler.EV_VALIDATE, EntityEcaHandler.OP_REMOVE, value, false); + + GenericHelper helper = getEntityHelper(value.getEntityName()); + + if (doCacheClear) { + ecaRunner.evalRules(EntityEcaHandler.EV_CACHE_CLEAR, EntityEcaHandler.OP_REMOVE, value, false); + this.clearCacheLine(value); + } + + ecaRunner.evalRules(EntityEcaHandler.EV_RUN, EntityEcaHandler.OP_REMOVE, value, false); + + // if audit log on for any fields, save old value before actual remove + if (value != null && value.getModelEntity().getHasFieldWithAuditLog()) { + createEntityAuditLogAll(value, true, true); + } + + GenericValue removedValue = null; + if (testMode) { + removedValue = this.findOne(value.getEntityName(), value.getPrimaryKey(), false); + } + + int num = helper.removeByPrimaryKey(value.getPrimaryKey()); + + if (testMode) { + storeForTestRollback(new TestOperation(OperationType.DELETE, removedValue)); + } + + this.saveEntitySyncRemoveInfo(value.getPrimaryKey()); + + ecaRunner.evalRules(EntityEcaHandler.EV_RETURN, EntityEcaHandler.OP_REMOVE, value, false); + + return num; + } catch (GenericEntityException e) { + String errMsg = "Failure in removeValue operation for entity [" + value.getEntityName() + "]: " + e.toString() + ". Rolling back transaction."; + Debug.logError(e, errMsg, module); + try { + // only rollback the transaction if we started one... + TransactionUtil.rollback(beganTransaction, errMsg, e); + } catch (GenericEntityException e2) { + Debug.logError(e2, "[GenericDelegator] Could not rollback transaction: " + e2.toString(), module); + } + // after rolling back, rethrow the exception + throw e; + } finally { + // only commit the transaction if we started one... this will throw an exception if it fails + TransactionUtil.commit(beganTransaction); + } + } - /** Find a number of Generic Value objects by their Primary Keys, all at once; - * this first looks in the local cache for each PK and if there then it puts it - * in the return list rather than putting it in the batch to send to - * a given helper. - * NOTE 20080502: 0 references - *@param primaryKeys A Collection of primary keys to find by. - *@return List of GenericValue objects corresponding to the passed primaryKey objects - *@deprecated + /** Removes/deletes Generic Entity records found by all of the specified fields (ie: combined using AND) + *@param entityName The Name of the Entity as defined in the entity XML file + *@param fields The fields of the named entity to query by with their corresponding values + *@return int representing number of rows effected by this operation */ - @Deprecated - public List<GenericValue> findAllByPrimaryKeysCache(Collection<GenericPK> primaryKeys) throws GenericEntityException; + public int removeByAnd(String entityName, Object... fields) throws GenericEntityException { + return removeByAnd(entityName, UtilMisc.<String, Object>toMap(fields)); + } - /** Finds all Generic entities, looking first in the cache - * NOTE 20080502: 4 references; all changed to findList + /** Removes/deletes Generic Entity records found by all of the specified fields (ie: combined using AND) *@param entityName The Name of the Entity as defined in the entity XML file - *@return List containing all Generic entities - *@deprecated Use findList() instead + *@param fields The fields of the named entity to query by with their corresponding values + *@return int representing number of rows effected by this operation */ - @Deprecated - public List<GenericValue> findAllCache(String entityName) throws GenericEntityException; + public int removeByAnd(String entityName, Map<String, ? extends Object> fields) throws GenericEntityException { + return this.removeByAnd(entityName, fields, true); + } - /** Finds all Generic entities, looking first in the cache; uses orderBy for lookup, but only keys results on the entityName and fields - * NOTE 20080502: 2 references; all changed to findList + /** Removes/deletes Generic Entity records found by all of the specified fields (ie: combined using AND) *@param entityName The Name of the Entity as defined in the entity XML file - *@param orderBy The fields of the named entity to order the query by; optionally add a " ASC" for ascending or " DESC" for descending - *@return List containing all Generic entities - *@deprecated Use findList() instead + *@param doCacheClear boolean that specifies whether to clear cache entries for this value to be removed + *@param fields The fields of the named entity to query by with their corresponding values + *@return int representing number of rows effected by this operation */ - @Deprecated - public List<GenericValue> findAllCache(String entityName, List<String> orderBy) throws GenericEntityException; + public int removeByAnd(String entityName, boolean doCacheClear, Object... fields) throws GenericEntityException { + return removeByAnd(entityName, UtilMisc.<String, Object>toMap(fields), doCacheClear); + } - /** Finds all Generic entities, looking first in the cache; uses orderBy for lookup, but only keys results on the entityName and fields - * NOTE 20080502: 0 references + /** Removes/deletes Generic Entity records found by all of the specified fields (ie: combined using AND) *@param entityName The Name of the Entity as defined in the entity XML file - *@param orderBy The fields of the named entity to order the query by; optionally add a " ASC" for ascending or " DESC" for descending - *@return List containing all Generic entities - *@deprecated Use findList() instead + *@param fields The fields of the named entity to query by with their corresponding values + *@param doCacheClear boolean that specifies whether to clear cache entries for this value to be removed + *@return int representing number of rows effected by this operation */ - @Deprecated - public List<GenericValue> findAllCache(String entityName, String... orderBy) throws GenericEntityException; + public int removeByAnd(String entityName, Map<String, ? extends Object> fields, boolean doCacheClear) throws GenericEntityException { + EntityCondition ecl = EntityCondition.makeCondition(fields); + return removeByCondition(entityName, ecl, doCacheClear); + } - /** Finds Generic Entity records by all of the specified expressions (ie: combined using AND) - * NOTE 20080502: 11 references; all changed to findList + /** Removes/deletes Generic Entity records found by the condition *@param entityName The Name of the Entity as defined in the entity XML file - *@param expressions The expressions to use for the lookup, each consisting of at least a field name, an EntityOperator, and a value to compare to - *@return List of GenericValue instances that match the query - *@deprecated Use findList() instead + *@param condition The condition used to restrict the removing + *@return int representing number of rows effected by this operation */ - @Deprecated - public <T extends EntityCondition> List<GenericValue> findByAnd(String entityName, List<T> expressions) throws GenericEntityException; + public int removeByCondition(String entityName, EntityCondition condition) throws GenericEntityException { + return this.removeByCondition(entityName, condition, true); + } - /** Finds Generic Entity records by all of the specified expressions (ie: combined using AND) - * NOTE 20080502: 24 references; all changed to findList + /** Removes/deletes Generic Entity records found by the condition *@param entityName The Name of the Entity as defined in the entity XML file - *@param expressions The expressions to use for the lookup, each consisting of at least a field name, an EntityOperator, and a value to compare to - *@param orderBy The fields of the named entity to order the query by; optionally add a " ASC" for ascending or " DESC" for descending - *@return List of GenericValue instances that match the query - *@deprecated Use findList() instead + *@param condition The condition used to restrict the removing + *@param doCacheClear boolean that specifies whether to clear cache entries for this value to be removed + *@return int representing number of rows effected by this operation */ - @Deprecated - public <T extends EntityCondition> List<GenericValue> findByAnd(String entityName, List<T> expressions, List<String> orderBy) throws GenericEntityException; + public int removeByCondition(String entityName, EntityCondition condition, boolean doCacheClear) throws GenericEntityException { + boolean beganTransaction = false; + try { + if (alwaysUseTransaction) { + beganTransaction = TransactionUtil.begin(); + } + + if (doCacheClear) { + // always clear cache before the operation + this.clearCacheLineByCondition(entityName, condition); + } + ModelEntity modelEntity = getModelReader().getModelEntity(entityName); + GenericHelper helper = getEntityHelper(entityName); + + List<GenericValue> removedEntities = null; + if (testMode) { + removedEntities = this.findList(entityName, condition, null, null, null, false); + } + + int rowsAffected = helper.removeByCondition(modelEntity, condition); + + if (testMode) { + for (GenericValue entity : removedEntities) { + storeForTestRollback(new TestOperation(OperationType.DELETE, entity)); + } + } + + return rowsAffected; + } catch (GenericEntityException e) { + String errMsg = "Failure in removeByCondition operation for entity [" + entityName + "]: " + e.toString() + ". Rolling back transaction."; + Debug.logError(e, errMsg, module); + try { + // only rollback the transaction if we started one... + TransactionUtil.rollback(beganTransaction, errMsg, e); + } catch (GenericEntityException e2) { + Debug.logError(e2, "[GenericDelegator] Could not rollback transaction: " + e2.toString(), module); + } + // after rolling back, rethrow the exception + throw e; + } finally { + // only commit the transaction if we started one... this will throw an exception if it fails + TransactionUtil.commit(beganTransaction); + } + } - /** Finds Generic Entity records by all of the specified fields (ie: combined using AND) - * NOTE 20080502: 264 references - * @param entityName The Name of the Entity as defined in the entity XML file - * @param fields The fields of the named entity to query by with their corresponding values - * @return List of GenericValue instances that match the query + /** Remove the named Related Entity for the GenericValue from the persistent store + *@param relationName String containing the relation name which is the + * combination of relation.title and relation.rel-entity-name as + * specified in the entity XML definition file + *@param value GenericValue instance containing the entity + *@return int representing number of rows effected by this operation */ - public List<GenericValue> findByAnd(String entityName, Map<String, ? extends Object> fields) throws GenericEntityException; + public int removeRelated(String relationName, GenericValue value) throws GenericEntityException { + return this.removeRelated(relationName, value, true); + } - /** Finds Generic Entity records by all of the specified fields (ie: combined using AND) - * NOTE 20080502: 72 references - * @param entityName The Name of the Entity as defined in the entity XML file - * @param fields The fields of the named entity to query by with their corresponding values - * @param orderBy The fields of the named entity to order the query by; - * optionally add a " ASC" for ascending or " DESC" for descending - * @return List of GenericValue instances that match the query - */ - public List<GenericValue> findByAnd(String entityName, Map<String, ? extends Object> fields, List<String> orderBy) throws GenericEntityException; - - /** Finds Generic Entity records by all of the specified fields (ie: combined using AND) - * NOTE 20080502: 1 references - * @param entityName The Name of the Entity as defined in the entity XML file - * @param fields The fields of the named entity to query by with their corresponding values - * @return List of GenericValue instances that match the query - */ - public List<GenericValue> findByAnd(String entityName, Object... fields) throws GenericEntityException; - - /** Finds Generic Entity records by all of the specified expressions (ie: combined using AND) - * NOTE 20080502: 0 references - *@param entityName The Name of the Entity as defined in the entity XML file - *@param expressions The expressions to use for the lookup, each consisting of at least a field name, an EntityOperator, and a value to compare to - *@return List of GenericValue instances that match the query - *@deprecated Use findList() instead + /** Remove the named Related Entity for the GenericValue from the persistent store + *@param relationName String containing the relation name which is the + * combination of relation.title and relation.rel-entity-name as + * specified in the entity XML definition file + *@param value GenericValue instance containing the entity + *@param doCacheClear boolean that specifies whether to clear cache entries for this value to be removed + *@return int representing number of rows effected by this operation */ - @Deprecated - public <T extends EntityCondition> List<GenericValue> findByAnd(String entityName, T... expressions) throws GenericEntityException; + public int removeRelated(String relationName, GenericValue value, boolean doCacheClear) throws GenericEntityException { + ModelEntity modelEntity = value.getModelEntity(); + ModelRelation relation = modelEntity.getRelation(relationName); + + if (relation == null) { + throw new GenericModelException("Could not find relation for relationName: " + relationName + " for value " + value); + } + + Map<String, Object> fields = FastMap.newInstance(); + for (int i = 0; i < relation.getKeyMapsSize(); i++) { + ModelKeyMap keyMap = relation.getKeyMap(i); + fields.put(keyMap.getRelFieldName(), value.get(keyMap.getFieldName())); + } - /** Finds Generic Entity records by all of the specified fields (ie: combined using AND), looking first in the cache; uses orderBy for lookup, but only keys results on the entityName and fields - * NOTE 20080502: 91 references - *@param entityName The Name of the Entity as defined in the entity XML file - *@param fields The fields of the named entity to query by with their corresponding values - *@return List of GenericValue instances that match the query - */ - public List<GenericValue> findByAndCache(String entityName, Map<String, ? extends Object> fields) throws GenericEntityException; + return this.removeByAnd(relation.getRelEntityName(), fields, doCacheClear); + } - /** Finds Generic Entity records by all of the specified fields (ie: combined using AND), looking first in the cache; uses orderBy for lookup, but only keys results on the entityName and fields - * NOTE 20080502: 56 references - *@param entityName The Name of the Entity as defined in the entity XML file - *@param fields The fields of the named entity to query by with their corresponding values - *@param orderBy The fields of the named entity to order the query by; optionally add a " ASC" for ascending or " DESC" for descending - *@return List of GenericValue instances that match the query + /** Refresh the Entity for the GenericValue from the persistent store + *@param value GenericValue instance containing the entity to refresh */ - public List<GenericValue> findByAndCache(String entityName, Map<String, ? extends Object> fields, List<String> orderBy) throws GenericEntityException; + public void refresh(GenericValue value) throws GenericEntityException { + this.refresh(value, true); + } - /** Finds Generic Entity records by all of the specified fields (ie: combined using AND), looking first in the cache; uses orderBy for lookup, but only keys results on the entityName and fields - * NOTE 20080502: 0 references - *@param entityName The Name of the Entity as defined in the entity XML file - *@param fields The fields of the named entity to query by with their corresponding values - *@return List of GenericValue instances that match the query - *@deprecated Use findList() instead + /** Refresh the Entity for the GenericValue from the persistent store + *@param value GenericValue instance containing the entity to refresh + *@param doCacheClear boolean that specifies whether or not to automatically clear cache entries related to this operation */ - @Deprecated - public List<GenericValue> findByAndCache(String entityName, Object... fields) throws GenericEntityException; + public void refresh(GenericValue value, boolean doCacheClear) throws GenericEntityException { + if (doCacheClear) { + // always clear cache before the operation + clearCacheLine(value); + } + GenericPK pk = value.getPrimaryKey(); + GenericValue newValue = this.findOne(pk.getEntityName(), pk, false); + value.refreshFromValue(newValue); + } - /** Finds GenericValues by the conditions specified in the EntityCondition object, the the EntityCondition javadoc for more details. - * NOTE 20080502: 64 references; all changed to findList - *@param entityName The Name of the Entity as defined in the entity model XML file - *@param entityCondition The EntityCondition object that specifies how to constrain this query - *@param fieldsToSelect The fields of the named entity to get from the database; if empty or null all fields will be retreived - *@param orderBy The fields of the named entity to order the query by; optionally add a " ASC" for ascending or " DESC" for descending - *@return List of GenericValue objects representing the result - *@deprecated Use findList() instead + /** Refresh the Entity for the GenericValue from the cache + *@param value GenericValue instance containing the entity to refresh */ - @Deprecated - public List<GenericValue> findByCondition(String entityName, EntityCondition entityCondition, Collection<String> fieldsToSelect, List<String> orderBy) throws GenericEntityException; + public void refreshFromCache(GenericValue value) throws GenericEntityException { + GenericPK pk = value.getPrimaryKey(); + GenericValue newValue = findOne(pk.getEntityName(), pk, true); + value.refreshFromValue(newValue); + } - /** Finds GenericValues by the conditions specified in the EntityCondition object, the the EntityCondition javadoc for more details. - * NOTE 20080502: 6 references; all changed to findList + /** Store a group of values *@param entityName The name of the Entity as defined in the entity XML file - *@param whereEntityCondition The EntityCondition object that specifies how to constrain this query before any groupings are done (if this is a view entity with group-by aliases) - *@param havingEntityCondition The EntityCondition object that specifies how to constrain this query after any groupings are done (if this is a view entity with group-by aliases) - *@param fieldsToSelect The fields of the named entity to get from the database; if empty or null all fields will be retreived - *@param orderBy The fields of the named entity to order the query by; optionally add a " ASC" for ascending or " DESC" for descending - *@param findOptions An instance of EntityFindOptions that specifies advanced query options. See the EntityFindOptions JavaDoc for more details. - *@return List of GenericValue objects representing the result - *@deprecated Use findList() instead + *@param fieldsToSet The fields of the named entity to set in the database + *@param condition The condition that restricts the list of stored values + *@return int representing number of rows effected by this operation + *@throws GenericEntityException */ - @Deprecated - public List<GenericValue> findByCondition(String entityName, EntityCondition whereEntityCondition, EntityCondition havingEntityCondition, Collection<String> fieldsToSelect, List<String> orderBy, EntityFindOptions findOptions) throws GenericEntityException; + public int storeByCondition(String entityName, Map<String, ? extends Object> fieldsToSet, EntityCondition condition) throws GenericEntityException { + return storeByCondition(entityName, fieldsToSet, condition, true); + } - /** Finds GenericValues by the conditions specified in the EntityCondition object, looking first in the cache, see the EntityCondition javadoc for more details. - * NOTE 20080502: 17 references; all changed to findList - *@param entityName The Name of the Entity as defined in the entity model XML file - *@param entityCondition The EntityCondition object that specifies how to constrain this query - *@param fieldsToSelect The fields of the named entity to get from the database; if empty or null all fields will be retreived - *@param orderBy The fields of the named entity to order the query by; optionally add a " ASC" for ascending or " DESC" for descending - *@return List of GenericValue objects representing the result - *@deprecated Use findList() instead + /** Store a group of values + *@param entityName The name of the Entity as defined in the entity XML file + *@param fieldsToSet The fields of the named entity to set in the database + *@param condition The condition that restricts the list of stored values + *@param doCacheClear boolean that specifies whether to clear cache entries for these values + *@return int representing number of rows effected by this operation + *@throws GenericEntityException */ - @Deprecated - public List<GenericValue> findByConditionCache(String entityName, EntityCondition entityCondition, Collection<String> fieldsToSelect, List<String> orderBy) throws GenericEntityException; + public int storeByCondition(String entityName, Map<String, ? extends Object> fieldsToSet, EntityCondition condition, boolean doCacheClear) throws GenericEntityException { + boolean beganTransaction = false; + try { + if (alwaysUseTransaction) { + beganTransaction = TransactionUtil.begin(); + } + + if (doCacheClear) { + // always clear cache before the operation + this.clearCacheLineByCondition(entityName, condition); + } + ModelEntity modelEntity = getModelReader().getModelEntity(entityName); + GenericHelper helper = getEntityHelper(entityName); + + List<GenericValue> updatedEntities = null; + if (testMode) { + updatedEntities = this.findList(entityName, condition, null, null, null, false); + } + + int rowsAffected = helper.storeByCondition(modelEntity, fieldsToSet, condition); + + if (testMode) { + for (GenericValue entity : updatedEntities) { + storeForTestRollback(new TestOperation(OperationType.UPDATE, entity)); + } + } + + return rowsAffected; + } catch (GenericEntityException e) { + String errMsg = "Failure in storeByCondition operation for entity [" + entityName + "]: " + e.toString() + ". Rolling back transaction."; + Debug.logError(e, errMsg, module); + try { + // only rollback the transaction if we started one... + TransactionUtil.rollback(beganTransaction, errMsg, e); + } catch (GenericEntityException e2) { + Debug.logError(e2, "[GenericDelegator] Could not rollback transaction: " + e2.toString(), module); + } + // after rolling back, rethrow the exception + throw e; + } finally { + // only commit the transaction if we started one... this will throw an exception if it fails + TransactionUtil.commit(beganTransaction); + } + } - /** - * NOTE 20080502: 1 references; all changed to findList - *@deprecated Use findList() instead + /** Store the Entity from the GenericValue to the persistent store + *@param value GenericValue instance containing the entity + *@return int representing number of rows effected by this operation */ - @Deprecated - public List<GenericValue> findByLike(String entityName, Map<String, ? extends Object> fields) throws GenericEntityException; + public int store(GenericValue value) throws GenericEntityException { + return this.store(value, true); + } - /** - * NOTE 20080502: 1 references; all changed to findList - *@deprecated Use findList() instead + /** Store the Entity from the GenericValue to the persistent store + *@param value GenericValue instance containing the entity + *@param doCacheClear boolean that specifies whether or not to automatically clear cache entries related to this operation + *@return int representing number of rows effected by this operation */ - @Deprecated - public List<GenericValue> findByLike(String entityName, Map<String, ? extends Object> fields, List<String> orderBy) throws GenericEntityException; + public int store(GenericValue value, boolean doCacheClear) throws GenericEntityException { + boolean beganTransaction = false; + try { + if (alwaysUseTransaction) { + beganTransaction = TransactionUtil.begin(); + } + + EntityEcaRuleRunner<?> ecaRunner = this.getEcaRuleRunner(value.getEntityName()); + ecaRunner.evalRules(EntityEcaHandler.EV_VALIDATE, EntityEcaHandler.OP_STORE, value, false); + GenericHelper helper = getEntityHelper(value.getEntityName()); + + if (doCacheClear) { + // always clear cache before the operation + ecaRunner.evalRules(EntityEcaHandler.EV_CACHE_CLEAR, EntityEcaHandler.OP_STORE, value, false); + this.clearCacheLine(value); + } + + ecaRunner.evalRules(EntityEcaHandler.EV_RUN, EntityEcaHandler.OP_STORE, value, false); + this.encryptFields(value); + + // if audit log on for any fields, save old value before the update so we still have both + if (value != null && value.getModelEntity().getHasFieldWithAuditLog()) { + createEntityAuditLogAll(value, true, false); + } + + GenericValue updatedEntity = null; + + if (testMode) { + updatedEntity = this.findOne(value.entityName, value.getPrimaryKey(), false); + } + + int retVal = helper.store(value); + + if (testMode) { + storeForTestRollback(new TestOperation(OperationType.UPDATE, updatedEntity)); + } + // refresh the valueObject to get the new version + if (value.lockEnabled()) { + refresh(value, doCacheClear); + } + + ecaRunner.evalRules(EntityEcaHandler.EV_RETURN, EntityEcaHandler.OP_STORE, value, false); + + return retVal; + } catch (GenericEntityException e) { + String errMsg = "Failure in store operation for entity [" + value.getEntityName() + "]: " + e.toString() + ". Rolling back transaction."; + Debug.logError(e, errMsg, module); + try { + // only rollback the transaction if we started one... + TransactionUtil.rollback(beganTransaction, errMsg, e); + } catch (GenericEntityException e2) { + Debug.logError(e2, "[GenericDelegator] Could not rollback transaction: " + e2.toString(), module); + } + // after rolling back, rethrow the exception + throw e; + } finally { + // only commit the transaction if we started one... this will throw an exception if it fails + TransactionUtil.commit(beganTransaction); + } + } - /** - * NOTE 20080502: 0 references - *@deprecated Use findList() instead + /** Store the Entities from the List GenericValue instances to the persistent store. + * <br/>This is different than the normal store method in that the store method only does + * an update, while the storeAll method checks to see if each entity exists, then + * either does an insert or an update as appropriate. + * <br/>These updates all happen in one transaction, so they will either all succeed or all fail, + * if the data source supports transactions. This is just like to othersToStore feature + * of the GenericEntity on a create or store. + *@param values List of GenericValue instances containing the entities to store + *@return int representing number of rows effected by this operation */ - @Deprecated - public List<GenericValue> findByLike(String entityName, Object... fields) throws GenericEntityException; + public int storeAll(List<GenericValue> values) throws GenericEntityException { + return this.storeAll(values, true); + } - /** Finds Generic Entity records by all of the specified expressions (ie: combined using OR) - * NOTE 20080502: 2 references; all changed to findList - *@param entityName The Name of the Entity as defined in the entity XML file - *@param expressions The expressions to use for the lookup, each consisting of at least a field name, an EntityOperator, and a value to compare to - *@return List of GenericValue instances that match the query - *@deprecated Use findList() instead + /** Store the Entities from the List GenericValue instances to the persistent store. + * <br/>This is different than the normal store method in that the store method only does + * an update, while the storeAll method checks to see if each entity exists, then + * either does an insert or an update as appropriate. [... 2628 lines stripped ...] |
Free forum by Nabble | Edit this page |