HHH-12054 - Handle UNFETCHED_PROPERTY in CollectionType.replace()

This commit is contained in:
barreiro 2017-10-30 13:53:37 +00:00 committed by Vlad Mihalcea
parent d2a19c9a77
commit ae8c365bf5
5 changed files with 132 additions and 22 deletions

View File

@ -71,10 +71,11 @@ public abstract class AbstractStandardBasicType<T>
}
protected T getReplacement(T original, T target, SharedSessionContractImplementor session) {
if ( !isMutable() ) {
return original;
if ( original == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
return target;
}
else if ( isEqual( original, target ) ) {
else if ( !isMutable() ||
( target != LazyPropertyInitializer.UNFETCHED_PROPERTY && isEqual( original, target ) ) ) {
return original;
}
else {

View File

@ -682,7 +682,10 @@ public abstract class CollectionType extends AbstractType implements Association
// for a null target, or a target which is the same as the original, we
// need to put the merged elements in a new collection
Object result = target == null || target == original ? instantiateResult( original ) : target;
Object result = ( target == null ||
target == original ||
target == LazyPropertyInitializer.UNFETCHED_PROPERTY ) ?
instantiateResult( original ) : target;
//for arrays, replaceElements() may return a different reference, since
//the array length might not match

View File

@ -156,26 +156,9 @@ public class TypeHelper {
final Map copyCache) {
Object[] copied = new Object[original.length];
for ( int i = 0; i < types.length; i++ ) {
if ( original[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY
|| original[i] == PropertyAccessStrategyBackRefImpl.UNKNOWN ) {
if ( original[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY || original[i] == PropertyAccessStrategyBackRefImpl.UNKNOWN ) {
copied[i] = target[i];
}
else if ( target[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
// Should be no need to check for target[i] == PropertyAccessStrategyBackRefImpl.UNKNOWN
// because PropertyAccessStrategyBackRefImpl.get( object ) returns
// PropertyAccessStrategyBackRefImpl.UNKNOWN, so target[i] == original[i].
//
// We know from above that original[i] != LazyPropertyInitializer.UNFETCHED_PROPERTY &&
// original[i] != PropertyAccessStrategyBackRefImpl.UNKNOWN;
// This is a case where the entity being merged has a lazy property
// that has been initialized. Copy the initialized value from original.
if ( types[i].isMutable() ) {
copied[i] = types[i].deepCopy( original[i], session.getFactory() );
}
else {
copied[i] = original[i];
}
}
else {
copied[i] = types[i].replace( original[i], target[i], session, owner, copyCache );
}

View File

@ -0,0 +1,116 @@
/*
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.bytecode.enhancement.cascade;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.util.ArrayList;
import java.util.List;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
/**
* @author Luis Barreiro
*/
@TestForIssue( jiraKey = "HHH-10254" )
@RunWith( BytecodeEnhancerRunner.class )
public class CascadeDetachedTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[]{Author.class, Book.class};
}
@Test
public void test() {
Book book = new Book( "978-1118063330", "Operating System Concepts 9th Edition" );
book.addAuthor( new Author( "Abraham", "Silberschatz", new char[] { 'a', 'b' } ) );
book.addAuthor( new Author( "Peter", "Galvin", new char[] { 'c', 'd' } ) );
book.addAuthor( new Author( "Greg", "Gagne", new char[] { 'e', 'f' } ) );
doInJPA( this::sessionFactory, em -> {
em.persist( book );
} );
doInJPA( this::sessionFactory, em -> {
em.merge( book );
} );
}
// --- //
@Entity
@Table( name = "BOOK" )
public static class Book {
@Id
@GeneratedValue( strategy = GenerationType.IDENTITY )
Long id;
String isbn;
String title;
@OneToMany( cascade = CascadeType.ALL, mappedBy = "book" )
List<Author> authors = new ArrayList<>();
public Book() {
}
public Book(String isbn, String title) {
this.isbn = isbn;
this.title = title;
}
public void addAuthor(Author author) {
authors.add( author );
author.book = this;
}
}
@Entity
@Table( name = "AUTHOR" )
public static class Author {
@Id
@GeneratedValue( strategy = GenerationType.IDENTITY )
Long id;
String firstName;
String lastName;
@ManyToOne( fetch = FetchType.LAZY )
@JoinColumn
Book book;
@Basic( fetch = FetchType.LAZY )
char[] charArrayCode;
public Author() {
}
public Author(String firstName, String lastName, char[] charArrayCode) {
this.firstName = firstName;
this.lastName = lastName;
this.charArrayCode = charArrayCode;
}
}
}

View File

@ -20,6 +20,7 @@ import java.util.Locale;
import java.util.SimpleTimeZone;
import java.util.TimeZone;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.junit.Before;
import org.junit.Test;
@ -64,6 +65,8 @@ import org.hibernate.type.YesNoType;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
/**
@ -365,6 +368,10 @@ public class TypeTest extends BaseUnitTestCase {
assertTrue( original == type.replace( original, copy, null, null, null ) );
// following tests assert that types work with properties not yet loaded in bytecode enhanced entities
assertSame( copy, type.replace( LazyPropertyInitializer.UNFETCHED_PROPERTY, copy, null, null, null ) );
assertNotEquals( LazyPropertyInitializer.UNFETCHED_PROPERTY, type.replace( original, LazyPropertyInitializer.UNFETCHED_PROPERTY, null, null, null ) );
assertTrue( type.isSame( original, copy ) );
assertTrue( type.isEqual( original, copy ) );
assertTrue( type.isEqual( original, copy ) );