org.eclipse.persistence.mappings.foundation.AbstractDirectMapping Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction 346465e
/*******************************************************************************
* Copyright (c) 1998, 2013 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 v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation from Oracle TopLink
* 11/13/2009-2.0 mobrien - 294765: MapKey keyType DirectToField processing
* should return attributeClassification class in getMapKeyTargetType when
* accessor.attributeField is null in the absence of a MapKey annotation
******************************************************************************/
package org.eclipse.persistence.mappings.foundation;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.util.*;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.*;
import org.eclipse.persistence.expressions.*;
import org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor;
import org.eclipse.persistence.internal.databaseaccess.DatabasePlatform;
import org.eclipse.persistence.internal.descriptors.*;
import org.eclipse.persistence.internal.expressions.SQLSelectStatement;
import org.eclipse.persistence.internal.helper.*;
import org.eclipse.persistence.internal.identitymaps.CacheKey;
import org.eclipse.persistence.internal.queries.ContainerPolicy;
import org.eclipse.persistence.internal.queries.JoinedAttributeManager;
import org.eclipse.persistence.internal.queries.MappedKeyMapContainerPolicy;
import org.eclipse.persistence.internal.sessions.*;
import org.eclipse.persistence.mappings.converters.*;
import org.eclipse.persistence.mappings.querykeys.DirectQueryKey;
import org.eclipse.persistence.mappings.querykeys.QueryKey;
import org.eclipse.persistence.queries.*;
import org.eclipse.persistence.sessions.remote.*;
import org.eclipse.persistence.sessions.CopyGroup;
import org.eclipse.persistence.sessions.Project;
import org.eclipse.persistence.sessions.Session;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.security.PrivilegedClassForName;
/**
* Purpose: Maps an attribute to the corresponding database field type.
* The list of field types that are supported by EclipseLink's direct to field mapping
* is dependent on the relational database being used.
* A converter can be used to convert between the object and data type if they do not match.
*
* @see Converter
* @see ObjectTypeConverter
* @see TypeConversionConverter
* @see SerializedObjectConverter
* @see ClassInstanceConverter
*
* @author Sati
* @since TopLink/Java 1.0
*/
public abstract class AbstractDirectMapping extends AbstractColumnMapping implements MapKeyMapping {
/** To specify the conversion type */
protected transient Class attributeClassification;
protected transient String attributeClassificationName;
/** PERF: Also store object class of attribute in case of primitive. */
protected transient Class attributeObjectClassification;
/** Support specification of the value to use for null. */
protected transient Object nullValue;
protected DatabaseTable keyTableForMapKey = null;
protected String fieldClassificationClassName = null;
/**
* PERF: Indicates if this mapping's attribute is a simple atomic value and cannot be modified, only replaced.
* This is a tri-state to allow user to set to true or false, as default is false but
* some data-types such as Calendar or byte[] or converter types may be desired to be used as mutable.
*/
protected Boolean isMutable;
/**
* Default constructor.
*/
public AbstractDirectMapping() {
super();
}
/**
* INTERNAL:
* Used when initializing queries for mappings that use a Map.
* Called when the selection query is being initialized to add the fields for the map key to the query.
*/
public void addAdditionalFieldsToQuery(ReadQuery selectionQuery, Expression baseExpression){
if (selectionQuery.isObjectLevelReadQuery()){
((ObjectLevelReadQuery)selectionQuery).addAdditionalField(baseExpression.getField(getField()));
} else if (selectionQuery.isDataReadQuery()){
((SQLSelectStatement)((DataReadQuery)selectionQuery).getSQLStatement()).addField(baseExpression.getField(getField()));
}
}
/**
* INTERNAL:
* Used when initializing queries for mappings that use a Map
* Called when the insert query is being initialized to ensure the fields for the map key are in the insert query.
*/
public void addFieldsForMapKey(AbstractRecord joinRow) {
if (!isReadOnly()){
if (isUpdatable()){
joinRow.put(getField(), null);
}
}
}
/**
* INTERNAL:
* For mappings used as MapKeys in MappedKeyContainerPolicy. Add the target of this mapping to the deleted
* objects list if necessary
*
* This method is used for removal of private owned relationships
* DirectMappings are dealt with in their parent delete, so this is a no-op.
*/
public void addKeyToDeletedObjectsList(Object object, Map deletedObjects) {
}
/**
* PUBLIC:
* Return true if the attribute for this mapping is a simple atomic value that cannot be modified,
* only replaced.
* This is false by default unless a mutable converter is used such as the SerializedObjectConverter.
* This can be set to false in this case, or if a Calendar or byte[] is desired to be used as a mutable value it can be set to true.
*/
public boolean isMutable() {
if (isMutable == null) {
return false;
}
return isMutable.booleanValue();
}
/**
* PUBLIC:
* Return true if the attribute for this mapping is a simple atomic value that cannot be modified,
* only replaced.
* This is false by default unless a mutable converter is used such as the SerializedObjectConverter.
* This can be set to false in this case, or if a Calendar or byte[] is desired to be used as a mutable value it can be set to true.
*/
public void setIsMutable(boolean isMutable) {
if (isMutable == true) {
this.isMutable = Boolean.TRUE;
} else {
this.isMutable = Boolean.FALSE;
}
}
/**
* INTERNAL:
* Clone the attribute from the clone and assign it to the backup.
*/
@Override
public void buildBackupClone(Object clone, Object backup, UnitOfWorkImpl unitOfWork) {
buildClone(clone, null, backup, null, unitOfWork);
}
/**
* INTERNAL:
* Directly build a change record without comparison
*/
@Override
public ChangeRecord buildChangeRecord(Object clone, ObjectChangeSet owner, AbstractSession session) {
return internalBuildChangeRecord(getAttributeValueFromObject(clone), null, owner);
}
/**
* 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) {
buildCloneValue(original, clone, cloningSession);
}
/**
* 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 databaseRow, JoinedAttributeManager joinManager, Object clone, CacheKey sharedCacheKey, ObjectBuildingQuery sourceQuery, UnitOfWorkImpl unitOfWork, AbstractSession executionSession) {
// Even though the correct value may exist on the original, we can't
// make that assumption. It is easy to just build it again from the
// row even if copy policy already copied it.
// That optimization is lost.
Object attributeValue = valueFromRow(databaseRow, joinManager, sourceQuery, sharedCacheKey, executionSession, true, null);
setAttributeValueInObject(clone, attributeValue);
}
/**
* INTERNAL:
* Clone the attribute from the original and assign it to the clone.
* If mutability is configured to be true, clone the attribute if it is an instance of
* byte[], java.util.Calendar or java.util.Date (or their subclasses).
*/
public void buildCloneValue(Object original, Object clone, AbstractSession session) {
Object attributeValue = getAttributeValueFromObject(original);
attributeValue = buildCloneValue(attributeValue, session);
setAttributeValueInObject(clone, attributeValue);
}
/**
* INTERNAL:
* Clone the actual value represented by this mapping. Do set the cloned value into the object.
*/
protected Object buildCloneValue(Object attributeValue, AbstractSession session) {
Object newAttributeValue = attributeValue;
if (isMutable() && attributeValue != null) {
// EL Bug 252047 - Mutable attributes are not cloned when isMutable is enabled on a Direct Mapping
if (attributeValue instanceof byte[]) {
int length = ((byte[]) attributeValue).length;
byte[] arrayCopy = new byte[length];
System.arraycopy(attributeValue, 0, arrayCopy, 0, length);
newAttributeValue = arrayCopy;
} else if (attributeValue instanceof Byte[]) {
int length = ((Byte[]) attributeValue).length;
Byte[] arrayCopy = new Byte[length];
System.arraycopy(attributeValue, 0, arrayCopy, 0, length);
newAttributeValue = arrayCopy;
} else if (attributeValue instanceof char[]) {
int length = ((char[]) attributeValue).length;
char[] arrayCopy = new char[length];
System.arraycopy(attributeValue, 0, arrayCopy, 0, length);
newAttributeValue = arrayCopy;
} else if (attributeValue instanceof Character[]) {
int length = ((Character[]) attributeValue).length;
Character[] arrayCopy = new Character[length];
System.arraycopy(attributeValue, 0, arrayCopy, 0, length);
newAttributeValue = arrayCopy;
} else if (attributeValue instanceof Date) {
newAttributeValue = ((Date)attributeValue).clone();
} else if (attributeValue instanceof Calendar) {
newAttributeValue = ((Calendar)attributeValue).clone();
} else {
newAttributeValue = getObjectValue(getFieldValue(attributeValue, session), session);
}
}
return newAttributeValue;
}
/**
* 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) {
buildCloneValue(original, copy, group.getSession());
}
/**
* Build a clone of the given element in a unitOfWork.
*/
public Object buildElementClone(Object attributeValue, Object parent, CacheKey cacheKey, Integer refreshCascade, AbstractSession cloningSession, boolean isExisting, boolean isFromSharedCache){
return buildCloneValue(attributeValue, cloningSession);
}
/**
* INTERNAL:
* In case Query By Example is used, this method builds and returns an expression that
* corresponds to a single attribute and it's value for a directToField mapping.
*/
@Override
public Expression buildExpression(Object queryObject, QueryByExamplePolicy policy, Expression expressionBuilder, Map processedObjects, AbstractSession session) {
String attributeName = this.getAttributeName();
Object attributeValue = this.getAttributeValueFromObject(queryObject);
if (!policy.shouldIncludeInQuery(queryObject.getClass(), attributeName, attributeValue)) {
//the attribute name and value pair is not to be included in the query.
return null;
}
Expression expression = expressionBuilder.get(attributeName);
if (attributeValue == null) {
expression = policy.completeExpressionForNull(expression);
} else {
expression = policy.completeExpression(expression, attributeValue, attributeValue.getClass());
}
return expression;
}
/**
* INTERNAL:
* Certain key mappings favor different types of selection query. Return the appropriate
* type of selectionQuery.
*/
public ReadQuery buildSelectionQueryForDirectCollectionKeyMapping(ContainerPolicy containerPolicy){
DataReadQuery query = new DataReadQuery();
query.setSQLStatement(new SQLSelectStatement());
query.setContainerPolicy(containerPolicy);
return query;
}
/**
* INTERNAL:
* Cascade discover and persist new objects during commit to the map key.
*/
public void cascadeDiscoverAndPersistUnregisteredNewObjects(Object object, Map newObjects, Map unregisteredExistingObjects, Map visitedObjects, UnitOfWorkImpl uow, boolean getAttributeValueFromObject, Set cascadeErrors){
//objects referenced by this mapping are not registered as they have
// no identity, this is a no-op.
}
/**
* INTERNAL:
* Cascade perform delete through mappings that require the cascade.
*/
public void cascadePerformRemoveIfRequired(Object object, UnitOfWorkImpl uow, Map visitedObjects, boolean getAttributeValueFromObject) {
//objects referenced by this mapping are not registered as they have
// no identity, this is a no-op.
}
/**
* INTERNAL:
* Cascade perform delete through mappings that require the cascade.
*/
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.
*/
public void cascadeRegisterNewIfRequired(Object object, UnitOfWorkImpl uow, Map visitedObjects, boolean getAttributeValueFromObject) {
//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.
*/
public void cascadeRegisterNewIfRequired(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:
* The mapping clones itself to create deep copy.
*/
@Override
public Object clone() {
AbstractDirectMapping clone = (AbstractDirectMapping)super.clone();
// Field must be cloned so aggregates do not share fields.
clone.setField(getField().clone());
return clone;
}
/**
* INTERNAL:
* Compare the clone and backup clone values and return a change record if the value changed.
*/
@Override
public ChangeRecord compareForChange(Object clone, Object backUp, ObjectChangeSet owner, AbstractSession session) {
// same code as write from object into row for update
if (owner.isNew()) {
return internalBuildChangeRecord(getAttributeValueFromObject(clone), null, owner);
} else if (!compareObjects(backUp, clone, session)) {
Object oldValue = null;
if (backUp != null && clone != backUp) {
oldValue = getAttributeValueFromObject(backUp);
}
return internalBuildChangeRecord(getAttributeValueFromObject(clone), oldValue, owner);
}
return null;
}
/**
* INTERNAL:
* For mappings used as MapKeys in MappedKeyContainerPolicy, Delete the passed object if necessary.
*
* This method is used for removal of private owned relationships
* DirectMappings are dealt with in their parent delete, so this is a no-op.
*/
public void deleteMapKey(Object objectDeleted, AbstractSession session){
}
/**
* INTERNAL:
* Compare the attributes belonging to this mapping for the objects.
*/
@Override
public boolean compareObjects(Object firstObject, Object secondObject, AbstractSession session) {
Object firstValue = getAttributeValueFromObject(firstObject);
Object secondValue = getAttributeValueFromObject(secondObject);
return compareObjectValues(firstValue, secondValue, session);
}
/**
* INTERNAL:
* Compare the attribute values.
*/
protected boolean compareObjectValues(Object firstValue, Object secondValue, AbstractSession session) {
// PERF: Check identity before conversion.
if (firstValue == secondValue) {
return true;
}
if ((firstValue != null) && (secondValue != null)) {
// PERF: Check equals first, as normally no change.
// Also for serialization objects bytes may not be consistent, but equals may work (HashMap).
if (firstValue.equals(secondValue)) {
return true;
}
}
// CR2114 - following two lines modified; getFieldValue() needs class as an argument
firstValue = getFieldValue(firstValue, session);
secondValue = getFieldValue(secondValue, session);
// PERF: Check identity/nulls before special type comparison.
if (firstValue == secondValue) {
return true;
}
if ((firstValue == null) || (secondValue == null)) {
return false;
}
// PERF: Check equals first, as normally no change.
if (firstValue.equals(secondValue)) {
return true;
}
return Helper.comparePotentialArrays(firstValue, secondValue);
}
/**
* INTERNAL:
* Convert all the class-name-based settings in this mapping to actual class-based settings
* This method is implemented by subclasses as necessary.
*/
@Override
public void convertClassNamesToClasses(ClassLoader classLoader){
super.convertClassNamesToClasses(classLoader);
if (getAttributeClassificationName() != null) {
Class attributeClass = null;
try{
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
try {
attributeClass = (Class)AccessController.doPrivileged(new PrivilegedClassForName(getAttributeClassificationName(), true, classLoader));
} catch (PrivilegedActionException exception) {
throw ValidationException.classNotFoundWhileConvertingClassNames(getAttributeClassificationName(), exception.getException());
}
} else {
attributeClass = org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(getAttributeClassificationName(), true, classLoader);
}
} catch (ClassNotFoundException exc){
throw ValidationException.classNotFoundWhileConvertingClassNames(getAttributeClassificationName(), exc);
}
setAttributeClassification(attributeClass);
}
if (fieldClassificationClassName != null){
Class fieldClassification = null;
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
try {
fieldClassification = (Class) AccessController.doPrivileged(new PrivilegedClassForName(fieldClassificationClassName, true, classLoader));
} catch (PrivilegedActionException exception) {
throw ValidationException.classNotFoundWhileConvertingClassNames(fieldClassificationClassName, exception.getException());
}
} else {
fieldClassification = org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(fieldClassificationClassName, true, classLoader);
}
} catch (ClassNotFoundException exc) {
throw ValidationException.classNotFoundWhileConvertingClassNames(fieldClassificationClassName, exc);
} catch (Exception e) {
// Catches IllegalAccessException and InstantiationException
throw ValidationException.classNotFoundWhileConvertingClassNames(fieldClassificationClassName, e);
}
setFieldClassification(fieldClassification);
}
}
/**
* INTERNAL:
* Creates the Array of simple types used to recreate this map.
*/
public Object createSerializableMapKeyInfo(Object key, AbstractSession session){
return key; // DirectToFields are already simple types.
}
/**
* INTERNAL:
* Create an instance of the Key object from the key information extracted from the map.
* This may return the value directly in case of a simple key or will be used as the FK to load a related entity.
*/
public List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy