All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.persistence.internal.dynamic.DynamicEntityImpl Maven / Gradle / Ivy

There is a newer version: 5.0.0-B03
Show newest version
/*
 * Copyright (c) 1998, 2019 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:
//     dclarke, mnorman - Dynamic Persistence
//       http://wiki.eclipse.org/EclipseLink/Development/Dynamic
//       (https://bugs.eclipse.org/bugs/show_bug.cgi?id=200045)
//
package org.eclipse.persistence.internal.dynamic;

//javase imports
import static org.eclipse.persistence.internal.helper.Helper.getShortClassName;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.changetracking.ChangeTracker;
import org.eclipse.persistence.dynamic.DynamicEntity;
import org.eclipse.persistence.dynamic.DynamicType;
import org.eclipse.persistence.exceptions.DynamicException;
import org.eclipse.persistence.indirection.IndirectContainer;
import org.eclipse.persistence.indirection.ValueHolderInterface;
import org.eclipse.persistence.internal.descriptors.DescriptorIterator;
import org.eclipse.persistence.internal.descriptors.PersistenceEntity;
import org.eclipse.persistence.internal.identitymaps.CacheKey;
import org.eclipse.persistence.internal.queries.JoinedAttributeManager;
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.UnitOfWorkImpl;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.queries.FetchGroup;
import org.eclipse.persistence.queries.FetchGroupTracker;
import org.eclipse.persistence.queries.ObjectBuildingQuery;
import org.eclipse.persistence.queries.ObjectLevelReadQuery;
import org.eclipse.persistence.sessions.Session;
import org.eclipse.persistence.sessions.remote.DistributedSession;

/**
 * This abstract class is used to represent an entity which typically is not
 * realized in Java code. In combination with the DynamicClassLoader ASM is used
 * to generate subclasses that will work within EclipseLink's framework. Since
 * no concrete fields or methods exist on this class the mappings used must be
 * customized to use a custom AttributeAccessor ({@link ValuesAccessor}).
 * 

* Type/Property Meta-model: This dynamic entity approach also includes a * meta-model facade to simplify access to the types and property information so * that clients can more easily understand the model. Each * {@link DynamicTypeImpl} wraps the underlying EclipseLink * relational-descriptor and the {@link DynamicPropertiesManager} wraps each mapping. * The client application can use these types and properties to facilitate * generic access to the entity instances and are required for creating new * instances as well as for accessing the Java class needed for JPA and * EclipseLink native API calls. * * @author dclarke, mnorman * @since EclipseLink 1.2 */ public abstract class DynamicEntityImpl implements DynamicEntity, PersistenceEntity, ChangeTracker, FetchGroupTracker { /** * Fetch properties manager. * * @return the dynamic properties manager */ public abstract DynamicPropertiesManager fetchPropertiesManager(); protected Map propertiesMap = new HashMap<>(); /** * Instantiates a new dynamic entity impl. */ public DynamicEntityImpl() { postConstruct(); // life-cycle callback } /** * Gets the properties map. * * @return the properties map */ public Map getPropertiesMap() { return propertiesMap; } /** * Post construct. */ protected void postConstruct() { DynamicPropertiesManager dpm = fetchPropertiesManager(); dpm.postConstruct(this); } /** * Gets internal impl class of {@link DynamicType}. * * @return Dynamic type of this entity * @throws DynamicException if type is null */ public DynamicTypeImpl getType() throws DynamicException { DynamicType type = fetchPropertiesManager().getType(); if (type == null) { throw DynamicException.entityHasNullType(this); } return (DynamicTypeImpl) type; } //DynamicEntity API /* (non-Javadoc) * @see org.eclipse.persistence.dynamic.DynamicEntity#get(java.lang.String) */ @Override public T get(String propertyName) throws DynamicException { DynamicPropertiesManager dpm = fetchPropertiesManager(); if (dpm.contains(propertyName)) { if (_persistence_getFetchGroup() != null) { String errorMsg = _persistence_getFetchGroup().onUnfetchedAttribute(this, propertyName); if (errorMsg != null) { throw DynamicException.invalidPropertyName(dpm.getType(), propertyName); } } PropertyWrapper wrapper = propertiesMap.get(propertyName); if (wrapper == null) { // properties can be added after constructor is called wrapper = new PropertyWrapper(); propertiesMap.put(propertyName, wrapper); } Object value = wrapper.getValue(); // trigger any indirection if (value instanceof ValueHolderInterface) { value = ((ValueHolderInterface) value).getValue(); } else if (value instanceof IndirectContainer) { value = ((IndirectContainer) value).getValueHolder().getValue(); } try { return (T) value; } catch (ClassCastException cce) { ClassDescriptor descriptor = getType().getDescriptor(); DatabaseMapping dm = null; if (descriptor != null) { dm = descriptor.getMappingForAttributeName(propertyName); } else { dm = new UnknownMapping(propertyName); } throw DynamicException.invalidGetPropertyType(dm, cce); } } else { throw DynamicException.invalidPropertyName(dpm.getType(), propertyName); } } /* (non-Javadoc) * @see org.eclipse.persistence.dynamic.DynamicEntity#isSet(java.lang.String) */ @Override public boolean isSet(String propertyName) throws DynamicException { if (fetchPropertiesManager().contains(propertyName)) { if (_persistence_getFetchGroup() != null && !_persistence_getFetchGroup().containsAttributeInternal(propertyName)) { return false; } PropertyWrapper wrapper = propertiesMap.get(propertyName); if (wrapper == null) { // properties can be added after constructor is called wrapper = new PropertyWrapper(); propertiesMap.put(propertyName, wrapper); } return wrapper.isSet(); } else { throw DynamicException.invalidPropertyName(fetchPropertiesManager().getType(), propertyName); } } /* (non-Javadoc) * @see org.eclipse.persistence.dynamic.DynamicEntity#set(java.lang.String, java.lang.Object) */ @Override public DynamicEntity set(String propertyName, Object value) throws DynamicException { return set(propertyName, value, true); } /** * Sets the. * * @param propertyName the property name * @param value the value * @param firePropertyChange the fire property change * @return the dynamic entity * @throws DynamicException the dynamic exception */ public DynamicEntity set(String propertyName, Object value, boolean firePropertyChange) throws DynamicException { DynamicPropertiesManager dpm = fetchPropertiesManager(); dpm.checkSet(propertyName, value); // life-cycle callback if (_persistence_getFetchGroup() != null) { String errorMsg = _persistence_getFetchGroup().onUnfetchedAttributeForSet(this, propertyName); if (errorMsg != null) { throw DynamicException.invalidPropertyName(dpm.getType(), propertyName); } } PropertyWrapper wrapper = propertiesMap.get(propertyName); if (wrapper == null) { // properties can be added after constructor is called wrapper = new PropertyWrapper(); propertiesMap.put(propertyName, wrapper); } Object oldValue = null; Object wrapperValue = wrapper.getValue(); if (wrapperValue instanceof ValueHolderInterface) { ValueHolderInterface vh = (ValueHolderInterface) wrapperValue; if (vh.isInstantiated()) { oldValue = vh.getValue(); } vh.setValue(value); wrapper.isSet(true); } else { oldValue = wrapperValue; wrapper.setValue(value); wrapper.isSet(true); } if (changeListener != null && firePropertyChange) { changeListener.propertyChange(new PropertyChangeEvent(this, propertyName, oldValue, value)); } return this; } // Made static final for performance reasons. public static final class PropertyWrapper { private Object value = null; private boolean isSet = false; /** * Instantiates a new property wrapper. */ public PropertyWrapper() { } /** * Instantiates a new property wrapper. * * @param value the value */ public PropertyWrapper(Object value) { setValue(value); } /** * Gets the value. * * @return the value */ public Object getValue() { return value; } /** * Sets the value. * * @param value the new value */ public void setValue(Object value) { this.value = value; } /** * Checks if is sets the. * * @return true, if is sets the */ public boolean isSet() { return isSet; } /** * Checks if is set. * * @param isSet the is set */ public void isSet(boolean isSet) { this.isSet = isSet; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { StringBuilder sb = new StringBuilder(); if (isSet) { sb.append("[T]"); } else { if (value == null) { sb.append("[F]"); } else { sb.append("[d]"); } } if (value == null) { sb.append(""); } else { sb.append(value.toString()); } return sb.toString(); } } // Made static final for performance reasons. static final class UnknownMapping extends DatabaseMapping { /** * Instantiates a new unknown mapping. * * @param propertyName the property name */ public UnknownMapping(String propertyName) { setAttributeName(propertyName); } /* (non-Javadoc) * @see org.eclipse.persistence.mappings.DatabaseMapping#buildBackupClone(java.lang.Object, java.lang.Object, org.eclipse.persistence.internal.sessions.UnitOfWorkImpl) */ @Override public void buildBackupClone(Object clone, Object backup, UnitOfWorkImpl unitOfWork) { } /* (non-Javadoc) * @see org.eclipse.persistence.mappings.DatabaseMapping#buildClone(java.lang.Object, org.eclipse.persistence.internal.identitymaps.CacheKey, java.lang.Object, java.lang.Integer, org.eclipse.persistence.internal.sessions.AbstractSession) */ @Override public void buildClone(Object original, CacheKey cacheKey, Object clone, Integer refreshCascade, AbstractSession cloningSession) { } /* (non-Javadoc) * @see org.eclipse.persistence.mappings.DatabaseMapping#buildCloneFromRow(org.eclipse.persistence.internal.sessions.AbstractRecord, org.eclipse.persistence.internal.queries.JoinedAttributeManager, java.lang.Object, org.eclipse.persistence.internal.identitymaps.CacheKey, org.eclipse.persistence.queries.ObjectBuildingQuery, org.eclipse.persistence.internal.sessions.UnitOfWorkImpl, org.eclipse.persistence.internal.sessions.AbstractSession) */ @Override public void buildCloneFromRow(AbstractRecord databaseRow, JoinedAttributeManager joinManager, Object clone, CacheKey sharedCacheKey, ObjectBuildingQuery sourceQuery, UnitOfWorkImpl unitOfWork, AbstractSession executionSession) { } /* (non-Javadoc) * @see org.eclipse.persistence.mappings.DatabaseMapping#cascadePerformRemoveIfRequired(java.lang.Object, org.eclipse.persistence.internal.sessions.UnitOfWorkImpl, java.util.Map) */ @Override public void cascadePerformRemoveIfRequired(Object object, UnitOfWorkImpl uow, Map visitedObjects) { } /* (non-Javadoc) * @see org.eclipse.persistence.mappings.DatabaseMapping#cascadeRegisterNewIfRequired(java.lang.Object, org.eclipse.persistence.internal.sessions.UnitOfWorkImpl, java.util.Map) */ @Override public void cascadeRegisterNewIfRequired(Object object, UnitOfWorkImpl uow, Map visitedObjects) { } /* (non-Javadoc) * @see org.eclipse.persistence.mappings.DatabaseMapping#compareForChange(java.lang.Object, java.lang.Object, org.eclipse.persistence.internal.sessions.ObjectChangeSet, org.eclipse.persistence.internal.sessions.AbstractSession) */ @Override public ChangeRecord compareForChange(Object clone, Object backup, ObjectChangeSet owner, AbstractSession session) { return null; } /* (non-Javadoc) * @see org.eclipse.persistence.mappings.DatabaseMapping#compareObjects(java.lang.Object, java.lang.Object, org.eclipse.persistence.internal.sessions.AbstractSession) */ @Override public boolean compareObjects(Object firstObject, Object secondObject, AbstractSession session) { return false; } /* (non-Javadoc) * @see org.eclipse.persistence.mappings.DatabaseMapping#fixObjectReferences(java.lang.Object, java.util.Map, java.util.Map, org.eclipse.persistence.queries.ObjectLevelReadQuery, org.eclipse.persistence.sessions.remote.RemoteSession) */ @Override public void fixObjectReferences(Object object, Map objectDescriptors, Map processedObjects, ObjectLevelReadQuery query, DistributedSession session) { } /* (non-Javadoc) * @see org.eclipse.persistence.mappings.DatabaseMapping#iterate(org.eclipse.persistence.internal.descriptors.DescriptorIterator) */ @Override public void iterate(DescriptorIterator iterator) { } /* (non-Javadoc) * @see org.eclipse.persistence.mappings.DatabaseMapping#mergeChangesIntoObject(java.lang.Object, org.eclipse.persistence.internal.sessions.ChangeRecord, java.lang.Object, org.eclipse.persistence.internal.sessions.MergeManager, org.eclipse.persistence.internal.sessions.AbstractSession) */ @Override public void mergeChangesIntoObject(Object target, ChangeRecord changeRecord, Object source, MergeManager mergeManager, AbstractSession targetSession) { } /* (non-Javadoc) * @see org.eclipse.persistence.mappings.DatabaseMapping#mergeIntoObject(java.lang.Object, boolean, java.lang.Object, org.eclipse.persistence.internal.sessions.MergeManager, org.eclipse.persistence.internal.sessions.AbstractSession) */ @Override public void mergeIntoObject(Object target, boolean isTargetUninitialized, Object source, MergeManager mergeManager, AbstractSession targetSession) { } } //PersistenceEntity API /** * Cache the primary key within the entity * * @see PersistenceEntity#_persistence_setId(Object) */ private Object primaryKey; protected CacheKey cacheKey; /* (non-Javadoc) * @see org.eclipse.persistence.internal.descriptors.PersistenceEntity#_persistence_getId() */ @Override @SuppressWarnings("unchecked") public Object _persistence_getId() { return this.primaryKey; } /* (non-Javadoc) * @see org.eclipse.persistence.internal.descriptors.PersistenceEntity#_persistence_setId(java.lang.Object) */ @Override @SuppressWarnings("unchecked") public void _persistence_setId(Object pk) { this.primaryKey = pk; } /* (non-Javadoc) * @see org.eclipse.persistence.internal.descriptors.PersistenceEntity#_persistence_getCacheKey() */ @Override public CacheKey _persistence_getCacheKey() { return this.cacheKey; } /* (non-Javadoc) * @see org.eclipse.persistence.internal.descriptors.PersistenceEntity#_persistence_setCacheKey(org.eclipse.persistence.internal.identitymaps.CacheKey) */ @Override public void _persistence_setCacheKey(CacheKey cacheKey) { this.cacheKey = cacheKey; } //ChangeTracker API /** * ChangeListener used for attribute change tracking processed in the * property. Set through * {@link ChangeTracker#_persistence_setPropertyChangeListener(PropertyChangeListener)} */ private PropertyChangeListener changeListener = null; /* (non-Javadoc) * @see org.eclipse.persistence.descriptors.changetracking.ChangeTracker#_persistence_getPropertyChangeListener() */ @Override public PropertyChangeListener _persistence_getPropertyChangeListener() { return this.changeListener; } /* (non-Javadoc) * @see org.eclipse.persistence.descriptors.changetracking.ChangeTracker#_persistence_setPropertyChangeListener(java.beans.PropertyChangeListener) */ @Override public void _persistence_setPropertyChangeListener(PropertyChangeListener listener) { this.changeListener = listener; } //FetchGroup API /** * FetchGroup cached by * {@link FetchGroupTracker#_persistence_setFetchGroup(FetchGroup)} */ private FetchGroup fetchGroup; /** * {@link FetchGroupTracker#_persistence_setShouldRefreshFetchGroup(boolean)} */ private boolean refreshFetchGroup = false; /* (non-Javadoc) * @see org.eclipse.persistence.queries.FetchGroupTracker#_persistence_getFetchGroup() */ @Override public FetchGroup _persistence_getFetchGroup() { return this.fetchGroup; } /* (non-Javadoc) * @see org.eclipse.persistence.queries.FetchGroupTracker#_persistence_setFetchGroup(org.eclipse.persistence.queries.FetchGroup) */ @Override public void _persistence_setFetchGroup(FetchGroup group) { this.fetchGroup = group; } /* (non-Javadoc) * @see org.eclipse.persistence.queries.FetchGroupTracker#_persistence_setShouldRefreshFetchGroup(boolean) */ @Override public void _persistence_setShouldRefreshFetchGroup(boolean shouldRefreshFetchGroup) { this.refreshFetchGroup = shouldRefreshFetchGroup; } /* (non-Javadoc) * @see org.eclipse.persistence.queries.FetchGroupTracker#_persistence_shouldRefreshFetchGroup() */ @Override public boolean _persistence_shouldRefreshFetchGroup() { return this.refreshFetchGroup; } /** * Return true if the attribute is in the fetch group being tracked. * * @param attribute the attribute * @return true, if successful */ @Override public boolean _persistence_isAttributeFetched(String attribute) { return this.fetchGroup == null || this.fetchGroup.containsAttributeInternal(attribute); } /** * Reset all attributes of the tracked object to the un-fetched state with * initial default values. */ @Override public void _persistence_resetFetchGroup() { } /** * Session cached by * {@link FetchGroupTracker#_persistence_setSession(Session)} */ private Session session; /* (non-Javadoc) * @see org.eclipse.persistence.queries.FetchGroupTracker#_persistence_getSession() */ @Override public Session _persistence_getSession() { return this.session; } /* (non-Javadoc) * @see org.eclipse.persistence.queries.FetchGroupTracker#_persistence_setSession(org.eclipse.persistence.sessions.Session) */ @Override public void _persistence_setSession(Session session) { this.session = session; } /** * String representation of the dynamic entity using the entity type name * and the primary key values - something like {Emp 10} or {Phone 234-5678 10}. * * @return the string */ @Override public String toString() { // this will print something like {Emp 10} or {Phone 234-5678 10} StringBuilder sb = new StringBuilder(20); sb.append('{'); sb.append(getShortClassName(this.getClass())); if (primaryKey != null) { sb.append(' '); sb.append(primaryKey); } sb.append('}'); return sb.toString(); } }