blob: 1d207eefe0bdae75056f88708569f42700b72c51 [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
// 08/23/2010-2.2 Michael O'Brien
// - 323043: application.xml module ordering may cause weaving not to occur causing an NPE.
// warn if expected "_persistence_//_vh" method not found
// instead of throwing NPE during deploy validation.
package org.eclipse.persistence.mappings.foundation;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import org.eclipse.persistence.exceptions.ConversionException;
import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.indirection.ValueHolderInterface;
import org.eclipse.persistence.internal.databaseaccess.DatabasePlatform;
import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition;
import org.eclipse.persistence.internal.descriptors.DescriptorIterator;
import org.eclipse.persistence.internal.descriptors.FieldTransformation;
import org.eclipse.persistence.internal.descriptors.InstanceVariableAttributeAccessor;
import org.eclipse.persistence.internal.descriptors.MethodAttributeAccessor;
import org.eclipse.persistence.internal.descriptors.MethodBasedFieldTransformation;
import org.eclipse.persistence.internal.descriptors.TransformerBasedFieldTransformation;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.identitymaps.CacheKey;
import org.eclipse.persistence.internal.indirection.BasicIndirectionPolicy;
import org.eclipse.persistence.internal.indirection.ContainerIndirectionPolicy;
import org.eclipse.persistence.internal.indirection.DatabaseValueHolder;
import org.eclipse.persistence.internal.indirection.IndirectionPolicy;
import org.eclipse.persistence.internal.indirection.NoIndirectionPolicy;
import org.eclipse.persistence.internal.queries.JoinedAttributeManager;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.security.PrivilegedClassForName;
import org.eclipse.persistence.internal.security.PrivilegedNewInstanceFromClass;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.internal.sessions.ChangeRecord;
import org.eclipse.persistence.internal.sessions.MergeManager;
import org.eclipse.persistence.internal.sessions.ObjectChangeSet;
import org.eclipse.persistence.internal.sessions.TransformationMappingChangeRecord;
import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl;
import org.eclipse.persistence.internal.sessions.remote.RemoteValueHolder;
import org.eclipse.persistence.mappings.Association;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.transformers.AttributeTransformer;
import org.eclipse.persistence.mappings.transformers.FieldTransformer;
import org.eclipse.persistence.mappings.transformers.MethodBasedAttributeTransformer;
import org.eclipse.persistence.mappings.transformers.MethodBasedFieldTransformer;
import org.eclipse.persistence.queries.ObjectBuildingQuery;
import org.eclipse.persistence.queries.ObjectLevelReadQuery;
import org.eclipse.persistence.queries.ReadObjectQuery;
import org.eclipse.persistence.queries.WriteObjectQuery;
import org.eclipse.persistence.sessions.CopyGroup;
import org.eclipse.persistence.sessions.DatabaseRecord;
import org.eclipse.persistence.sessions.Project;
import org.eclipse.persistence.sessions.remote.DistributedSession;
/**
* <p><b>Purpose</b>: A transformation mapping is used for a specialized translation between how
* a value is represented in Java and its representation on the databae. Transformation mappings
* should only be used when other mappings are inadequate.
*
* @author Sati
* @since TOPLink/Java 1.0
*/
public abstract class AbstractTransformationMapping extends DatabaseMapping {
/** Name of the class which implements AttributeTransformer to be used to retrieve the attribute value */
protected String attributeTransformerClassName;
/** attributeTransformerClassName is converter to an instance of AttributeTransformer */
protected AttributeTransformer attributeTransformer;
/** Stores field name and the class name of a FieldTransformer in a vector to preserve order */
protected List<FieldTransformation> fieldTransformations;
/** The TransformerClassNames are converted into instances of FieldTransformer */
protected List<Object[]> fieldToTransformers;
/** PERF: Indicates if this mapping's attribute is a simple value which cannot be modified only replaced. */
protected boolean isMutable;
/** Implements indirection behaviour */
protected IndirectionPolicy indirectionPolicy;
/**
* PUBLIC:
* Default constructor.
*/
protected AbstractTransformationMapping() {
fieldTransformations = new ArrayList();
fieldToTransformers = new ArrayList();
setIsMutable(true);
dontUseIndirection();
this.setWeight(WEIGHT_TRANSFORM);
}
/**
* PUBLIC:
* Add the field and the name of the method
* that returns the value to be placed in said field
* when the object is written to the database.
* The method may take zero arguments, or it may
* take a single argument of type
* <code>org.eclipse.persistence.sessions.Session</code>.
*/
public void addFieldTransformation(DatabaseField field, String methodName) {
MethodBasedFieldTransformation transformation = new MethodBasedFieldTransformation();
transformation.setField(field);
transformation.setMethodName(methodName);
getFieldTransformations().add(transformation);
}
/**
* PUBLIC:
* Add the name of field and the name of the method
* that returns the value to be placed in said field
* when the object is written to the database.
* The method may take zero arguments, or it may
* take a single argument of type
* <code>org.eclipse.persistence.sessions.Session</code>.
*/
public void addFieldTransformation(String fieldName, String methodName) {
addFieldTransformation(new DatabaseField(fieldName), methodName);
}
/**
* INTERNAL:
* Add the name of a field and the name of a class which implements
* the FieldTransformer interface. When the object is written, the transform
* method will be called on the FieldTransformer to acquire the value to put
* in the field.
*/
public void addFieldTransformerClassName(String fieldName, String className) {
addFieldTransformerClassName(new DatabaseField(fieldName), className);
}
/**
* INTERNAL:
* Add the name of a field and the name of a class which implements
* the FieldTransformer interface. When the object is written, the transform
* method will be called on the FieldTransformer to acquire the value to put
* in the field.
*/
public void addFieldTransformerClassName(DatabaseField field, String className) {
TransformerBasedFieldTransformation transformation = new TransformerBasedFieldTransformation();
transformation.setField(field);
transformation.setTransformerClassName(className);
getFieldTransformations().add(transformation);
}
/**
* PUBLIC:
* Add the name of field and the transformer
* that returns the value to be placed in the field
* when the object is written to the database.
*/
public void addFieldTransformer(String fieldName, FieldTransformer transformer) {
this.addFieldTransformer(new DatabaseField(fieldName), transformer);
}
/**
* PUBLIC:
* Add the field and the transformer
* that returns the value to be placed in the field
* when the object is written to the database.
*/
public void addFieldTransformer(DatabaseField field, FieldTransformer transformer) {
TransformerBasedFieldTransformation transformation = new TransformerBasedFieldTransformation(transformer);
transformation.setField(field);
getFieldTransformations().add(transformation);
}
/**
* INTERNAL:
* The referenced object is checked if it is instantiated or not
*/
protected boolean areObjectsToBeProcessedInstantiated(Object object) {
return this.indirectionPolicy.objectIsInstantiated(getAttributeValueFromObject(object));
}
/**
* INTERNAL:
* Clone the attribute from the clone and assign it to the backup.
*/
@Override
public void buildBackupClone(Object clone, Object backup, UnitOfWorkImpl unitOfWork) {
// If mapping is a no-attribute transformation mapping, do nothing
if (isWriteOnly()) {
return;
}
Object attributeValue = getAttributeValueFromObject(clone);
Object clonedAttributeValue = this.indirectionPolicy.backupCloneAttribute(attributeValue, clone, backup, unitOfWork);
setAttributeValueInObject(backup, clonedAttributeValue);
}
/**
* INTERNAL
* Build a phantom row that contains only the fields
* for the mapping, populated with the values generated by
* invoking the field methods on the specified object.
*/
protected AbstractRecord buildPhantomRowFrom(Object domainObject, AbstractSession session) {
AbstractRecord row = new DatabaseRecord(this.fieldToTransformers.size());
for (Object[] pair : this.fieldToTransformers) {
DatabaseField field = (DatabaseField)pair[0];
FieldTransformer transformer = (FieldTransformer)pair[1];
Object fieldValue = this.invokeFieldTransformer(field, transformer, domainObject, session);
row.put(field, fieldValue);
}
return row;
}
/**
* INTERNAL:
* Builds a shallow original object. Only direct attributes and primary
* keys are populated. In this way the minimum original required for
* instantiating a working copy clone can be built without placing it in
* the shared cache (no concern over cycles).
* @param original later the input to buildCloneFromRow
*/
@Override
public void buildShallowOriginalFromRow(AbstractRecord record, Object original, JoinedAttributeManager joinManager, ObjectBuildingQuery query, AbstractSession executionSession) {
// In this case we know it is a primary key mapping, so hope that it
// is essentially a direct mapping. If it is a 1-1 with a
// no-indirection pointer back to original, then will get a stack
// overflow.
// Only solution to this is to trigger the transformation using the root
// session.
UnitOfWorkImpl unitOfWork = (UnitOfWorkImpl)query.getSession();
query.setSession(unitOfWork.getParent());
try {
readFromRowIntoObject(record, joinManager, original, null, query, executionSession, false);
} finally {
query.setSession(unitOfWork);
}
}
/**
* INTERNAL:
* Used during building the backup shallow copy to copy the vector without re-registering the target objects.
* For 1-1 or ref the reference is from the clone so it is already registered.
*/
@Override
public Object buildBackupCloneForPartObject(Object attributeValue, Object clone, Object backup, UnitOfWorkImpl unitOfWork) {
return buildCloneForPartObject(attributeValue, clone, null, backup, unitOfWork, null, true, true);
}
/**
* INTERNAL:
* Clone the attribute from the original and assign it to the clone.
*/
@Override
public void buildClone(Object original, CacheKey cacheKey, Object clone, Integer refreshCascade, AbstractSession cloningSession) {
// If mapping is a no-attribute transformation mapping, do nothing
if (isWriteOnly()) {
return;
}
Object attributeValue = getAttributeValueFromObject(original);
Object clonedAttributeValue = this.indirectionPolicy.cloneAttribute(attributeValue, original, cacheKey, clone, refreshCascade, cloningSession, false);// building clone from an original not a row.
setAttributeValueInObject(clone, clonedAttributeValue);
}
/**
* INTERNAL:
* Extract value from the row and set the attribute to this value in the
* working copy clone.
* In order to bypass the shared cache when in transaction a UnitOfWork must
* be able to populate working copies directly from the row.
*/
@Override
public void buildCloneFromRow(AbstractRecord record, JoinedAttributeManager joinManager, Object clone, CacheKey sharedCacheKey, ObjectBuildingQuery sourceQuery, UnitOfWorkImpl unitOfWork, AbstractSession executionSession) {
// If mapping is a no-attribute transformation mapping, do nothing
if (isWriteOnly()) {
return;
}
// This will set the value in the clone automatically.
Object attributeValue = readFromRowIntoObject(record, joinManager, clone, sharedCacheKey, sourceQuery, executionSession, true);
if (usesIndirection()) {
boolean wasCacheUsed = this.isCacheable && sharedCacheKey != null && this.descriptor.getCachePolicy().isProtectedIsolation()
&& sharedCacheKey.getObject() != null;
//it would be better if wasCacheUsed could be calculated within readFromRowIntoObject but that would require changing the signature of all mappings just for
//transformation mapping.
if (!wasCacheUsed){
//if the cache was used then the attribute has already been cloned by readFromRowIntoObject
attributeValue = this.indirectionPolicy.cloneAttribute(attributeValue, null,// no original
null, clone, null, unitOfWork, true);// build clone directly from row.
}
setAttributeValueInObject(clone, attributeValue);
}
}
/**
* INTERNAL:
* Require for cloning, the part must be cloned.
* Ignore the attribute value, go right to the object itself.
*/
@Override
public Object buildCloneForPartObject(Object attributeValue, Object original, CacheKey cacheKey, Object clone, AbstractSession cloningSession, Integer refreshCascade, boolean isExisting, boolean isFromSharedCache) {
if (isReadOnly() || !isMutable()) {
return attributeValue;
}
AbstractRecord row = buildPhantomRowFrom(original, cloningSession);
return invokeAttributeTransformer(row, clone, cloningSession);
}
/**
* INTERNAL:
* Copy of the attribute of the object.
* This is NOT used for unit of work but for templatizing an object.
*/
@Override
public void buildCopy(Object copy, Object original, CopyGroup group) {
// If mapping is a no-attribute transformation mapping, do nothing
if (isWriteOnly()) {
return;
}
Object clonedAttributeValue;
// If the mapping is read-only, a direct pass through of the value will be performed.
// This is done because the method invocation is not possible as the row will be
// empty and we have no way to clone the value.
// Since the value cannot change anyway we just pass it through.
if (isReadOnly() || !isMutable()) {
clonedAttributeValue = getRealAttributeValueFromObject(original, group.getSession());
} else {
AbstractRecord row = buildPhantomRowFrom(original, group.getSession());
clonedAttributeValue = invokeAttributeTransformer(row, copy, group.getSession());
}
this.indirectionPolicy.reset(copy);
setRealAttributeValueInObject(copy, clonedAttributeValue);
}
/**
* INTERNAL:
* Cascade perform delete through mappings that require the cascade
*/
@Override
public void cascadePerformRemoveIfRequired(Object object, UnitOfWorkImpl uow, Map visitedObjects){
//objects referenced by this mapping are not registered as they have
// no identity, this is a no-op.
}
/**
* INTERNAL:
* Cascade registerNew for Create through mappings that require the cascade
*/
@Override
public void cascadeRegisterNewIfRequired(Object object, UnitOfWorkImpl uow, Map visitedObjects){
//Objects referenced through transformation mappings are not registered as
// they have no identity, this is a no-op.
}
/**
* INTERNAL:
* The mapping clones itself to create deep copy.
*/
@Override
public Object clone() {
AbstractTransformationMapping clone = (AbstractTransformationMapping)super.clone();
clone.setFieldToTransformers(new ArrayList(this.fieldToTransformers.size()));
for (Object[] pair : this.fieldToTransformers) {
Object[] transformation = new Object[2];
transformation[0] = pair[0];
transformation[1] = pair[1];
clone.getFieldToTransformers().add(transformation);
}
clone.setIndirectionPolicy((IndirectionPolicy)indirectionPolicy.clone());
return clone;
}
/**
* INTERNAL:
* Return all the fields with this mapping.
*/
@Override
protected Vector collectFields() {
Vector databaseFields = new Vector(this.fieldToTransformers.size());
for (Object[] pair : this.fieldToTransformers) {
databaseFields.add(pair[0]);
}
return databaseFields;
}
/**
* INTERNAL:
* Compare the attributes belonging to this mapping for the objects.
*/
@Override
public ChangeRecord compareForChange(Object clone, Object backUp, ObjectChangeSet owner, AbstractSession session) {
if (isReadOnly() || isWriteOnly()) {
return null;
}
Object cloneAttribute = getAttributeValueFromObject(clone);
Object backUpAttribute = null;
if ((cloneAttribute != null) && (!this.indirectionPolicy.objectIsInstantiated(cloneAttribute))) {
return null;
}
boolean difference = false;
Object backupValue = null;
if (owner.isNew()) {
difference = true;
} else {
if (backUp != null) {
backUpAttribute = getAttributeValueFromObject(backUp);
backupValue = this.indirectionPolicy.getRealAttributeValueFromObject(backUp, backUpAttribute);
}
boolean backUpIsInstantiated = ((backUpAttribute == null) || (this.indirectionPolicy.objectIsInstantiated(backUpAttribute)));
Object cloneValue = this.indirectionPolicy.getRealAttributeValueFromObject(clone, cloneAttribute);
if (backUpIsInstantiated) {
if (cloneValue == backupValue) {
return null;
}
if (((cloneValue != null && (backupValue != null)) && cloneValue.equals(backupValue))) {
return null;
}
}
for (Object[] pair : this.fieldToTransformers) {
DatabaseField field = (DatabaseField)pair[0];
FieldTransformer transformer = (FieldTransformer)pair[1];
Object cloneFieldValue = null;
Object backUpFieldValue = null;
if (clone != null) {
cloneFieldValue = invokeFieldTransformer(field, transformer, clone, session);
}
if ((backUpIsInstantiated) && (backUp != null)) {
backUpFieldValue = invokeFieldTransformer(field, transformer, backUp, session);
}
if (cloneFieldValue == backUpFieldValue) {
continue; // skip this iteration, go to the next one
}
if ((cloneFieldValue == null) || (backUpFieldValue == null)) {
difference = true;
break; // There is a difference.
}
if (cloneFieldValue.equals(backUpFieldValue)) {
continue; // skip this iteration, go to the next one
}
if (Helper.comparePotentialArrays(cloneFieldValue, backUpFieldValue)) {
continue; // skip this iteration, go to the next one
}
difference = true;
break; // There is a difference.
}
}
if (difference) {
return internalBuildChangeRecord(clone, backupValue, owner, session);
}
return null;
}
/**
* INTERNAL:
* Directly build a change record without comparison
*/
@Override
public ChangeRecord buildChangeRecord(Object clone, ObjectChangeSet owner, AbstractSession session) {
return internalBuildChangeRecord(clone, null, owner, session);
}
/**
* INTERNAL:
* Build a change record.
*/
public ChangeRecord internalBuildChangeRecord(Object clone, Object oldValue, ObjectChangeSet owner, AbstractSession session) {
TransformationMappingChangeRecord changeRecord = new TransformationMappingChangeRecord(owner);
changeRecord.setRow(buildPhantomRowFrom(clone, session));
changeRecord.setAttribute(getAttributeName());
changeRecord.setMapping(this);
changeRecord.setOldValue(oldValue);
return changeRecord;
}
/**
* INTERNAL:
* Compare the attributes belonging to this mapping for the objects.
*/
@Override
public boolean compareObjects(Object firstObject, Object secondObject, AbstractSession session) {
if (!isWriteOnly()) {
// PERF: Checks if attribute values are equal first before apply field translation.
Object firstValue = getRealAttributeValueFromObject(firstObject, session);
Object secondValue = getRealAttributeValueFromObject(secondObject, session);
if (firstValue == secondValue) {
return true;
}
if ((firstValue == null) || (secondValue == null)) {
return false;
}
if (firstValue.equals(secondValue)) {
return true;
}
}
for (Object[] pair : this.fieldToTransformers) {
DatabaseField field = (DatabaseField)pair[0];
FieldTransformer transformer = (FieldTransformer)pair[1];
Object firstFieldValue = invokeFieldTransformer(field, transformer, firstObject, session);
Object secondFieldValue = invokeFieldTransformer(field, transformer, secondObject, session);
if (firstFieldValue == secondFieldValue) {
continue; // skip this iteration, go to the next one
}
if ((firstFieldValue == null) || (secondFieldValue == null)) {
return false;
}
if (!firstFieldValue.equals(secondFieldValue)) {
if (!Helper.comparePotentialArrays(firstFieldValue, secondFieldValue)) {
return false;
}
}
}
return true;
}
/**
* INTERNAL:
* Convert all the class-name-based settings in this mapping to actual class-based
* settings
*/
@Override
public void convertClassNamesToClasses(ClassLoader classLoader){
super.convertClassNamesToClasses(classLoader);
if (attributeTransformerClassName != null) {
Class attributeTransformerClass = null;
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
try {
attributeTransformerClass = AccessController.doPrivileged(new PrivilegedClassForName(attributeTransformerClassName, true, classLoader));
} catch (PrivilegedActionException exception) {
throw ValidationException.classNotFoundWhileConvertingClassNames(attributeTransformerClassName, exception.getException());
}
} else {
attributeTransformerClass = PrivilegedAccessHelper.getClassForName(attributeTransformerClassName, true, classLoader);
}
} catch (ClassNotFoundException exc){
throw ValidationException.classNotFoundWhileConvertingClassNames(attributeTransformerClassName, exc);
}
this.setAttributeTransformerClass(attributeTransformerClass);
}
for (FieldTransformation transformation : getFieldTransformations()) {
if (transformation instanceof TransformerBasedFieldTransformation) {
TransformerBasedFieldTransformation transformer = (TransformerBasedFieldTransformation)transformation;
String transformerClassName = transformer.getTransformerClassName();
if (transformerClassName == null) {
return;
}
Class transformerClass = null;
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
try {
transformerClass = AccessController.doPrivileged(new PrivilegedClassForName(transformerClassName, true, classLoader));
} catch (PrivilegedActionException exception) {
throw ValidationException.classNotFoundWhileConvertingClassNames(transformerClassName, exception.getException());
}
} else {
transformerClass = PrivilegedAccessHelper.getClassForName(transformerClassName, true, classLoader);
}
} catch (ClassNotFoundException exc){
throw ValidationException.classNotFoundWhileConvertingClassNames(transformerClassName, exc);
}
transformer.setTransformerClass(transformerClass);
}
}
}
/**
* INTERNAL:
* Builder the unit of work value holder.
* Ignore the original object.
* @param buildDirectlyFromRow indicates that we are building the clone directly
* from a row as opposed to building the original from the row, putting it in
* the shared cache, and then cloning the original.
*/
@Override
public DatabaseValueHolder createCloneValueHolder(ValueHolderInterface attributeValue, Object original, Object clone, AbstractRecord row, AbstractSession cloningSession, boolean buildDirectlyFromRow) {
return cloningSession.createCloneTransformationValueHolder(attributeValue, original, clone, this);
}
/**
* PUBLIC:
* Indirection means that a ValueHolder will be put in-between the attribute and the real object.
* This defaults to false and only required for transformations that perform database access.
*/
public void dontUseIndirection() {
setIndirectionPolicy(new NoIndirectionPolicy());
}
/**
* INTERNAL:
* An object has been serialized from the server to the client.
* Replace the transient attributes of the remote value holders
* with client-side objects.
*/
@Override
public void fixObjectReferences(Object object, Map objectDescriptors, Map processedObjects, ObjectLevelReadQuery query, DistributedSession session) {
this.indirectionPolicy.fixObjectReferences(object, objectDescriptors, processedObjects, query, session);
}
/**
* INTERNAL:
* The attributeTransformer stores an instance of the class which implements
* AttributeTransformer.
*/
public AttributeTransformer getAttributeTransformer() {
return attributeTransformer;
}
/**
* PUBLIC:
* Return the attribute transformation method name.
*/
public String getAttributeMethodName() {
if (this.attributeTransformer instanceof MethodBasedAttributeTransformer) {
return ((MethodBasedAttributeTransformer)this.attributeTransformer).getMethodName();
}
return null;
}
/**
* INTERNAL:
* Return the attribute transformer's class.
* This is used to map to XML.
*/
public Class getAttributeTransformerClass() {
if ((this.attributeTransformer == null) || (this.attributeTransformer instanceof MethodBasedAttributeTransformer)) {
return null;
}
return this.attributeTransformer.getClass();
}
/**
* INTERNAL:
* Set the attribute transformer's class.
* This is used to map from XML.
*/
public void setAttributeTransformerClass(Class attributeTransformerClass) {
if (attributeTransformerClass == null) {
return;
}
try {
Object instance = null;
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
try {
instance = AccessController.doPrivileged(new PrivilegedNewInstanceFromClass(attributeTransformerClass));
} catch (PrivilegedActionException ex) {
throw (Exception)ex.getCause();
}
} else {
instance = PrivilegedAccessHelper.newInstanceFromClass(attributeTransformerClass);
}
setAttributeTransformer((AttributeTransformer)instance);
} catch (Exception exception) {
throw DescriptorException.attributeTransformerClassInvalid(attributeTransformerClass.getName(), this, exception);
}
}
/**
* INTERNAL:
* Return the attribute transformer class name
*/
public String getAttributeTransformerClassName() {
return attributeTransformerClassName;
}
/**
* INTERNAL:
* Check for write-only, one-way transformation.
*/
@Override
public Object getAttributeValueFromObject(Object object) throws DescriptorException {
if (isWriteOnly()) {
return null;
}
Object attributeValue = super.getAttributeValueFromObject(object);
return this.indirectionPolicy.validateAttributeOfInstantiatedObject(attributeValue);
}
/**
* INTERNAL:
* Returns a Vector which stores fieldnames and the respective method/transformer names.
*/
public List<FieldTransformation> getFieldTransformations() {
return fieldTransformations;
}
/**
* INTERNAL:
* @return a vector which stores fields and their respective transformers.
*/
public List<Object[]> getFieldToTransformers() {
return fieldToTransformers;
}
/**
* INTERNAL:
* Return the mapping's indirection policy.
*/
public IndirectionPolicy getIndirectionPolicy() {
return indirectionPolicy;
}
/**
* INTERNAL:
* Returns the real attribute value from the reference object's attribute value.
* If the attribute is using indirection the value of the value-holder is returned.
* If the value holder is not instantiated then it is instantiated.
*/
@Override
public Object getRealAttributeValueFromAttribute(Object attributeValue, Object object, AbstractSession session) {
return this.indirectionPolicy.getRealAttributeValueFromObject(object, attributeValue);
}
/**
* INTERNAL:
* Trigger the instantiation of the attribute if lazy.
*/
@Override
public void instantiateAttribute(Object object, AbstractSession session) {
this.indirectionPolicy.instantiateObject(object, getAttributeValueFromObject(object));
}
/**
* INTERNAL:
* Extract and return the appropriate value from the
* specified remote value holder.
*/
@Override
public Object getValueFromRemoteValueHolder(RemoteValueHolder remoteValueHolder) {
return this.indirectionPolicy.getValueFromRemoteValueHolder(remoteValueHolder);
}
/**
* INTERNAL:
* The mapping is initialized with the given session.
*/
@Override
public void initialize(AbstractSession session) throws DescriptorException {
super.initialize(session);
initializeAttributeTransformer(session);
initializeFieldToTransformers(session);
setFields(collectFields());
this.indirectionPolicy.initialize();
if (usesIndirection()) {
for (DatabaseField field : this.fields) {
field.setKeepInRow(true);
}
}
}
/**
* INTERNAL:
* Convert the attribute transformer class name into an AttributeTransformer
* If the old-style method name in set, then use a MethodBasedAttributeTRansformer
*/
protected void initializeAttributeTransformer(AbstractSession databaseSession) throws DescriptorException {
if (isWriteOnly()) {
return;
}
this.attributeTransformer.initialize(this);
}
/**
* INTERNAL:
* Required for reverse compatibility and test cases:
* @return a hash table containing the fieldName and their respective method names
*/
public Hashtable getFieldNameToMethodNames() {
Hashtable table = new Hashtable(getFieldTransformations().size());
Iterator transformations = getFieldTransformations().iterator();
while (transformations.hasNext()) {
FieldTransformation transformation = (FieldTransformation)transformations.next();
if (transformation instanceof MethodBasedFieldTransformation) {
table.put(transformation.getField().getQualifiedName(), ((MethodBasedFieldTransformation)transformation).getMethodName());
}
}
return table;
}
/**
* INTERNAL:
* Convert the field names and their corresponding method names to
* DatabaseFields and Methods.
*/
protected void initializeFieldToTransformers(AbstractSession session) throws DescriptorException {
for (Object[] pair : this.fieldToTransformers) {
pair[0] = getDescriptor().buildField(((DatabaseField)pair[0]));
((FieldTransformer)pair[1]).initialize(this);
}
for (FieldTransformation transformation : getFieldTransformations()) {
DatabaseField field = getDescriptor().buildField(transformation.getField());
String transformerClassName = "MethodBasedFieldTransformer";
FieldTransformer transformer = null;
try {
transformer = transformation.buildTransformer();
} catch (ConversionException ex) {
if (transformation instanceof TransformerBasedFieldTransformation) {
transformerClassName = ((TransformerBasedFieldTransformation)transformation).getTransformerClassName();
}
throw DescriptorException.fieldTransformerClassNotFound(transformerClassName, this, ex);
} catch (Exception ex) {
if (transformation instanceof TransformerBasedFieldTransformation) {
transformerClassName = ((TransformerBasedFieldTransformation)transformation).getTransformerClassName();
}
throw DescriptorException.fieldTransformerClassInvalid(transformerClassName, this, ex);
}
transformer.initialize(this);
// Attempt to ensure a type is set on the field.
if (field.getType() == null) {
if (transformer instanceof MethodBasedFieldTransformer) {
field.setType(((MethodBasedFieldTransformer)transformer).getFieldType());
} else if (field.getColumnDefinition() != null) {
// Search for the type for this field definition.
if (session.getDatasourcePlatform() instanceof DatabasePlatform) {
Iterator iterator = session.getPlatform().getFieldTypes().entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry entry = (Map.Entry)iterator.next();
if (((FieldTypeDefinition)entry.getValue()).getName().equals(field.getColumnDefinition())) {
field.setType((Class)entry.getKey());
break;
}
}
}
}
}
Object[] fieldToTransformer = new Object[2];
fieldToTransformer[0] = field;
fieldToTransformer[1] = transformer;
this.fieldToTransformers.add(fieldToTransformer);
}
}
/**
* INTERNAL:
* Invoke the buildAttributeValue method on the AttributeTransformer
*/
public Object invokeAttributeTransformer(AbstractRecord record, Object domainObject, AbstractSession session) throws DescriptorException {
return this.attributeTransformer.buildAttributeValue(record, domainObject, session);
}
/**
* INTERNAL:
* Invoke the buildFieldValue on the appropriate FieldTransformer
*/
protected Object invokeFieldTransformer(DatabaseField field, FieldTransformer transformer, Object domainObject, AbstractSession session) throws DescriptorException {
return transformer.buildFieldValue(domainObject, field.getName(), session);
}
protected Object invokeFieldTransformer(DatabaseField field, Object domainObject, AbstractSession session) {
for (Object[] pair : this.fieldToTransformers) {
if (field.equals(pair[0])) {
return invokeFieldTransformer(field, (FieldTransformer)pair[1], domainObject, session);
}
}
return null;
}
/**
* PUBLIC:
* Return true if the attribute for this mapping is not a simple atomic value that cannot be modified,
* only replaced.
* This is true by default for non-primitives, but can be set to false to avoid cloning
* and change comparison in the unit of work.
*/
public boolean isMutable() {
return isMutable;
}
/**
* INTERNAL:
* Return true if read-only is explicitly set to true;
* otherwise return whether the transformation has no fields
* (no fields = read-only)
*/
@Override
public boolean isReadOnly() {
if (super.isReadOnly()) {
return true;
} else {
return getFieldTransformations().isEmpty() && this.fieldToTransformers.isEmpty();
}
}
/**
* INTERNAL:
*/
@Override
public boolean isTransformationMapping() {
return true;
}
/**
* INTERNAL:
* Return if the transformation has no attribute, is write only.
*/
@Override
public boolean isWriteOnly() {
return (getAttributeName() == null) && ((this.attributeTransformer == null) && (this.attributeTransformerClassName == null));
}
/**
* INTERNAL:
* Perform the iteration opperation on the iterators current objects attributes.
* Only require if primitives are desired.
*/
@Override
public void iterate(DescriptorIterator iterator) {
Object attributeValue = getAttributeValueFromObject(iterator.getVisitedParent());
this.indirectionPolicy.iterateOnAttributeValue(iterator, attributeValue);
}
/**
* INTERNAL:
* Iterate on the attribute value.
* The value holder has already been processed.
*/
@Override
public void iterateOnRealAttributeValue(DescriptorIterator iterator, Object realAttributeValue) {
iterator.iteratePrimitiveForMapping(realAttributeValue, this);
}
/**
* INTERNAL:
* Merge changes from the source to the target object. Which is the original from the parent UnitOfWork
*/
@Override
public void mergeChangesIntoObject(Object target, ChangeRecord changeRecord, Object source, MergeManager mergeManager, AbstractSession targetSession) {
if (isWriteOnly()) {
return;
}
// PERF: If not mutable then just set the value from the source.
if (!isMutable() && (source != null)) {
setRealAttributeValueInObject(target, getRealAttributeValueFromObject(source, mergeManager.getSession()));
return;
}
AbstractRecord record = (AbstractRecord)((TransformationMappingChangeRecord)changeRecord).getRecord();
Object attributeValue = invokeAttributeTransformer(record, target, targetSession);
setRealAttributeValueInObject(target, attributeValue);
}
/**
* INTERNAL:
* Merge changes from the source to the target object.
*/
@Override
public void mergeIntoObject(Object target, boolean isTargetUnInitialized, Object source, MergeManager mergeManager, AbstractSession targetSession) {
if (isWriteOnly()) {
return;
}
// do refresh check first as I may need to reset remote value holder
if (mergeManager.shouldRefreshRemoteObject() && usesIndirection()) {
this.indirectionPolicy.mergeRemoteValueHolder(target, source, mergeManager);
return;
}
if (mergeManager.isForRefresh()) {
if (!areObjectsToBeProcessedInstantiated(target)) {
// This will occur when the clone's value has not been instantiated yet and we do not need
// the refresh that attribute
return;
}
} else if (!areObjectsToBeProcessedInstantiated(source)) {
// I am merging from a clone into an original. No need to do merge if the attribute was never
// modified
return;
}
if (isTargetUnInitialized) {
// This will happen if the target object was removed from the cache before the commit was attempted
if (mergeManager.shouldMergeWorkingCopyIntoOriginal() && (!areObjectsToBeProcessedInstantiated(source))) {
setAttributeValueInObject(target, this.indirectionPolicy.getOriginalIndirectionObject(getAttributeValueFromObject(source), targetSession));
return;
}
}
if (isReadOnly()) {
// if it is read only then we do not have any fields specified for the
// transformer, without fields we can not build the row, so just copy
// over the value alternatively we could build the entire row for the object.
setRealAttributeValueInObject(target, getRealAttributeValueFromObject(source, mergeManager.getSession()));
return;
}
if (!isMutable()) {
Object attribute = getRealAttributeValueFromObject(source, mergeManager.getSession());
if (this.descriptor.getObjectChangePolicy().isObjectChangeTrackingPolicy()) {
// Object level or attribute level so lets see if we need to raise the event?
Object targetAttribute = getRealAttributeValueFromObject(target, mergeManager.getSession());
if ((mergeManager.shouldMergeCloneIntoWorkingCopy() || mergeManager.shouldMergeCloneWithReferencesIntoWorkingCopy()) && !mergeManager.isForRefresh()
&& (((targetAttribute == null) && (attribute != null)) || ((targetAttribute != null) && ((attribute == null) || ((!targetAttribute.equals(attribute)) && (!Helper.comparePotentialArrays(targetAttribute, attribute))))))) {
this.descriptor.getObjectChangePolicy().raiseInternalPropertyChangeEvent(target, getAttributeName(), targetAttribute, attribute);
}
}
setRealAttributeValueInObject(target, attribute);
return;
}
// This dumps the attribute into the row and back.
AbstractRecord row = buildPhantomRowFrom(source, mergeManager.getSession());
Object attributeValue = invokeAttributeTransformer(row, source, mergeManager.getSession());
AbstractRecord targetRow = buildPhantomRowFrom(target, mergeManager.getSession());
setRealAttributeValueInObject(target, attributeValue);
//set the change after the set on the object as this mapping uses the object to build the change record.
if (this.descriptor.getObjectChangePolicy().isObjectChangeTrackingPolicy()) {
for (Enumeration keys = targetRow.keys(); keys.hasMoreElements(); ){
Object field = keys.nextElement();
if ((mergeManager.shouldMergeCloneIntoWorkingCopy() || mergeManager.shouldMergeCloneWithReferencesIntoWorkingCopy())
&& (!row.get(field).equals(targetRow.get(field)))) {
this.descriptor.getObjectChangePolicy().raiseInternalPropertyChangeEvent(target, getAttributeName(), invokeAttributeTransformer(targetRow, source, mergeManager.getSession()), attributeValue);
break;
}
}
}
}
/**
* INTERNAL:
* Allow for initialization of properties and validation.
*/
@Override
public void preInitialize(AbstractSession session) throws DescriptorException {
if (isWriteOnly()) {
return;// Allow for one-way transformations.
}
super.preInitialize(session);
// PERF: Also auto-set mutable to false is the attribute type is a primitive.
// This means it is not necessary to clone the value (through double transformation).
if ((getAttributeClassification() != null) && (getAttributeClassification().isPrimitive() || Helper.isPrimitiveWrapper(getAttributeClassification()) || getAttributeClassification().equals(ClassConstants.STRING) || getAttributeClassification().equals(ClassConstants.BIGDECIMAL) || getAttributeClassification().equals(ClassConstants.NUMBER))) {
setIsMutable(false);
}
}
/**
* INTERNAL:
* Extracts value from return row and set the attribute to the value in the object.
* Return row is merged into object after execution of insert or update call
* according to ReturningPolicy.
*/
public Object readFromReturnRowIntoObject(AbstractRecord row, Object object, ReadObjectQuery query, Collection handledMappings, ObjectChangeSet changeSet) throws DatabaseException {
int size = this.fields.size();
AbstractRecord transformationRow = new DatabaseRecord(size);
for (int i = 0; i < size; i++) {
DatabaseField field = this.fields.get(i);
Object value;
if (row.containsKey(field)) {
value = row.get(field);
} else {
value = valueFromObject(object, field, query.getSession());
}
transformationRow.add(field, value);
}
if(changeSet != null && (!changeSet.isNew() || (query.getDescriptor() != null && query.getDescriptor().shouldUseFullChangeSetsForNewObjects()))) {
TransformationMappingChangeRecord record = (TransformationMappingChangeRecord)changeSet.getChangesForAttributeNamed(attributeName);
if (record == null) {
record = new TransformationMappingChangeRecord(changeSet);
record.setAttribute(attributeName);
record.setMapping(this);
record.setOldValue(getAttributeValueFromObject(object));
changeSet.addChange(record);
}
record.setRow(transformationRow);
}
Object attributeValue = readFromRowIntoObject(transformationRow, null, object, null, query, query.getSession(), true);
if (handledMappings != null) {
handledMappings.add(this);
}
return attributeValue;
}
/**
* INTERNAL:
* Extract value from the row and set the attribute to the value in the object.
*/
@Override
public Object readFromRowIntoObject(AbstractRecord row, JoinedAttributeManager joinManager, Object object, CacheKey parentCacheKey, ObjectBuildingQuery query, AbstractSession executionSession, boolean isTargetProtected) throws DatabaseException {
if (isWriteOnly()) {
return null;
}
if (this.descriptor.getCachePolicy().isProtectedIsolation()) {
if (this.isCacheable && isTargetProtected && parentCacheKey != null) {
Object cached = parentCacheKey.getObject();
if (cached != null) {
Object attributeValue = getAttributeValueFromObject(cached);
Integer refreshCascade = null;
if (query != null && query.shouldRefreshIdentityMapResult()){
refreshCascade = query.getCascadePolicy();
}
return this.indirectionPolicy.cloneAttribute(attributeValue, cached, parentCacheKey, object, refreshCascade, executionSession, false);
}
return null;
}
}
if (row != null && row.hasSopObject()) {
return getAttributeValueFromObject(row.getSopObject());
}
Object attributeValue = this.indirectionPolicy.valueFromMethod(object, row, query.getSession());
Object oldAttribute = null;
if (executionSession.isUnitOfWork() && query.shouldRefreshIdentityMapResult()){
oldAttribute = this.getAttributeValueFromObject(object);
}
try {
this.attributeAccessor.setAttributeValueInObject(object, attributeValue);
} catch (DescriptorException exception) {
exception.setMapping(this);
throw exception;
}
if (executionSession.isUnitOfWork() && query.shouldRefreshIdentityMapResult()){
if (this.indirectionPolicy.objectIsInstantiatedOrChanged(oldAttribute)){
this.indirectionPolicy.instantiateObject(object, attributeValue);
}
}
return attributeValue;
}
/**
* INTERNAL:
* Needed for backwards compatibility
*/
public Vector getFieldNameToMethodNameAssociations() {
Vector associations = new Vector();
for (Iterator source = getFieldTransformations().iterator(); source.hasNext();) {
FieldTransformation tf = (FieldTransformation)source.next();
if (tf instanceof MethodBasedFieldTransformation) {
Association ass = new Association();
ass.setKey(tf.getField().getQualifiedName());
ass.setValue(((MethodBasedFieldTransformation)tf).getMethodName());
associations.addElement(ass);
}
}
return associations;
}
/**
* INTERNAL:
* needed for backwards compatibility
*/
public void setFieldNameToMethodNameAssociations(Vector associations) {
setFieldTransformations(org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance(associations.size()));
for (Iterator source = associations.iterator(); source.hasNext();) {
Association ass = (Association)source.next();
MethodBasedFieldTransformation tf = new MethodBasedFieldTransformation();
tf.setField(new DatabaseField((String)ass.getKey()));
tf.setMethodName((String)ass.getValue());
getFieldTransformations().add(tf);
}
}
/**
* INTERNAL:
* Once descriptors are serialized to the remote session. All its mappings and reference descriptors are traversed. Usually
* mappings are initialized and serialized reference descriptors are replaced with local descriptors if they already exist on the
* remote session.
*/
@Override
public void remoteInitialization(DistributedSession session) {
setFieldToTransformers(new Vector());
// Remote mappings is initialized here again because while serializing only the uninitialized data is passed
// as the initialized data is not serializable.
if (!isWriteOnly()) {
super.remoteInitialization(session);
initializeAttributeTransformer(session);
}
initializeFieldToTransformers(session);
}
/**
* PUBLIC:
* Set the AttributeTransformer, this transformer will be used to extract the value for the
* object's attribute from the database row.
*/
public void setAttributeTransformer(AttributeTransformer transformer) {
attributeTransformer = transformer;
if ((transformer != null) && !(transformer instanceof MethodBasedAttributeTransformer)) {
attributeTransformerClassName = transformer.getClass().getName();
}
}
/**
* INTERNAL:
* Set the Attribute Transformer Class Name
*/
public void setAttributeTransformerClassName(String className) {
attributeTransformerClassName = className;
}
/**
* PUBLIC:
* To set the attribute method name. The method is invoked internally by TopLink
* to retrieve the value to store in the domain object. The method receives Record
* as its parameter and optionally Session, and should extract the value from the
* record to set into the object, but should not set the value on the object, only return it.
*/
public void setAttributeTransformation(String methodName) {
if ((methodName != null) && (!methodName.isEmpty())) {
setAttributeTransformer(new MethodBasedAttributeTransformer(methodName));
} else {
setAttributeTransformer(null);
}
}
/**
* INTERNAL:
* Check for write-only, one-way transformations.
*/
@Override
public void setAttributeValueInObject(Object object, Object value) {
if (isWriteOnly()) {
return;
}
super.setAttributeValueInObject(object, value);
}
/**
* PUBLIC:
* Set if the value of the attribute is atomic or a complex mutable object and can be modified without replacing the entire object.
* This defaults to true for non-primitives, but can be set to false to optimize object cloning and change comparison.
*/
public void setIsMutable(boolean mutable) {
this.isMutable = mutable;
}
/**
* INTERNAL:
* Set the value of the attribute mapped by this mapping,
* placing it inside a value holder if necessary.
* If the value holder is not instantiated then it is instantiated.
* Check for write-only, one-way transformations.
*/
@Override
public void setRealAttributeValueInObject(Object object, Object value) throws DescriptorException {
if (isWriteOnly()) {
return;
}
this.indirectionPolicy.setRealAttributeValueInObject(object, value);
}
/**
* INTERNAL:
* Set the field to method name associations.
*/
public void setFieldTransformations(List<FieldTransformation> fieldTransformations) {
this.fieldTransformations = fieldTransformations;
}
protected void setFieldToTransformers(List<Object[]> fieldToTransformers) {
this.fieldToTransformers = fieldToTransformers;
}
/**
* ADVANCED:
* Set the indirection policy.
*/
public void setIndirectionPolicy(IndirectionPolicy indirectionPolicy) {
this.indirectionPolicy = indirectionPolicy;
indirectionPolicy.setMapping(this);
}
/**
* INTERNAL:
* Will be used by Gromit. For details see usesIndirection().
* @see #useBasicIndirection()
* @see #dontUseIndirection()
*/
public void setUsesIndirection(boolean usesIndirection) {
if (usesIndirection) {
useBasicIndirection();
} else {
dontUseIndirection();
}
}
/**
* INTERNAL:
* Either create a new change record or update the change record with the new value.
* This is used by attribute change tracking.
*/
@Override
public void updateChangeRecord(Object clone, Object newValue, Object oldValue, ObjectChangeSet objectChangeSet, UnitOfWorkImpl uow) {
TransformationMappingChangeRecord changeRecord = (TransformationMappingChangeRecord)objectChangeSet.getChangesForAttributeNamed(this.getAttributeName());
Object updatedObject = descriptor.getInstantiationPolicy().buildNewInstance();
this.setAttributeValueInObject(updatedObject, newValue);
if (!isWriteOnly()) {
if (changeRecord == null) {
objectChangeSet.addChange(internalBuildChangeRecord(updatedObject, oldValue, objectChangeSet, uow));
} else {
changeRecord.setRow(this.buildPhantomRowFrom(updatedObject, uow));
}
}
}
/**
* INTERNAL:
* Return if this mapping supports change tracking.
*/
@Override
public boolean isChangeTrackingSupported(Project project) {
return ! isMutable();
}
/**
* INTERNAL:
* Return whether the specified object is instantiated.
*/
@Override
public boolean isAttributeValueFromObjectInstantiated(Object object) {
return this.indirectionPolicy.objectIsInstantiated(object);
}
/**
* PUBLIC:
* Indirection means that a ValueHolder will be put in-between the attribute and the real object.
* This defaults to false and only required for transformations that perform database access.
*/
public void useBasicIndirection() {
setIndirectionPolicy(new BasicIndirectionPolicy());
}
/**
* PUBLIC:
* Indirection means that a IndirectContainer (wrapping a ValueHolder) will be put in-between
* the attribute and the real object.
* This allows for the reading of the target from the database to be delayed until accessed.
* This defaults to true and is strongly suggested as it give a huge performance gain.
*/
public void useContainerIndirection(Class containerClass) {
ContainerIndirectionPolicy policy = new ContainerIndirectionPolicy();
policy.setContainerClass(containerClass);
setIndirectionPolicy(policy);
}
/**
* PUBLIC:
* Indirection means that a ValueHolder will be put in-between the attribute and the real object.
* This defaults to false and only required for transformations that perform database access.
* @see #useBasicIndirection()
*/
public void useIndirection() {
useBasicIndirection();
}
/**
* PUBLIC:
* Indirection means that a ValueHolder will be put in-between the attribute and the real object.
* This defaults to false and only required for transformations that perform database access.
* @see org.eclipse.persistence.internal.indirection.IndirectionPolicy
*/
public boolean usesIndirection() {
return this.indirectionPolicy.usesIndirection();
}
/**
* INTERNAL:
* Validate mapping declaration
*/
@Override
public void validateBeforeInitialization(AbstractSession session) throws DescriptorException {
super.validateBeforeInitialization(session);
if (isWriteOnly()) {
return;
}
if ((this.attributeTransformer == null) && (this.attributeTransformerClassName == null)) {
session.getIntegrityChecker().handleError(DescriptorException.noAttributeTransformationMethod(this));
}
if (getAttributeAccessor() instanceof InstanceVariableAttributeAccessor) {
Class attributeType = ((InstanceVariableAttributeAccessor)getAttributeAccessor()).getAttributeType();
this.indirectionPolicy.validateDeclaredAttributeType(attributeType, session.getIntegrityChecker());
} else if (getAttributeAccessor().isMethodAttributeAccessor()) {
// 323403
Class returnType = ((MethodAttributeAccessor)getAttributeAccessor()).getGetMethodReturnType();
this.indirectionPolicy.validateGetMethodReturnType(returnType, session.getIntegrityChecker());
Class parameterType = ((MethodAttributeAccessor)getAttributeAccessor()).getSetMethodParameterType();
this.indirectionPolicy.validateSetMethodParameterType(parameterType, session.getIntegrityChecker());
}
}
/**
* INTERNAL:
* Get a value from the object and set that in the respective field of the row.
*/
@Override
public Object valueFromObject(Object object, DatabaseField field, AbstractSession session) {
return invokeFieldTransformer(field, object, session);
}
/**
* INTERNAL:
* Get a value from the object and set that in the respective field of the row.
*/
@Override
public void writeFromObjectIntoRow(Object object, AbstractRecord row, AbstractSession session, WriteType writeType) {
if (isReadOnly()) {
return;
}
for (Object[] pair : this.fieldToTransformers) {
DatabaseField field = (DatabaseField)pair[0];
FieldTransformer transformer = (FieldTransformer)pair[1];
Object fieldValue = invokeFieldTransformer(field, transformer, object, session);
row.put(field, fieldValue);
}
}
/**
* INTERNAL:
* Get a value from the object and set that in the respective field of the row.
*/
@Override
public void writeFromObjectIntoRowWithChangeRecord(ChangeRecord changeRecord, AbstractRecord row, AbstractSession session, WriteType writeType) {
if (isReadOnly()) {
return;
}
for (Object[] pair : this.fieldToTransformers) {
DatabaseField field = (DatabaseField)pair[0];
Object fieldValue = ((TransformationMappingChangeRecord)changeRecord).getRecord().get(field);
row.put(field, fieldValue);
}
}
/**
* INTERNAL:
* Get a value from the object and set that in the respective field of the row.
* But before that check if the reference object is instantiated or not.
*/
@Override
public void writeFromObjectIntoRowForUpdate(WriteObjectQuery query, AbstractRecord record) {
if (!areObjectsToBeProcessedInstantiated(query.getObject())) {
return;
}
if (query.getSession().isUnitOfWork()) {
if (compareObjects(query.getBackupClone(), query.getObject(), query.getSession())) {
return;
}
}
writeFromObjectIntoRow(query.getObject(), record, query.getSession(), WriteType.UPDATE);
}
/**
* INTERNAL:
* Write fields needed for insert into the template for with null values.
*/
@Override
public void writeInsertFieldsIntoRow(AbstractRecord record, AbstractSession session) {
if (isReadOnly()) {
return;
}
for (Object[] pair : this.fieldToTransformers) {
DatabaseField field = (DatabaseField)pair[0];
record.put(field, null);
}
}
}