Skip to content

Commit

Permalink
HHH-12054 - Handle UNFETCHED_PROPERTY in CollectionType.replace()
Browse files Browse the repository at this point in the history
  • Loading branch information
barreiro authored and vladmihalcea committed Nov 16, 2017
1 parent d2a19c9 commit ae8c365
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 22 deletions.
Expand Up @@ -71,10 +71,11 @@ protected MutabilityPlan<T> getMutabilityPlan() {
}

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 {
Expand Down
Expand Up @@ -682,7 +682,10 @@ public Object replace(

// 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
Expand Down
19 changes: 1 addition & 18 deletions hibernate-core/src/main/java/org/hibernate/type/TypeHelper.java
Expand Up @@ -156,26 +156,9 @@ public static Object[] replace(
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 );
}
Expand Down
@@ -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;
}
}
}
Expand Up @@ -20,6 +20,7 @@
import java.util.SimpleTimeZone;
import java.util.TimeZone;

import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.junit.Before;
import org.junit.Test;

Expand Down Expand Up @@ -64,6 +65,8 @@
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;

/**
Expand Down Expand Up @@ -365,6 +368,10 @@ protected <T> void runBasicTests(AbstractSingleColumnStandardBasicType<T> type,

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 ) );
Expand Down

0 comments on commit ae8c365

Please sign in to comment.