HHH-12054 - Handle UNFETCHED_PROPERTY in CollectionType.replace()
This commit is contained in:
parent
d2a19c9a77
commit
ae8c365bf5
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 ) );
|
||||
|
|
Loading…
Reference in New Issue