Skip to content

Commit

Permalink
Implemented collection replace for basic-types.
Browse files Browse the repository at this point in the history
  • Loading branch information
Naros authored and dreab8 committed Feb 26, 2019
1 parent 27ff5dc commit 7156a72
Show file tree
Hide file tree
Showing 8 changed files with 277 additions and 1 deletion.
Expand Up @@ -8,9 +8,12 @@

import java.util.Map;

import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.mapping.Property;
import org.hibernate.metamodel.model.creation.spi.RuntimeModelCreationContext;
import org.hibernate.metamodel.model.domain.spi.AbstractPluralPersistentAttribute;
import org.hibernate.metamodel.model.domain.spi.CollectionElement;
import org.hibernate.metamodel.model.domain.spi.CollectionIndex;
import org.hibernate.metamodel.model.domain.spi.MapPersistentAttribute;
import org.hibernate.metamodel.model.domain.spi.PersistentCollectionDescriptor;
import org.hibernate.metamodel.model.domain.spi.SimpleTypeDescriptor;
Expand Down Expand Up @@ -45,4 +48,25 @@ public Class<K> getKeyJavaType() {
public SimpleTypeDescriptor<K> getKeyType() {
return (SimpleTypeDescriptor<K>) getPersistentCollectionDescriptor().getKeyDomainTypeDescriptor();
}

@Override
@SuppressWarnings("unchecked")
protected Map<K, V> replaceElements(
Map<K, V> originalValue,
Map<K, V> targetValue,
Object owner,
Map copyCache,
SessionImplementor session) {
targetValue.clear();

final CollectionIndex<K> indexDescriptor = getCollectionDescriptor().getIndexDescriptor();
final CollectionElement<V> elementDescriptor = getCollectionDescriptor().getElementDescriptor();
for ( Map.Entry<K, V> entry : originalValue.entrySet() ) {
K key = indexDescriptor.replace( entry.getKey(), null, owner, copyCache, session );
V value = elementDescriptor.replace( entry.getValue(), null, owner, copyCache, session );
targetValue.put( key, value );
}

return targetValue;
}
}
Expand Up @@ -6,23 +6,36 @@
*/
package org.hibernate.metamodel.model.domain.spi;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

