Java tutorial
/* * Copyright (c) 2012-2013, Batu Alp Ceylan * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.batoo.jpa.core.impl.instance; import java.sql.Connection; import java.sql.SQLException; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import javax.persistence.LockModeType; import javax.persistence.metamodel.Attribute.PersistentAttributeType; import javax.persistence.metamodel.PluralAttribute.CollectionType; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.mutable.MutableBoolean; import org.batoo.common.log.BLogger; import org.batoo.common.log.BLoggerFactory; import org.batoo.common.reflect.AbstractAccessor; import org.batoo.jpa.core.impl.manager.EntityManagerImpl; import org.batoo.jpa.core.impl.manager.SessionImpl; import org.batoo.jpa.core.impl.model.EntityTypeImpl; import org.batoo.jpa.core.impl.model.attribute.BasicAttribute; import org.batoo.jpa.core.impl.model.mapping.AbstractMapping; import org.batoo.jpa.core.impl.model.mapping.AssociationMappingImpl; import org.batoo.jpa.core.impl.model.mapping.BasicMappingImpl; import org.batoo.jpa.core.impl.model.mapping.EmbeddedMappingImpl; import org.batoo.jpa.core.impl.model.mapping.JoinedMapping; import org.batoo.jpa.core.impl.model.mapping.PluralAssociationMappingImpl; import org.batoo.jpa.core.impl.model.mapping.PluralMappingEx; import org.batoo.jpa.core.impl.model.mapping.SingularAssociationMappingImpl; import org.batoo.jpa.core.impl.model.mapping.SingularMappingEx; import org.batoo.common.util.Pair; import org.batoo.jpa.jdbc.mapping.SingularMapping; import org.batoo.jpa.parser.metadata.EntityListenerMetadata.EntityListenerType; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; /** * The managed instance to track entity instances. * * @param <X> * the type of the managed instance * * @author hceylan * @since 2.0.0 */ public class ManagedInstance<X> { private static final BLogger LOG = BLoggerFactory.getLogger(ManagedInstance.class); private final EntityTypeImpl<X> type; private final SessionImpl session; private final X instance; private Status status; private Status oldStatus; private LockModeType lockMode; private final HashMap<AbstractMapping<?, ?, ?>, Object> snapshot = Maps.newHashMap(); private final HashSet<String> joinsLoaded; private final ArrayList<PluralMappingEx<?, ?, ?>> collectionsChanged; private boolean loading; private boolean loadingFromCache; private boolean refreshing; private boolean changed; private boolean hasInitialId; private ManagedId<? super X> id; private int h; private boolean prePersistCalled; private boolean preRemoveCalled; private Object oldVersion; /** * The current lock mode context. */ public static ThreadLocal<LockModeType> LOCK_CONTEXT = new ThreadLocal<LockModeType>(); /** * @param type * the entity type of the instance * @param session * the session * @param instance * the instance * * @since 2.0.0 */ public ManagedInstance(EntityTypeImpl<X> type, SessionImpl session, X instance) { super(); this.type = type; this.session = session; this.instance = instance; this.lockMode = ManagedInstance.LOCK_CONTEXT.get(); this.collectionsChanged = Lists.newArrayList(); this.joinsLoaded = Sets.newHashSet(); this.status = Status.MANAGED; } /** * @param type * the entity type of the instance * @param session * the session * @param instance * the instance * @param id * the id of the instance * * @since 2.0.0 */ public ManagedInstance(EntityTypeImpl<X> type, SessionImpl session, X instance, ManagedId<? super X> id) { this(type, session, instance); type.setId(session, instance, id.getId()); this.id = id; } /** * Cascades the detach operation. * * @param entityManager * the entity manager * * @since 2.0.0 */ public void cascadeDetach(EntityManagerImpl entityManager) { this.status = Status.DETACHED; ManagedInstance.LOG.debug("Cascading detach on {0}", this); for (final AssociationMappingImpl<?, ?, ?> association : this.type.getAssociationsDetachable()) { // if the association a collection attribute then we will cascade to each element if (association instanceof PluralAssociationMappingImpl) { final PluralAssociationMappingImpl<?, ?, ?> mapping = (PluralAssociationMappingImpl<?, ?, ?>) association; final Collection<?> collection; if (mapping.getAttribute().getCollectionType() == CollectionType.MAP) { collection = ((Map<?, ?>) mapping.get(this.instance)).values(); } else { // extract the collection collection = (Collection<?>) mapping.get(this.instance); } // cascade to each element in the collection if (collection instanceof List) { final List<?> list = (List<?>) collection; for (int i = 0; i < list.size(); i++) { entityManager.detach(list.get(i)); } } else if (collection != null) { for (final Object element : collection) { entityManager.detach(element); } } } else { final SingularAssociationMappingImpl<?, ?> mapping = (SingularAssociationMappingImpl<?, ?>) association; final Object associate = mapping.get(this.instance); entityManager.detach(associate); } } } /** * Cascades the persist operation. * * @param entityManager * the entity manager * @param processed * registry of processed entities * @param instances * the managed instances * @return true if an implicit flush is required, false otherwise * * @since 2.0.0 */ public boolean cascadePersist(EntityManagerImpl entityManager, ArrayList<Object> processed, LinkedList<ManagedInstance<?>> instances) { ManagedInstance.LOG.debug("Cascading persist on {0}", this); boolean requiresFlush = false; for (final AssociationMappingImpl<?, ?, ?> association : this.type.getAssociationsPersistable()) { // if the association a collection attribute then we will cascade to each element if (association instanceof PluralAssociationMappingImpl) { final PluralAssociationMappingImpl<?, ?, ?> mapping = (PluralAssociationMappingImpl<?, ?, ?>) association; switch (mapping.getAttribute().getCollectionType()) { case MAP: // extract the map final Map<?, ?> map = (Map<?, ?>) mapping.get(this.instance); // cascade to each element in the map for (final Object element : map.values()) { requiresFlush |= entityManager.persistImpl(element, processed, instances); } break; case LIST: // extract the list final List<?> list = (List<?>) mapping.get(this.instance); // cascade to each element in the list for (int i = 0; i < list.size(); i++) { requiresFlush |= entityManager.persistImpl(list.get(i), processed, instances); } break; default: // extract the collection final Collection<?> collection = (Collection<?>) mapping.get(this.instance); // cascade to each element in the collection if (collection instanceof List) { final List<?> castedList = (List<?>) collection; for (int i = 0; i < castedList.size(); i++) { requiresFlush |= entityManager.persistImpl(castedList.get(i), processed, instances); } } else if (collection != null) { for (final Object element : collection) { requiresFlush |= entityManager.persistImpl(element, processed, instances); } } break; } } else { final SingularAssociationMappingImpl<?, ?> mapping = (SingularAssociationMappingImpl<?, ?>) association; final Object associate = mapping.get(this.instance); if (associate != null) { requiresFlush |= entityManager.persistImpl(associate, processed, instances); } } } return requiresFlush; } /** * Cascades the remove operation * * @param entityManager * the entity manager * @param processed * registry of processed entities * @param instances * the managed instances * * @since 2.0.0 */ public void cascadeRemove(EntityManagerImpl entityManager, ArrayList<Object> processed, LinkedList<ManagedInstance<?>> instances) { ManagedInstance.LOG.debug("Cascading remove on {0}", this); for (final AssociationMappingImpl<?, ?, ?> association : this.type.getAssociationsRemovable()) { // if the association a collection attribute then we will cascade to each element if (association instanceof PluralAssociationMappingImpl) { final PluralAssociationMappingImpl<?, ?, ?> mapping = (PluralAssociationMappingImpl<?, ?, ?>) association; // extract the collection final Collection<?> collection; if (mapping.getAttribute().getCollectionType() == CollectionType.MAP) { collection = ((Map<?, ?>) mapping.get(this.instance)).values(); } else { collection = (Collection<?>) mapping.get(this.instance); } // cascade to each element in the collection if (collection instanceof List) { final List<?> list = (List<?>) collection; for (int i = 0; i < list.size(); i++) { entityManager.removeImpl(list.get(i), processed, instances); } } else if (collection != null) { for (final Object element : collection) { entityManager.removeImpl(element, processed, instances); } } } else { final SingularAssociationMappingImpl<?, ?> mapping = (SingularAssociationMappingImpl<?, ?>) association; final Object associate = mapping.get(this.instance); if (associate != null) { entityManager.removeImpl(associate, processed, instances); } } } } /** * Marks the instance as may have changed. * * @since 2.0.0 */ public void changed() { if (!this.changed && (this.collectionsChanged.size() == 0)) { this.session.setChanged(this); } if (!this.changed) { this.snapshot(); this.changed = true; } } /** * Checks that no association of the instance is transient * * @since 2.0.0 */ public void checkTransients() { for (final AssociationMappingImpl<?, ?, ?> association : this.type.getAssociationsNotPersistable()) { association.checkTransient(this); } } /** * Checks if the instance updated. * <p> * Only meaningful for external entities as their instances' are not enhanced. * * @since 2.0.0 */ public void checkUpdated() { // no snapshot, nothing to check if ((this.snapshot.size() == 0) || this.changed) { return; } if (this.checkUpdatedImpl()) { this.changed(); } } private boolean checkUpdatedImpl() { // iterate over old values for (final AbstractMapping<?, ?, ?> mapping : this.type.getMappingsSingular()) { final Object newValue = mapping.get(this.instance); final Object oldValue = this.snapshot.get(mapping); // if it is changed then mark as changed and bail out if (mapping.getAttribute().getPersistentAttributeType() == PersistentAttributeType.BASIC) { if (!ObjectUtils.equals(oldValue, newValue)) { return true; } continue; } if (oldValue != newValue) { return true; } } return false; } /** * Enhances the collections of the managed instance. * * @since 2.0.0 */ public void enhanceCollections() { for (final PluralMappingEx<?, ?, ?> collection : this.type.getMappingsPlural()) { collection.enhance(this); } } /** * {@inheritDoc} * */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } final ManagedId<? super X> thisId = this.getId(); final ManagedInstance<?> other = (ManagedInstance<?>) obj; return (thisId != null) && thisId.equals(other.getId()); } /** * Fills the sequence / table generated values. The operation returns false if at least one entity needs to obtain identity from the * database. * * @return false if all OK, true if if at least one entity needs to obtain identity from the database * * @since 2.0.0 */ public boolean fillIdValues() { ManagedInstance.LOG.debug("Auto generating id values for {0}", this); return this.hasInitialId = this.fillValuesImpl(); } private boolean fillValuesImpl() { final EntityTypeImpl<X> _type = this.type; if (_type.hasSingleIdAttribute()) { return this.type.getRootType().getIdMapping().fillValue(_type.getRootType(), this, this.instance); } else { for (final Pair<SingularMapping<?, ?>, AbstractAccessor> mapping : _type.getIdMappings()) { if (!((SingularMappingEx<?, ?>) mapping.getFirst()).fillValue(_type.getRootType(), this, this.instance)) { return false; } } return true; } } /** * Fires the callbacks. * * @param type * the type of the callbacks * * @since 2.0.0 */ public void fireCallbacks(EntityListenerType type) { EntityListenerType typeToFire = type; if ((type == EntityListenerType.PRE_UPDATE) && (this.status == Status.NEW)) { typeToFire = EntityListenerType.PRE_PERSIST; } if ((type == EntityListenerType.POST_UPDATE) && (this.oldStatus == Status.NEW)) { typeToFire = EntityListenerType.POST_PERSIST; } // safeguard single invocation for PrePersists if (typeToFire == EntityListenerType.PRE_PERSIST) { if (!this.prePersistCalled) { this.prePersistCalled = true; this.type.fireCallbacks(this.instance, typeToFire); } } else if (typeToFire == EntityListenerType.PRE_REMOVE) { if (!this.preRemoveCalled) { this.preRemoveCalled = true; this.type.fireCallbacks(this.instance, typeToFire); } } else { this.type.fireCallbacks(this.instance, typeToFire); } } /** * Flushes the associations. * * @param connection * the connection * @param removals * true if the removals should be flushed and false for the additions * @param force * true to force, effective only for insertions and for new entities. * @throws SQLException * thrown if there is an underlying SQL Exception * * @since 2.0.0 */ public void flushAssociations(Connection connection, boolean removals, boolean force) throws SQLException { if (!removals || (this.status != Status.NEW)) { ManagedInstance.LOG.debug("Flushing associations for instance {0}", this); for (final JoinedMapping<?, ?, ?> collection : this.type.getMappingsJoined()) { collection.flush(connection, this, removals, force); } } } /** * Returns the id of the instance. * * @return the id of the instance * * @since 2.0.0 */ public ManagedId<? super X> getId() { if (this.id != null) { return this.id; } return this.id = this.type.getId(this.instance); } /** * Returns the instance. * * @return the instance * @since 2.0.0 */ public X getInstance() { return this.instance; } /** * Returns the lock mode of the instance. * * @return the lock mode * * @since 2.0.0 */ public LockModeType getLockMode() { return this.lockMode; } /** * Returns the old version of the instance. * * @return the old version of the instance * * @since 2.0.1 */ public Object getOldVersion() { return this.oldVersion; } /** * Returns the session. * * @return the session * @since 2.0.0 */ public SessionImpl getSession() { return this.session; } /** * Returns the status. * * @return the status * @since 2.0.0 */ public Status getStatus() { return this.status; } /** * Returns the type. * * @return the type * @since 2.0.0 */ public EntityTypeImpl<X> getType() { return this.type; } /** * Handles the entities that have been added. * * @param entityManager * the entity manager * * @since 2.0.0 */ public void handleAdditions(EntityManagerImpl entityManager) { ManagedInstance.LOG.debug("Inspecting additions for instance {0}", this); for (int i = 0; i < this.collectionsChanged.size(); i++) { final PluralMappingEx<?, ?, ?> collection = this.collectionsChanged.get(i); if (collection instanceof PluralAssociationMappingImpl) { ((PluralAssociationMappingImpl<?, ?, ?>) collection).persistAdditions(entityManager, this); } } } /** * Handles the entities that have been orphaned. * * @param entityManager * the entity manager * * @since 2.0.0 */ public void handleOrphans(EntityManagerImpl entityManager) { ManagedInstance.LOG.debug("Inspecting orphans for instance {0}", this); for (int i = 0; i < this.collectionsChanged.size(); i++) { final PluralMappingEx<?, ?, ?> collection = this.collectionsChanged.get(i); if (collection.isAssociation()) { ((PluralAssociationMappingImpl<?, ?, ?>) collection).removeOrphans(entityManager, this); } } } /** * {@inheritDoc} * */ @Override public int hashCode() { if (this.h != 0) { return this.h; } final ManagedId<? super X> _id = this.getId(); final int prime = 31; int result = 1; ; result = (prime * result) + this.type.getRootType().getName().hashCode(); result = prime * result + ((_id == null) ? 0 : _id.hashCode()); return this.h = result; } /** * Returns if the instance has initial id. * * @return true if the instance has initial id, false otherwise * * @since 2.0.0 */ public boolean hasInitialId() { return this.hasInitialId; } /** * Returns if the instance has self update. * * @return true if the instance has self update, false otherwise * * @since 2.0.0 */ public boolean hasSelfUpdate() { if (!this.changed && (this.snapshot.size() == 0)) { return false; } if (this.collectionsChanged.size() > 0) { return true; } return this.checkUpdatedImpl(); } /** * Increments the version of the instance. * * @param connection * the connection * @param commit * true if version update should be committed immediately * @throws SQLException * thrown in case of an underlying SQL error * * @since 2.0.0 */ public void incrementVersion(Connection connection, boolean commit) throws SQLException { if (!this.type.getRootType().hasVersionAttribute()) { return; } final EntityTypeImpl<? super X> rootType = this.type.getRootType(); final BasicAttribute<? super X, ?> version = rootType.getVersionAttribute(); if (this.oldVersion == null) { switch (this.type.getVersionType()) { case SHORT: final short shortValue = (((Number) version.get(this.instance)).shortValue()); this.oldVersion = shortValue; version.set(this.instance, shortValue + 1); ManagedInstance.LOG.debug("Version upgraded instance: {0} - {1}", this, shortValue); break; case SHORT_OBJECT: final Short shortObjValue = version.get(this.instance) == null ? 0 : // Short.valueOf((((Number) version.get(this.instance)).shortValue())); this.oldVersion = shortObjValue; version.set(this.instance, shortObjValue + 1); ManagedInstance.LOG.debug("Version upgraded instance: {0} - {1}", this, shortObjValue); break; case INT: final int intValue = (((Number) version.get(this.instance)).intValue()); this.oldVersion = intValue; version.set(this.instance, intValue + 1); ManagedInstance.LOG.debug("Version upgraded instance: {0} - {1}", this, intValue); break; case INT_OBJECT: final Integer intObjValue = version.get(this.instance) == null ? 0 : // Integer.valueOf(((Number) version.get(this.instance)).intValue()); this.oldVersion = intObjValue; version.set(this.instance, intObjValue + 1); ManagedInstance.LOG.debug("Version upgraded instance: {0} - {1}", this, intObjValue); break; case LONG: final long longValue = (((Number) version.get(this.instance)).longValue()); this.oldVersion = longValue; version.set(this.instance, longValue + 1); ManagedInstance.LOG.debug("Version upgraded instance: {0} - {1}", this, longValue); break; case LONG_OBJECT: final Long longObjValue = version.get(this.instance) == null ? 0l : // Long.valueOf((((Number) version.get(this.instance)).longValue())); this.oldVersion = longObjValue; version.set(this.instance, longObjValue + 1); ManagedInstance.LOG.debug("Version upgraded instance: {0} - {1}", this, longObjValue); break; case TIMESTAMP: final Timestamp value = new Timestamp(System.currentTimeMillis()); this.oldVersion = version.get(this.instance); version.set(this.instance, value); ManagedInstance.LOG.debug("Version upgraded instance: {0} - {1}", this, value); } } if (commit) { final Object newVersion = version.get(this.instance); rootType.performVersionUpdate(connection, this, this.oldVersion, newVersion); ManagedInstance.LOG.debug("Version committed instance: {0} - {1} -> {2}", this, this.oldVersion, newVersion); this.oldVersion = null; } else { this.changed(); } } /** * Returns if attribute name <code>attributeNae</code> has been loaded. * * @param attributeName * the name of the attribute * @return true if join is loaded, false otherwise * * @since 2.0.0 */ public boolean isJoinLoaded(String attributeName) { final AbstractMapping<?, ?, ?> mapping = this.type.getRootMapping().getMapping(attributeName); if ((mapping instanceof BasicMappingImpl) || (mapping instanceof EmbeddedMappingImpl)) { return true; } if (((AssociationMappingImpl<?, ?, ?>) mapping).isEager()) { return true; } return this.joinsLoaded.contains(mapping.getPath()); } /** * Returns if the instance is loading. * * @return true if the instance is loading, false otherwise * * @since 2.0.0 */ public boolean isLoading() { return this.loading; } /** * Returns if the instance is loading from the cache. * * @return true if the instance is loading from the cache, false otherwise * * @since 2.0.0 */ public boolean isLoadingFromCache() { return this.loadingFromCache; } /** * Returns if the instance is refreshing. * * @return true if the instance is refreshing, false otherwise * * @since 2.0.0 */ public boolean isRefreshing() { return this.refreshing; } /** * Merges the instance state with the <code>entity</code>. * * @param entityManager * the entity manager * @param entity * the entity to merge * @param requiresFlush * if an implicit flush is required * @param processed * registry of processed entities * @param instances * the persisted instances * * @since 2.0.0 */ public void mergeWith(EntityManagerImpl entityManager, X entity, MutableBoolean requiresFlush, IdentityHashMap<Object, Object> processed, LinkedList<ManagedInstance<?>> instances) { this.snapshot(); for (final BasicMappingImpl<?, ?> mapping : this.type.getBasicMappings()) { mapping.set(this.instance, mapping.get(entity)); } for (final AssociationMappingImpl<?, ?, ?> association : this.type.getAssociations()) { association.mergeWith(entityManager, this, entity, requiresFlush, processed, instances); } this.checkUpdated(); } /** * Processes the associations. * * @since 2.0.0 */ public void processJoinedMappings() { ManagedInstance.LOG.debug("Post processing associations for instance {0}", this); final HashSet<String> _joinsLoaded = this.joinsLoaded; for (final PluralMappingEx<?, ?, ?> mapping : this.type.getMappingsPlural()) { final HashSet<String> joinsLoaded2 = _joinsLoaded; if (!joinsLoaded2.contains(mapping.getPath())) { if (mapping.isEager()) { mapping.load(this); } else { mapping.setLazy(this); } } } final X _instance = this.instance; final EntityManagerImpl entityManager = this.session.getEntityManager(); for (final SingularAssociationMappingImpl<?, ?> mapping : this.type.getAssociationsSingular()) { if (mapping.isEager()) { if (!_joinsLoaded.contains(mapping.getPath())) { mapping.initialize(this); } else { final Object associate = mapping.get(_instance); if (associate instanceof EnhancedInstance) { final EnhancedInstance enhancedInstance = (EnhancedInstance) associate; if (!enhancedInstance.__enhanced__$$__isInitialized()) { final ManagedInstance<?> associateManagedInstance = enhancedInstance .__enhanced__$$__getManagedInstance(); entityManager.find(associateManagedInstance.getType().getJavaType(), associateManagedInstance.getId().getId()); } } } } } } /** * Refreshes the instance from the database. * * @param entityManager * the entity manager * @param connection * the connection * @param lockMode * the lock mode * @param processed * the set of processed instances * * @since 2.0.0 */ public void refresh(EntityManagerImpl entityManager, Connection connection, LockModeType lockMode, Set<Object> processed) { ManagedInstance.LOG.debug("Refeshing instance {0}", this); this.type.performRefresh(connection, this, lockMode, processed); for (final AssociationMappingImpl<?, ?, ?> association : this.type.getAssociations()) { association.refresh(this, processed); } } /** * Resets the change status of the instance. * * @since 2.0.0 */ public void reset() { ManagedInstance.LOG.trace("Reset instance {0}", this); this.collectionsChanged.clear(); this.changed = false; this.snapshot.clear(); this.snapshot(); } /** * Marks the plural association as changed. * * @param association * the association that has changed * * @since 2.0.0 */ public void setChanged(PluralMappingEx<?, ?, ?> association) { if ((this.collectionsChanged.size() == 0) && !this.changed) { this.session.setChanged(this); } this.collectionsChanged.add(association); } /** * Sets the association as loaded. * * @param mapping * the association * * @since 2.0.0 */ public void setJoinLoaded(JoinedMapping<?, ?, ?> mapping) { this.joinsLoaded.add(mapping.getPath()); } /** * Marks the instance as loading. * * @param loading * loading to set * * @since 2.0.0 */ public void setLoading(boolean loading) { this.loading = loading; } /** * Marks the instance as loading loading from the cahce. * * @param loadingFromCache * loading to set * * @since 2.0.0 */ public void setLoadingFromCache(boolean loadingFromCache) { this.loadingFromCache = loadingFromCache; } /** * Marks the instance as refreshing. * * @param refreshing * refreshing to set * * @since 2.0.0 */ public void setRefreshing(boolean refreshing) { this.refreshing = refreshing; } /** * Sets the status. * * @param status * the status to set * @since 2.0.0 */ public void setStatus(Status status) { this.oldStatus = this.status; if (status != this.status) { ManagedInstance.LOG.debug("Instance status changing for {0}: {1} -> {2}", this, this.status, status); this.status = status; } } /** * Creates a snapshot of the entity. * * @since 2.0.0 */ private void snapshot() { ManagedInstance.LOG.trace("Snapshot generated for instance {0}", this); if (this.snapshot.size() == 0) { for (final AbstractMapping<?, ?, ?> mapping : this.type.getMappingsSingular()) { this.snapshot.put(mapping, mapping.get(this.instance)); } } } /** * Sorts the list associations. * * @since 2.0.0 */ public void sortLists() { for (final PluralMappingEx<?, ?, ?> mapping : this.type.getMappingsPluralSorted()) { mapping.sortList(this.instance); } } /** * {@inheritDoc} * */ @Override public String toString() { return "ManagedInstance [session=" + this.session // + ", type=" + this.type.getName() // + ", status=" + this.status // + ", id=" + (this.id != null ? this.id.getId() : null) + "]"; } }