Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.hibernate.event.internal.MergeContext Maven / Gradle / Ivy
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or .
*/
package org.hibernate.event.internal;
import java.util.Collection;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
import org.hibernate.event.spi.EntityCopyObserver;
import org.hibernate.event.spi.EventSource;
import org.hibernate.pretty.MessageHelper;
import org.jboss.logging.Logger;
/**
* MergeContext is a Map implementation that is intended to be used by a merge
* event listener to keep track of each entity being merged and their corresponding
* managed result. Entities to be merged may to be added to the MergeContext before
* the merge operation has cascaded to that entity.
*
* "Merge entity" and "mergeEntity" method parameter refer to an entity that is (or will be)
* merged via {@link org.hibernate.event.spi.EventSource#merge(Object mergeEntity)}.
*
* "Managed entity" and "managedEntity" method parameter refer to the managed entity that is
* the result of merging an entity.
*
* A merge entity can be transient, detached, or managed. If it is managed, then it must be
* the same as its associated entity result.
*
* If {@link #put(Object mergeEntity, Object managedEntity)} is called, and this
* MergeContext already contains an entry with a different entity as the key, but
* with the same (managedEntity) value, this means that multiple entity representations
* for the same persistent entity are being merged. If this happens,
* {@link org.hibernate.event.spi.EntityCopyObserver#entityCopyDetected(
* Object managedEntity, Object mergeEntity1, Object mergeEntity2, org.hibernate.event.spi.EventSource)}
* will be called. It is up to that method to determine the property course of
* action for this situation.
*
* There are several restrictions.
*
* Methods that return collections (e.g., {@link #keySet()},
* {@link #values()}, {@link #entrySet()}) return an
* unmodifiable view of the collection;
* If {@link #put(Object mergeEntity, Object) managedEntity} or
* {@link #put(Object mergeEntity, Object managedEntity, boolean isOperatedOn)}
* is executed and this MergeMap already contains a cross-reference for
* mergeEntity
, then managedEntity
must be the
* same as what is already associated with mergeEntity
in this
* MergeContext.
*
* If {@link #putAll(Map map)} is executed, the previous restriction
* applies to each entry in the Map;
* The {@link #remove(Object)} operation is not supported;
* The only way to remove data from a MergeContext is by calling
* {@link #clear()};
* the Map returned by {@link #invertMap()} will only contain the
* managed-to-merge entity cross-reference to its "newest"
* (most recently added) merge entity.
*
*
* The following method is intended to be used by a merge event listener (and other
* classes) in the same package to add a merge entity and its corresponding
* managed entity to a MergeContext and indicate if the merge operation is
* being performed on the merge entity yet.
* {@link MergeContext#put(Object mergeEntity, Object managedEntity, boolean isOperatedOn)}
*
* The following method is intended to be used by a merge event listener (and other
* classes) in the same package to indicate whether the merge operation is being
* performed on a merge entity already in the MergeContext:
* {@link MergeContext#setOperatedOn(Object mergeEntity, boolean isOperatedOn)}
*
* @author Gail Badner
*/
public class MergeContext implements Map {
private static final Logger LOG = Logger.getLogger( MergeContext.class );
private final EventSource session;
private final EntityCopyObserver entityCopyObserver;
private Map mergeToManagedEntityXref = new IdentityHashMap(10);
// key is an entity to be merged;
// value is the associated managed entity (result) in the persistence context.
private Map managedToMergeEntityXref = new IdentityHashMap( 10 );
// maintains the inverse of the mergeToManagedEntityXref for performance reasons.
// key is the managed entity result in the persistence context.
// value is the associated entity to be merged; if multiple
// representations of the same persistent entity are added to the MergeContext,
// value will be the most recently added merge entity that is
// associated with the managed entity.
// TODO: merge mergeEntityToOperatedOnFlagMap into mergeToManagedEntityXref, since they have the same key.
// need to check if this would hurt performance.
private Map mergeEntityToOperatedOnFlagMap = new IdentityHashMap( 10 );
// key is a merge entity;
// value is a flag indicating if the merge entity is currently in the merge process.
public MergeContext(EventSource session, EntityCopyObserver entityCopyObserver){
this.session = session;
this.entityCopyObserver = entityCopyObserver;
}
/**
* Clears the MergeContext.
*/
public void clear() {
mergeToManagedEntityXref.clear();
managedToMergeEntityXref.clear();
mergeEntityToOperatedOnFlagMap.clear();
}
/**
* Returns true if this MergeContext contains a cross-reference for the specified merge entity
* to a managed entity result.
*
* @param mergeEntity must be non-null
* @return true if this MergeContext contains a cross-reference for the specified merge entity
* @throws NullPointerException if mergeEntity is null
*/
public boolean containsKey(Object mergeEntity) {
if ( mergeEntity == null ) {
throw new NullPointerException( "null entities are not supported by " + getClass().getName() );
}
return mergeToManagedEntityXref.containsKey( mergeEntity );
}
/**
* Returns true if this MergeContext contains a cross-reference from the specified managed entity
* to a merge entity.
* @param managedEntity must be non-null
* @return true if this MergeContext contains a cross-reference from the specified managed entity
* to a merge entity
* @throws NullPointerException if managedEntity is null
*/
public boolean containsValue(Object managedEntity) {
if ( managedEntity == null ) {
throw new NullPointerException( "null copies are not supported by " + getClass().getName() );
}
return managedToMergeEntityXref.containsKey( managedEntity );
}
/**
* Returns an unmodifiable set view of the merge-to-managed entity cross-references contained in this MergeContext.
* @return an unmodifiable set view of the merge-to-managed entity cross-references contained in this MergeContext
*
* @see Collections#unmodifiableSet(java.util.Set)
*/
public Set entrySet() {
return Collections.unmodifiableSet( mergeToManagedEntityXref.entrySet() );
}
/**
* Returns the managed entity associated with the specified merge Entity.
* @param mergeEntity the merge entity; must be non-null
* @return the managed entity associated with the specified merge Entity
* @throws NullPointerException if mergeEntity is null
*/
public Object get(Object mergeEntity) {
if ( mergeEntity == null ) {
throw new NullPointerException( "null entities are not supported by " + getClass().getName() );
}
return mergeToManagedEntityXref.get( mergeEntity );
}
/**
* Returns true if this MergeContext contains no merge-to-managed entity cross-references.
* @return true if this MergeContext contains no merge-to-managed entity cross-references.
*/
public boolean isEmpty() {
return mergeToManagedEntityXref.isEmpty();
}
/**
* Returns an unmodifiable set view of the merge entities contained in this MergeContext
* @return an unmodifiable set view of the merge entities contained in this MergeContext
*
* @see Collections#unmodifiableSet(java.util.Set)
*/
public Set keySet() {
return Collections.unmodifiableSet( mergeToManagedEntityXref.keySet() );
}
/**
* Associates the specified merge entity with the specified managed entity result in this MergeContext.
* If this MergeContext already contains a cross-reference for mergeEntity
when this
* method is called, then managedEntity
must be the same as what is already associated
* with mergeEntity
.
*
* This method assumes that the merge process is not yet operating on mergeEntity
.
* Later when mergeEntity
enters the merge process, {@link #setOperatedOn(Object, boolean)}
* should be called.
*
* @param mergeEntity the merge entity; must be non-null
* @param managedEntity the managed entity result; must be non-null
* @return previous managed entity associated with specified merge entity, or null if
* there was no mapping for mergeEntity.
* @throws NullPointerException if mergeEntity or managedEntity is null
* @throws IllegalArgumentException if managedEntity
is not the same as the previous
* managed entity associated with merge entity
* @throws IllegalStateException if internal cross-references are out of sync,
*/
public Object put(Object mergeEntity, Object managedEntity) {
return put( mergeEntity, managedEntity, Boolean.FALSE );
}
/**
* Associates the specified merge entity with the specified managed entity in this MergeContext.
* If this MergeContext already contains a cross-reference for mergeEntity
when this
* method is called, then managedEntity
must be the same as what is already associated
* with mergeEntity
.
*
* @param mergeEntity the merge entity; must be non-null
* @param managedEntity the managed entity; must be non-null
* @param isOperatedOn indicates if the merge operation is performed on the mergeEntity.
*
* @return previous managed entity associated with specified merge entity, or null if
* there was no mapping for mergeEntity.
* @throws NullPointerException if mergeEntity or managedEntity is null
* @throws IllegalArgumentException if managedEntity
is not the same as the previous
* managed entity associated with mergeEntity
* @throws IllegalStateException if internal cross-references are out of sync,
*/
public Object put(Object mergeEntity, Object managedEntity, boolean isOperatedOn) {
if ( mergeEntity == null || managedEntity == null ) {
throw new NullPointerException( "null merge and managed entities are not supported by " + getClass().getName() );
}
Object oldManagedEntity = mergeToManagedEntityXref.put( mergeEntity, managedEntity );
Boolean oldOperatedOn = mergeEntityToOperatedOnFlagMap.put( mergeEntity, isOperatedOn );
// If managedEntity already corresponds with a different merge entity, that means
// that there are multiple entities being merged that correspond with managedEntity.
// In the following, oldMergeEntity will be replaced with mergeEntity in managedToMergeEntityXref.
Object oldMergeEntity = managedToMergeEntityXref.put( managedEntity, mergeEntity );
if ( oldManagedEntity == null ) {
// this is a new mapping for mergeEntity in mergeToManagedEntityXref
if ( oldMergeEntity != null ) {
// oldMergeEntity was a different merge entity with the same corresponding managed entity;
entityCopyObserver.entityCopyDetected(
managedEntity,
mergeEntity,
oldMergeEntity,
session
);
}
if ( oldOperatedOn != null ) {
throw new IllegalStateException(
"MergeContext#mergeEntityToOperatedOnFlagMap contains a merge entity " + printEntity( mergeEntity )
+ ", but MergeContext#mergeToManagedEntityXref does not."
);
}
}
else {
// mergeEntity was already mapped in mergeToManagedEntityXref
if ( oldManagedEntity != managedEntity ) {
throw new IllegalArgumentException(
"Error occurred while storing a merge Entity " + printEntity( mergeEntity )
+ ". It was previously associated with managed entity " + printEntity( oldManagedEntity )
+ ". Attempted to replace managed entity with " + printEntity( managedEntity )
);
}
if ( oldOperatedOn == null ) {
throw new IllegalStateException(
"MergeContext#mergeToManagedEntityXref contained a merge entity " + printEntity( mergeEntity )
+ ", but MergeContext#mergeEntityToOperatedOnFlagMap did not."
);
}
}
return oldManagedEntity;
}
/**
* Copies all of the mappings from the specified Map to this MergeContext.
* The key and value for each entry in map
is subject to the same
* restrictions as {@link #put(Object mergeEntity, Object managedEntity)}.
*
* This method assumes that the merge process is not yet operating on any merge entity
*
* @param map keys and values must be non-null
* @throws NullPointerException if any key or value is null
* @throws IllegalArgumentException if a key in map
was already in this MergeContext
* but associated value in map
is different from the previous value in this MergeContext.
* @throws IllegalStateException if internal cross-references are out of sync,
*/
public void putAll(Map map) {
for ( Object o : map.entrySet() ) {
Entry entry = (Entry) o;
put( entry.getKey(), entry.getValue() );
}
}
/**
* The remove operation is not supported.
* @param mergeEntity the merge entity.
* @throws UnsupportedOperationException if called.
*/
public Object remove(Object mergeEntity) {
throw new UnsupportedOperationException(
String.format( "Operation not supported: %s.remove()", getClass().getName() )
);
}
/**
* Returns the number of merge-to-managed entity cross-references in this MergeContext
* @return the number of merge-to-managed entity cross-references in this MergeContext
*/
public int size() {
return mergeToManagedEntityXref.size();
}
/**
* Returns an unmodifiable Set view of managed entities contained in this MergeContext.
* @return an unmodifiable Set view of managed entities contained in this MergeContext
*
* @see Collections#unmodifiableSet(java.util.Set)
*/
public Collection values() {
return Collections.unmodifiableSet( managedToMergeEntityXref.keySet() );
}
/**
* Returns true if the listener is performing the merge operation on the specified merge entity.
* @param mergeEntity the merge entity; must be non-null
* @return true if the listener is performing the merge operation on the specified merge entity;
* false, if there is no mapping for mergeEntity.
* @throws NullPointerException if mergeEntity is null
*/
public boolean isOperatedOn(Object mergeEntity) {
if ( mergeEntity == null ) {
throw new NullPointerException( "null merge entities are not supported by " + getClass().getName() );
}
final Boolean isOperatedOn = mergeEntityToOperatedOnFlagMap.get( mergeEntity );
return isOperatedOn == null ? false : isOperatedOn;
}
/**
* Set flag to indicate if the listener is performing the merge operation on the specified merge entity.
* @param mergeEntity must be non-null and this MergeContext must contain a cross-reference for mergeEntity
* to a managed entity
* @throws NullPointerException if mergeEntity is null
* @throws IllegalStateException if this MergeContext does not contain a a cross-reference for mergeEntity
*/
public void setOperatedOn(Object mergeEntity, boolean isOperatedOn) {
if ( mergeEntity == null ) {
throw new NullPointerException( "null entities are not supported by " + getClass().getName() );
}
if ( ! mergeEntityToOperatedOnFlagMap.containsKey( mergeEntity ) ||
! mergeToManagedEntityXref.containsKey( mergeEntity ) ) {
throw new IllegalStateException( "called MergeContext#setOperatedOn() for mergeEntity not found in MergeContext" );
}
mergeEntityToOperatedOnFlagMap.put( mergeEntity, isOperatedOn );
}
/**
* Returns an unmodifiable map view of the managed-to-merge entity
* cross-references.
*
* The returned Map will contain a cross-reference from each managed entity
* to the most recently associated merge entity that was most recently put in the MergeContext.
*
* @return an unmodifiable map view of the managed-to-merge entity cross-references.
*
* @see Collections#unmodifiableMap(java.util.Map)
*/
public Map invertMap() {
return Collections.unmodifiableMap( managedToMergeEntityXref );
}
private String printEntity(Object entity) {
if ( session.getPersistenceContextInternal().getEntry( entity ) != null ) {
return MessageHelper.infoString( session.getEntityName( entity ), session.getIdentifier( entity ) );
}
// Entity was not found in current persistence context. Use Object#toString() method.
return "[" + entity + "]";
}
}