import org.hibernate.Hibernate;
import org.hibernate.LockMode;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.collection.internal.AbstractPersistentCollection;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.internal.ForeignKeys;
import org.hibernate.engine.internal.NonNullableTransientDependencies;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.CollectionEntry;
import org.hibernate.engine.spi.CollectionKey;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.MarkerObject;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Property;
import org.hibernate.metamodel.model.creation.spi.RuntimeModelCreationContext;
Expand Down Expand Up @@ -455,4 +468,158 @@ public DomainType getAttributeType() {
public ForeignKeyDirection getForeignKeyDirection() {
return getPersistentCollectionDescriptor().getForeignKeyDirection();
}

@Override
public Object replace(C originalValue, C targetValue, Object owner, Map copyCache, SessionImplementor session) {
if ( originalValue == null ) {
return null;
}

if ( !Hibernate.isInitialized( originalValue ) ) {
final AbstractPersistentCollection collection = (AbstractPersistentCollection) originalValue;
if ( collection.hasQueuedOperations() ) {
collection.replaceQueuedOperationValues( getCollectionDescriptor(), copyCache );
}
return targetValue;
}

// For a null target or a target which is the same as the original,
// we need to put the merged elements into a new collection.
C result = ( targetValue == null ||
targetValue == originalValue ||
targetValue == LazyPropertyInitializer.UNFETCHED_PROPERTY ) ?
instantiateResult( originalValue ) : targetValue;

// For arrays, replaceElements() may return a different reference,
// since the array length might not match.
result = replaceElements( originalValue, result, owner, copyCache, session );

if ( originalValue == targetValue ) {
// Get the elements back into the target, making sure to handle the dirty flag.
boolean wasClean = PersistentCollection.class.isInstance( targetValue ) &&
!( (PersistentCollection) targetValue ).isDirty();

// todo (6.0) - this is a bit inefficient, no need to do a whole deep replaceElements() call
replaceElements( result, targetValue, owner, copyCache, session );
if ( wasClean ) {
( (PersistentCollection) targetValue ).clearDirty();;
}
}

return result;
}

@SuppressWarnings("unchecked")
protected C instantiateResult(C originalValue) {
// todo (6.0) - how to handle arrays? 5.x handled this slightly different.
return (C) collectionDescriptor.instantiateRaw( -1 );
}

protected C replaceElements(
C originalValue,
C targetValue,
Object owner,
Map copyCache,
SessionImplementor session) {
java.util.Collection result = (java.util.Collection) targetValue;
result.clear();

// copy elements into newly empty target collection
final CollectionElement elementDescriptor = collectionDescriptor.getElementDescriptor();
Iterator iter = ( (java.util.Collection) originalValue ).iterator();
while ( iter.hasNext() ) {
result.add( elementDescriptor.replace( iter.next(), null, owner, copyCache, session ) );
}

// If the originalValue is a PersistentCollection and that originalValue
// was not flagged as dirty, then reset the targetValue's dirty flag
// here after the copy operation
//
// One thing to note is if the originalValue was a bare collection, we
// should not reset the dirty flag because we simply do not know.
if ( originalValue instanceof PersistentCollection ) {
if ( targetValue instanceof PersistentCollection ) {
final PersistentCollection originalCollection = (PersistentCollection) originalValue;
final PersistentCollection targetCollection = (PersistentCollection) targetValue;

preserveSnapshot( originalCollection, targetCollection, owner, copyCache, session );

if ( !originalCollection.isDirty() ) {
targetCollection.clearDirty();
}
}
}

return (C) result;
}

protected void preserveSnapshot(
PersistentCollection originalCollection,
PersistentCollection targetCollection,
Object owner,
Map copyCache,
SessionImplementor session) {

// todo (6.0) - is it possible to refactor this code to subtypes?

Object originalValue = originalCollection.getStoredSnapshot();
Object targetValue = targetCollection.getStoredSnapshot();
Object result;

final CollectionElement elementDescriptor = getCollectionDescriptor().getElementDescriptor();

if ( originalValue instanceof List ) {
result = new ArrayList<>( ( (List) originalValue ).size() );
for ( Object entry : (List) originalValue ) {
( (List) result ).add( elementDescriptor.replace( entry, null, owner, copyCache, session ) );
}
}
else if ( originalValue instanceof Map ) {
if ( originalValue instanceof SortedMap ) {
result = new TreeMap<>( ( (SortedMap) originalValue ).comparator() );
}
else {
result = new HashMap<>(
CollectionHelper.determineProperSizing( ( (Map) originalValue ).size() ),
CollectionHelper.LOAD_FACTOR
);
}

for ( Map.Entry<?, ?> entry : ( (Map<?, ?>) originalValue ).entrySet() ) {
Object key = entry.getKey();
Object value = entry.getValue();
Object resultSnapshotValue = ( targetValue == null )
? null
: ( (Map<?, ?>) targetValue ).get( key );

Object newValue = elementDescriptor.replace( value, resultSnapshotValue,owner, copyCache, session );

if ( key == value ) {
( (Map) result ).put( newValue, newValue );

}
else {
( (Map) result ).put( key, newValue );
}
}

}
else if ( originalValue instanceof Object[] ) {
Object[] arr = (Object[]) originalValue;
for ( int i = 0; i < arr.length; i++ ) {
arr[i] = elementDescriptor.replace( arr[i], null, owner, copyCache, session );
}
result = originalValue;

}
else {
// retain the same snapshot
result = targetValue;
}

CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( targetCollection );
if ( entry != null ) {
entry.resetStoredSnapshot( targetCollection, (Serializable) result );
}
}
}
Expand Up @@ -6,6 +6,9 @@
*/
package org.hibernate.metamodel.model.domain.spi;

import java.util.Map;

import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.metamodel.model.domain.CollectionDomainType;
import org.hibernate.metamodel.model.relational.spi.Table;
import org.hibernate.sql.ast.produce.spi.TableReferenceContributor;
Expand Down Expand Up @@ -42,4 +45,15 @@ default Class<J> getJavaType() {
boolean hasNotNullColumns();

boolean isMutable();

// todo (6.0) - should this be moved into a super contract?
default J replace(J originalValue, J targetValue, Object owner, Map copyCache, SessionImplementor session) {
return getJavaTypeDescriptor().getMutabilityPlan().replace(
originalValue,
targetValue,
owner,
copyCache,
session
);
}
}
Expand Up @@ -7,7 +7,9 @@
package org.hibernate.metamodel.model.domain.spi;

import java.util.List;
import java.util.Map;

import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.metamodel.model.relational.spi.Column;
import org.hibernate.sql.ast.produce.spi.TableReferenceContributor;

Expand Down Expand Up @@ -42,4 +44,15 @@ default boolean canContainSubGraphs() {
boolean isSettable();

int getBaseIndex();

// todo (6.0) - should this be moved into a super contract?
default J replace(J originalValue, J targetValue, Object owner, Map copyCache, SessionImplementor session) {
return getJavaTypeDescriptor().getMutabilityPlan().replace(
originalValue,
targetValue,
owner,
copyCache,
session
);
}
}
Expand Up @@ -117,7 +117,7 @@ default Object replace(
Object owner,
Map copyCache,
SessionImplementor session) {
throw new NotYetImplementedFor6Exception();
throw new NotYetImplementedFor6Exception( getClass() );
}

default Object replace(
Expand Down
Expand Up @@ -7,6 +7,10 @@
package org.hibernate.type.descriptor.java;

import java.io.Serializable;
import java.util.Map;

import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.spi.SessionImplementor;

/**
* Describes the mutability aspects of a Java type. The term mutability refers to the fact that generally speaking
Expand Down Expand Up @@ -54,4 +58,13 @@ public interface MutabilityPlan<T> extends Serializable {
* @see #disassemble
*/
T assemble(Serializable cached);

default T replace(
T originalValue,
T targetValue,
Object owner,
Map copyCache,
SessionImplementor session) {
throw new NotYetImplementedFor6Exception( getClass() );
}
}
Expand Up @@ -7,7 +7,11 @@
package org.hibernate.type.descriptor.java.spi;

import java.io.Serializable;
import java.util.Map;

import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.type.descriptor.java.MutabilityPlan;

/**
Expand Down Expand Up @@ -38,4 +42,21 @@ public Serializable disassemble(T value) {
public T assemble(Serializable cached) {
return (T) cached;
}

@Override
public T replace(
T originalValue,
T targetValue,
Object owner,
Map copyCache,
SessionImplementor session) {
if ( originalValue == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
// todo (6.0) - Is this scenario possible?
throw new NotYetImplementedFor6Exception( getClass() );
// return targetValue;
}
else {
return originalValue;
}
}
}
Expand Up @@ -7,7 +7,10 @@
package org.hibernate.type.internal;

import java.io.Serializable;
import java.util.Map;

import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.MutabilityPlan;

Expand Down Expand Up @@ -42,4 +45,25 @@ public Serializable disassemble(T value) {
public T assemble(Serializable cached) {
return (T) basicType.assemble( cached );
}

@Override
@SuppressWarnings("unchecked")
public T replace(
T originalValue,
T targetValue,
Object owner,
Map copyCache,
SessionImplementor session) {
if ( originalValue == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
return targetValue;
}
else if ( !isMutable() ||
( targetValue == LazyPropertyInitializer.UNFETCHED_PROPERTY &&
basicType.getJavaTypeDescriptor().areEqual( originalValue, targetValue ) ) ) {
return originalValue;
}
else {
return deepCopy( originalValue );
}
}
}

0 comments on commit 7156a72

Please sign in to comment.