HHH-5855 : Merge causes a duplicated "insert" of a child entity in lazy collection
(cherry picked from commit efa72a8333
)
This commit is contained in:
parent
b540ad43ee
commit
c1934b72ed
|
@ -14,6 +14,7 @@ import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.naming.NamingException;
|
import javax.naming.NamingException;
|
||||||
|
|
||||||
|
@ -474,6 +475,20 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
|
||||||
dirty = true;
|
dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace entity instances with copy in {@code copyCache}/.
|
||||||
|
*
|
||||||
|
* @param copyCache - mapping from entity in the process of being
|
||||||
|
* merged to managed copy.
|
||||||
|
*/
|
||||||
|
public final void replaceQueuedOperationValues(CollectionPersister persister, Map copyCache) {
|
||||||
|
for ( DelayedOperation operation : operationQueue ) {
|
||||||
|
if ( ValueDelayedOperation.class.isInstance( operation ) ) {
|
||||||
|
( (ValueDelayedOperation) operation ).replace( persister, copyCache );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* After reading all existing elements from the database,
|
* After reading all existing elements from the database,
|
||||||
* add the queued elements to the underlying collection.
|
* add the queued elements to the underlying collection.
|
||||||
|
@ -1133,6 +1148,40 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
|
||||||
public Object getOrphan();
|
public Object getOrphan();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected interface ValueDelayedOperation extends DelayedOperation {
|
||||||
|
void replace(CollectionPersister collectionPersister, Map copyCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract class AbstractValueDelayedOperation implements ValueDelayedOperation {
|
||||||
|
private Object addedValue;
|
||||||
|
private Object orphan;
|
||||||
|
|
||||||
|
protected AbstractValueDelayedOperation(Object addedValue, Object orphan) {
|
||||||
|
this.addedValue = addedValue;
|
||||||
|
this.orphan = orphan;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void replace(CollectionPersister persister, Map copyCache) {
|
||||||
|
if ( addedValue != null ) {
|
||||||
|
addedValue = getReplacement( persister.getElementType(), addedValue, copyCache );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final Object getReplacement(Type type, Object current, Map copyCache) {
|
||||||
|
return type.replace( current, null, session, owner, copyCache );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Object getAddedInstance() {
|
||||||
|
return addedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Object getOrphan() {
|
||||||
|
return orphan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a collection of entity instances that used to
|
* Given a collection of entity instances that used to
|
||||||
* belong to the collection, and a collection of instances
|
* belong to the collection, and a collection of instances
|
||||||
|
|
|
@ -546,28 +546,16 @@ public class PersistentBag extends AbstractPersistentCollection implements List
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class SimpleAdd implements DelayedOperation {
|
final class SimpleAdd extends AbstractValueDelayedOperation {
|
||||||
private Object value;
|
|
||||||
|
|
||||||
public SimpleAdd(Object value) {
|
public SimpleAdd(Object addedValue) {
|
||||||
this.value = value;
|
super( addedValue, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void operate() {
|
public void operate() {
|
||||||
bag.add( value );
|
bag.add( getAddedInstance() );
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getAddedInstance() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getOrphan() {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
|
@ -511,131 +510,81 @@ public class PersistentList extends AbstractPersistentCollection implements List
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class SimpleAdd implements DelayedOperation {
|
final class SimpleAdd extends AbstractValueDelayedOperation {
|
||||||
private Object value;
|
|
||||||
|
|
||||||
public SimpleAdd(Object value) {
|
public SimpleAdd(Object addedValue) {
|
||||||
this.value = value;
|
super( addedValue, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void operate() {
|
public void operate() {
|
||||||
list.add( value );
|
list.add( getAddedInstance() );
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getAddedInstance() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getOrphan() {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class Add implements DelayedOperation {
|
abstract class AbstractListValueDelayedOperation extends AbstractValueDelayedOperation {
|
||||||
private int index;
|
private int index;
|
||||||
private Object value;
|
|
||||||
|
|
||||||
public Add(int index, Object value) {
|
AbstractListValueDelayedOperation(Integer index, Object addedValue, Object orphan) {
|
||||||
|
super( addedValue, orphan );
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.value = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected final int getIndex() {
|
||||||
@SuppressWarnings("unchecked")
|
return index;
|
||||||
public void operate() {
|
|
||||||
list.add( index, value );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getAddedInstance() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getOrphan() {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class Set implements DelayedOperation {
|
final class Add extends AbstractListValueDelayedOperation {
|
||||||
private int index;
|
|
||||||
private Object value;
|
|
||||||
private Object old;
|
|
||||||
|
|
||||||
public Set(int index, Object value, Object old) {
|
public Add(int index, Object addedValue) {
|
||||||
this.index = index;
|
super( index, addedValue, null );
|
||||||
this.value = value;
|
|
||||||
this.old = old;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void operate() {
|
public void operate() {
|
||||||
list.set( index, value );
|
list.add( getIndex(), getAddedInstance() );
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getAddedInstance() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getOrphan() {
|
|
||||||
return old;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class Remove implements DelayedOperation {
|
final class Set extends AbstractListValueDelayedOperation {
|
||||||
private int index;
|
|
||||||
private Object old;
|
|
||||||
|
|
||||||
public Remove(int index, Object old) {
|
public Set(int index, Object addedValue, Object orphan) {
|
||||||
this.index = index;
|
super( index, addedValue, orphan );
|
||||||
this.old = old;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void operate() {
|
public void operate() {
|
||||||
list.remove( index );
|
list.set( getIndex(), getAddedInstance() );
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getAddedInstance() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getOrphan() {
|
|
||||||
return old;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class SimpleRemove implements DelayedOperation {
|
final class Remove extends AbstractListValueDelayedOperation {
|
||||||
private Object value;
|
|
||||||
|
|
||||||
public SimpleRemove(Object value) {
|
public Remove(int index, Object orphan) {
|
||||||
this.value = value;
|
super( index, null, orphan );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void operate() {
|
public void operate() {
|
||||||
list.remove( value );
|
list.remove( getIndex() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class SimpleRemove extends AbstractValueDelayedOperation {
|
||||||
|
|
||||||
|
public SimpleRemove(Object orphan) {
|
||||||
|
super( null, orphan );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getAddedInstance() {
|
@SuppressWarnings("unchecked")
|
||||||
return null;
|
public void operate() {
|
||||||
}
|
list.remove( getOrphan() );
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getOrphan() {
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -570,59 +570,42 @@ public class PersistentMap extends AbstractPersistentCollection implements Map {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class Put implements DelayedOperation {
|
abstract class AbstractMapValueDelayedOperation extends AbstractValueDelayedOperation {
|
||||||
private Object index;
|
private Object index;
|
||||||
private Object value;
|
|
||||||
private Object old;
|
protected AbstractMapValueDelayedOperation(Object index, Object addedValue, Object orphan) {
|
||||||
|
super( addedValue, orphan );
|
||||||
public Put(Object index, Object value, Object old) {
|
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.value = value;
|
|
||||||
this.old = old;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected final Object getIndex() {
|
||||||
@SuppressWarnings("unchecked")
|
return index;
|
||||||
public void operate() {
|
|
||||||
map.put( index, value );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public Object getAddedInstance() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public Object getOrphan() {
|
|
||||||
return old;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class Remove implements DelayedOperation {
|
final class Put extends AbstractMapValueDelayedOperation {
|
||||||
private Object index;
|
|
||||||
private Object old;
|
public Put(Object index, Object addedValue, Object orphan) {
|
||||||
|
super( index, addedValue, orphan );
|
||||||
public Remove(Object index, Object old) {
|
|
||||||
this.index = index;
|
|
||||||
this.old = old;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void operate() {
|
public void operate() {
|
||||||
map.remove( index );
|
map.put( getIndex(), getAddedInstance() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class Remove extends AbstractMapValueDelayedOperation {
|
||||||
|
|
||||||
|
public Remove(Object index, Object orphan) {
|
||||||
|
super( index, null, orphan );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getAddedInstance() {
|
@SuppressWarnings("unchecked")
|
||||||
return null;
|
public void operate() {
|
||||||
}
|
map.remove( getIndex() );
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getOrphan() {
|
|
||||||
return old;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
|
@ -460,51 +461,29 @@ public class PersistentSet extends AbstractPersistentCollection implements java.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class SimpleAdd implements DelayedOperation {
|
final class SimpleAdd extends AbstractValueDelayedOperation {
|
||||||
private Object value;
|
|
||||||
|
public SimpleAdd(Object addedValue) {
|
||||||
public SimpleAdd(Object value) {
|
super( addedValue, null );
|
||||||
this.value = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void operate() {
|
public void operate() {
|
||||||
set.add( value );
|
set.add( getAddedInstance() );
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getAddedInstance() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getOrphan() {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class SimpleRemove implements DelayedOperation {
|
final class SimpleRemove extends AbstractValueDelayedOperation {
|
||||||
private Object value;
|
|
||||||
|
public SimpleRemove(Object orphan) {
|
||||||
public SimpleRemove(Object value) {
|
super( null, orphan );
|
||||||
this.value = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void operate() {
|
public void operate() {
|
||||||
set.remove( value );
|
set.remove( getOrphan() );
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getAddedInstance() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getOrphan() {
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.hibernate.EntityMode;
|
||||||
import org.hibernate.Hibernate;
|
import org.hibernate.Hibernate;
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
|
import org.hibernate.collection.internal.AbstractPersistentCollection;
|
||||||
import org.hibernate.collection.spi.PersistentCollection;
|
import org.hibernate.collection.spi.PersistentCollection;
|
||||||
import org.hibernate.engine.jdbc.Size;
|
import org.hibernate.engine.jdbc.Size;
|
||||||
import org.hibernate.engine.spi.CollectionEntry;
|
import org.hibernate.engine.spi.CollectionEntry;
|
||||||
|
@ -47,9 +48,6 @@ import org.hibernate.proxy.LazyInitializer;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
import org.dom4j.Element;
|
|
||||||
import org.dom4j.Node;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A type that handles Hibernate <tt>PersistentCollection</tt>s (including arrays).
|
* A type that handles Hibernate <tt>PersistentCollection</tt>s (including arrays).
|
||||||
*
|
*
|
||||||
|
@ -135,7 +133,7 @@ public abstract class CollectionType extends AbstractType implements Association
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object nullSafeGet(ResultSet rs, String name, SessionImplementor session, Object owner) throws SQLException {
|
public Object nullSafeGet(ResultSet rs, String name, SessionImplementor session, Object owner) throws SQLException {
|
||||||
return nullSafeGet( rs, new String[] {name}, session, owner );
|
return nullSafeGet( rs, new String[] { name }, session, owner );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -649,6 +647,10 @@ public abstract class CollectionType extends AbstractType implements Association
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if ( !Hibernate.isInitialized( original ) ) {
|
if ( !Hibernate.isInitialized( original ) ) {
|
||||||
|
if ( ( (PersistentCollection) original ).hasQueuedOperations() ) {
|
||||||
|
final AbstractPersistentCollection pc = (AbstractPersistentCollection) original;
|
||||||
|
pc.replaceQueuedOperationValues( getPersister( session ), copyCache );
|
||||||
|
}
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.hibernate.Criteria;
|
import org.hibernate.Criteria;
|
||||||
|
import org.hibernate.Hibernate;
|
||||||
import org.hibernate.NullPrecedence;
|
import org.hibernate.NullPrecedence;
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
import org.hibernate.dialect.H2Dialect;
|
import org.hibernate.dialect.H2Dialect;
|
||||||
|
@ -406,8 +407,12 @@ public class OrderByTest extends BaseCoreFunctionalTestCase {
|
||||||
assertEquals( 2, forum.getPosts().size() );
|
assertEquals( 2, forum.getPosts().size() );
|
||||||
assertEquals( "post1", forum.getPosts().get( 0 ).getName() );
|
assertEquals( "post1", forum.getPosts().get( 0 ).getName() );
|
||||||
assertEquals( "post2", forum.getPosts().get( 1 ).getName() );
|
assertEquals( "post2", forum.getPosts().get( 1 ).getName() );
|
||||||
|
Hibernate.initialize( forum.getPosts() );
|
||||||
|
assertEquals( 2, forum.getPosts().size() );
|
||||||
assertEquals( 1, forum.getUsers().size() );
|
assertEquals( 1, forum.getUsers().size() );
|
||||||
assertEquals( "john", forum.getUsers().get( 0 ).getName() );
|
assertEquals( "john", forum.getUsers().get( 0 ).getName() );
|
||||||
|
Hibernate.initialize( forum.getUsers() );
|
||||||
|
assertEquals( 1, forum.getUsers().size() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
* 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.cascade;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.hibernate.Hibernate;
|
||||||
|
import org.hibernate.Session;
|
||||||
|
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|
||||||
|
public class MergeTest extends BaseCoreFunctionalTestCase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMergeDetachedEntityWithNewOneToManyElements() {
|
||||||
|
Order order = new Order();
|
||||||
|
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
s.persist( order );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
Item item1 = new Item();
|
||||||
|
item1.name = "i1";
|
||||||
|
|
||||||
|
Item item2 = new Item();
|
||||||
|
item2.name = "i2";
|
||||||
|
|
||||||
|
order.addItem( item1 );
|
||||||
|
order.addItem( item2 );
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
order = (Order) s.merge( order );
|
||||||
|
s.flush();
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
order = s.get( Order.class, order.id );
|
||||||
|
assertEquals( 2, order.items.size() );
|
||||||
|
s.delete( order );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMergeEntityWithNewOneToManyElements() {
|
||||||
|
Order order = new Order();
|
||||||
|
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
s.persist( order );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
order = s.get( Order.class, order.id );
|
||||||
|
Item item1 = new Item();
|
||||||
|
item1.name = "i1";
|
||||||
|
Item item2 = new Item();
|
||||||
|
item2.name = "i2";
|
||||||
|
order.addItem( item1 );
|
||||||
|
order.addItem( item2 );
|
||||||
|
assertFalse( Hibernate.isInitialized( order.items ) );
|
||||||
|
order = (Order) s.merge( order );
|
||||||
|
//s.flush();
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
order = s.get( Order.class, order.id );
|
||||||
|
assertEquals( 2, order.items.size() );
|
||||||
|
s.delete( order );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Class[] getAnnotatedClasses() {
|
||||||
|
return new Class[] {
|
||||||
|
Order.class,
|
||||||
|
Item.class
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
private static class Order {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@OneToMany(cascade = CascadeType.ALL, mappedBy = "order", orphanRemoval = true)
|
||||||
|
private List<Item> items = new ArrayList<Item>();
|
||||||
|
|
||||||
|
public Order() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addItem(Item item) {
|
||||||
|
items.add( item );
|
||||||
|
item.order = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
private static class Item {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
private Order order;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,230 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2014 JBoss Inc
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.hibernate.test.collection.bag;
|
||||||
|
|
||||||
|
import org.hibernate.HibernateException;
|
||||||
|
import org.hibernate.Session;
|
||||||
|
import org.hibernate.Transaction;
|
||||||
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
|
import org.hibernate.cfg.Configuration;
|
||||||
|
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This template demonstrates how to develop a test case for Hibernate ORM, using its built-in unit test framework.
|
||||||
|
* Although ORMStandaloneTestCase is perfectly acceptable as a reproducer, usage of this class is much preferred.
|
||||||
|
* Since we nearly always include a regression test with bug fixes, providing your reproducer using this method
|
||||||
|
* simplifies the process.
|
||||||
|
*
|
||||||
|
* What's even better? Fork hibernate-orm itself, add your test case directly to a module's unit tests, then
|
||||||
|
* submit it as a PR!
|
||||||
|
*/
|
||||||
|
public class BagDuplicatesTest extends BaseCoreFunctionalTestCase {
|
||||||
|
|
||||||
|
// Add your entities here.
|
||||||
|
@Override
|
||||||
|
protected Class[] getAnnotatedClasses() {
|
||||||
|
return new Class[] {
|
||||||
|
Parent.class,
|
||||||
|
Child.class
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// If you use *.hbm.xml mappings, instead of annotations, add the mappings here.
|
||||||
|
@Override
|
||||||
|
protected String[] getMappings() {
|
||||||
|
return new String[] {
|
||||||
|
// "Foo.hbm.xml",
|
||||||
|
// "Bar.hbm.xml"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// If those mappings reside somewhere other than resources/org/hibernate/test, change this.
|
||||||
|
@Override
|
||||||
|
protected String getBaseForMappings() {
|
||||||
|
return "org/hibernate/test/";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add in any settings that are specific to your test. See resources/hibernate.properties for the defaults.
|
||||||
|
@Override
|
||||||
|
protected void configure(Configuration configuration) {
|
||||||
|
super.configure( configuration );
|
||||||
|
|
||||||
|
configuration.setProperty( AvailableSettings.SHOW_SQL, "true" );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add your tests, using standard JUnit.
|
||||||
|
@Test
|
||||||
|
public void HHH10385Test() throws Exception {
|
||||||
|
// BaseCoreFunctionalTestCase automatically creates the SessionFactory and provides the Session.
|
||||||
|
Session session = null;
|
||||||
|
Transaction transaction = null;
|
||||||
|
|
||||||
|
Long parentId = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
session = openSession();
|
||||||
|
transaction = session.beginTransaction();
|
||||||
|
|
||||||
|
Parent parent = new Parent();
|
||||||
|
session.persist(parent);
|
||||||
|
session.flush();
|
||||||
|
parentId = parent.getId();
|
||||||
|
|
||||||
|
transaction.commit();
|
||||||
|
} catch (HibernateException e) {
|
||||||
|
if (transaction != null) {
|
||||||
|
transaction.rollback();
|
||||||
|
}
|
||||||
|
fail(e.getMessage());
|
||||||
|
} finally {
|
||||||
|
if (session != null) {
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
session = openSession();
|
||||||
|
transaction = session.beginTransaction();
|
||||||
|
|
||||||
|
Parent parent = session.get(Parent.class, parentId);
|
||||||
|
Child child1 = new Child();
|
||||||
|
child1.setName("child1");
|
||||||
|
child1.setParent(parent);
|
||||||
|
parent.addChild(child1);
|
||||||
|
parent = (Parent) session.merge(parent);
|
||||||
|
session.flush();
|
||||||
|
//assertEquals(1, parent.getChildren().size());
|
||||||
|
|
||||||
|
transaction.commit();
|
||||||
|
} catch (HibernateException e) {
|
||||||
|
if (transaction != null) {
|
||||||
|
transaction.rollback();
|
||||||
|
}
|
||||||
|
fail(e.getMessage());
|
||||||
|
} finally {
|
||||||
|
if (session != null) {
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
session = openSession();
|
||||||
|
transaction = session.beginTransaction();
|
||||||
|
|
||||||
|
Parent parent = session.get(Parent.class, parentId);
|
||||||
|
assertEquals(1, parent.getChildren().size());
|
||||||
|
|
||||||
|
transaction.commit();
|
||||||
|
} catch (HibernateException e) {
|
||||||
|
if (transaction != null) {
|
||||||
|
transaction.rollback();
|
||||||
|
}
|
||||||
|
fail(e.getMessage());
|
||||||
|
} finally {
|
||||||
|
if (session != null) {
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Parent")
|
||||||
|
public static class Parent {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@OneToMany(cascade = CascadeType.ALL, mappedBy = "parent", orphanRemoval = true)
|
||||||
|
private List<Child> children = new ArrayList<Child>();
|
||||||
|
|
||||||
|
public Parent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Child> getChildren() {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addChild(Child child) {
|
||||||
|
children.add(child);
|
||||||
|
child.setParent(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeChild(Child child) {
|
||||||
|
children.remove(child);
|
||||||
|
child.setParent(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Child")
|
||||||
|
public static class Child {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
private Parent parent;
|
||||||
|
|
||||||
|
public Child() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Parent getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParent(Parent parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Child{" +
|
||||||
|
"id=" + id +
|
||||||
|
", name='" + name + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* 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.collection.bag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Gail Badner
|
||||||
|
*/
|
||||||
|
public class Item {
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
private Order order;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Order getOrder() {
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
public void setOrder(Order order) {
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,4 +23,22 @@
|
||||||
</bag>
|
</bag>
|
||||||
</class>
|
</class>
|
||||||
|
|
||||||
|
<class name="Order" table="TAuction">
|
||||||
|
<id name="id">
|
||||||
|
<generator class="native"/>
|
||||||
|
</id>
|
||||||
|
<bag name="items" inverse="true"
|
||||||
|
cascade="all-delete-orphan">
|
||||||
|
<key column="orderId"/>
|
||||||
|
<one-to-many class="Item"/>
|
||||||
|
</bag>
|
||||||
|
</class>
|
||||||
|
|
||||||
|
<class name="Item" table="TBid">
|
||||||
|
<id name="id">
|
||||||
|
<generator class="native"/>
|
||||||
|
</id>
|
||||||
|
<property name="name"/>
|
||||||
|
<many-to-one name="order" column="orderId"/>
|
||||||
|
</class>
|
||||||
</hibernate-mapping>
|
</hibernate-mapping>
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* 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.collection.bag;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Gail Badner
|
||||||
|
*/
|
||||||
|
public class Order {
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private List<Item> items = new ArrayList<Item>();
|
||||||
|
|
||||||
|
public Order() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Item> getItems() {
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
public void setItems(List<Item> items) {
|
||||||
|
this.items = items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addItem(Item item) {
|
||||||
|
items.add( item );
|
||||||
|
item.setOrder( this );
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import org.hibernate.collection.internal.PersistentBag;
|
||||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
@ -69,4 +70,37 @@ public class PersistentBagTest extends BaseCoreFunctionalTestCase {
|
||||||
session.getTransaction().commit();
|
session.getTransaction().commit();
|
||||||
session.close();
|
session.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMergePersistentEntityWithNewOneToManyElements() {
|
||||||
|
Order order = new Order();
|
||||||
|
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
s.persist( order );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
order = s.get( Order.class, order.getId() );
|
||||||
|
Item item1 = new Item();
|
||||||
|
item1.setName( "i1" );
|
||||||
|
Item item2 = new Item();
|
||||||
|
item2.setName( "i2" );
|
||||||
|
order.addItem( item1 );
|
||||||
|
order.addItem( item2 );
|
||||||
|
order = (Order) s.merge( order );
|
||||||
|
//s.flush();
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
order = s.get( Order.class, order.getId() );
|
||||||
|
assertEquals( 2, order.getItems().size() );
|
||||||
|
s.delete( order );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,340 @@
|
||||||
|
/*
|
||||||
|
* 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.collection.delayedOperation;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.hibernate.Hibernate;
|
||||||
|
import org.hibernate.Session;
|
||||||
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests delayed operations that are queued for a PersistentBag. The Bag does not have
|
||||||
|
* to be extra-lazy to queue the operations.
|
||||||
|
* @author Gail Badner
|
||||||
|
*/
|
||||||
|
public class BagDelayedOperationTest extends BaseCoreFunctionalTestCase {
|
||||||
|
private Long parentId;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class[] getAnnotatedClasses() {
|
||||||
|
return new Class[] {
|
||||||
|
Parent.class,
|
||||||
|
Child.class
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
// start by cleaning up in case a test fails
|
||||||
|
if ( parentId != null ) {
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
Parent parent = new Parent();
|
||||||
|
Child child1 = new Child( "Sherman" );
|
||||||
|
Child child2 = new Child( "Yogi" );
|
||||||
|
parent.addChild( child1 );
|
||||||
|
parent.addChild( child2 );
|
||||||
|
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
s.persist( parent );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
parentId = parent.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void cleanup() {
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Parent parent = s.get( Parent.class, parentId );
|
||||||
|
parent.getChildren().clear();
|
||||||
|
s.delete( parent );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
parentId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-5855")
|
||||||
|
public void testSimpleAddDetached() {
|
||||||
|
// Create 2 detached Child objects.
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Child c1 = new Child( "Darwin" );
|
||||||
|
s.persist( c1 );
|
||||||
|
Child c2 = new Child( "Comet" );
|
||||||
|
s.persist( c2 );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Now Child c is detached.
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Parent p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
// add detached Child c
|
||||||
|
p.addChild( c1 );
|
||||||
|
// collection should still be uninitialized
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Add a detached Child and commit
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 3, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Add another detached Child, merge, and commit
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
p.addChild( c2 );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
s.merge( p );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 4, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-5855")
|
||||||
|
public void testSimpleAddTransient() {
|
||||||
|
// Add a transient Child and commit.
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Parent p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
// add transient Child
|
||||||
|
p.addChild( new Child( "Darwin" ) );
|
||||||
|
// collection should still be uninitialized
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 3, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Add another transient Child and commit again.
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
// add transient Child
|
||||||
|
p.addChild( new Child( "Comet" ) );
|
||||||
|
// collection should still be uninitialized
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
s.merge( p );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 4, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-5855")
|
||||||
|
public void testSimpleAddManaged() {
|
||||||
|
// Add 2 Child entities
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Child c1 = new Child( "Darwin" );
|
||||||
|
s.persist( c1 );
|
||||||
|
Child c2 = new Child( "Comet" );
|
||||||
|
s.persist( c2 );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Add a managed Child and commit
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Parent p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
// get the first Child so it is managed; add to collection
|
||||||
|
p.addChild( s.get( Child.class, c1.getId() ) );
|
||||||
|
// collection should still be uninitialized
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 3, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Add the other managed Child, merge and commit.
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
// get the second Child so it is managed; add to collection
|
||||||
|
p.addChild( s.get( Child.class, c2.getId() ) );
|
||||||
|
// collection should still be uninitialized
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
s.merge( p );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 4, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Parent")
|
||||||
|
public static class Parent {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
// Don't need extra-lazy to delay add operations to a bag.
|
||||||
|
@OneToMany(cascade = CascadeType.ALL, mappedBy = "parent", orphanRemoval = true)
|
||||||
|
private List<Child> children = new ArrayList<Child>();
|
||||||
|
|
||||||
|
public Parent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Child> getChildren() {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addChild(Child child) {
|
||||||
|
children.add(child);
|
||||||
|
child.setParent(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Child")
|
||||||
|
public static class Child {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
private Parent parent;
|
||||||
|
|
||||||
|
public Child() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Child(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Parent getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParent(Parent parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Child{" +
|
||||||
|
"id=" + id +
|
||||||
|
", name='" + name + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if ( this == o ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ( o == null || getClass() != o.getClass() ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Child child = (Child) o;
|
||||||
|
|
||||||
|
return name.equals( child.name );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return name.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,465 @@
|
||||||
|
/*
|
||||||
|
* 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.collection.delayedOperation;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.OrderColumn;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.hibernate.Hibernate;
|
||||||
|
import org.hibernate.Session;
|
||||||
|
import org.hibernate.annotations.LazyCollection;
|
||||||
|
import org.hibernate.annotations.LazyCollectionOption;
|
||||||
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests delayed operations that are queued for a PersistentSet. remove( Object )
|
||||||
|
* requires extra lazy to queue the operations.
|
||||||
|
* @author Gail Badner
|
||||||
|
*/
|
||||||
|
public class ListDelayedOperationTest extends BaseCoreFunctionalTestCase {
|
||||||
|
private Long parentId;
|
||||||
|
private Long childId1;
|
||||||
|
private Long childId2;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class[] getAnnotatedClasses() {
|
||||||
|
return new Class[] {
|
||||||
|
Parent.class,
|
||||||
|
Child.class
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
// start by cleaning up in case a test fails
|
||||||
|
if ( parentId != null ) {
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
Parent parent = new Parent();
|
||||||
|
Child child1 = new Child( "Sherman" );
|
||||||
|
Child child2 = new Child( "Yogi" );
|
||||||
|
parent.addChild( child1 );
|
||||||
|
parent.addChild( child2 );
|
||||||
|
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
s.persist( parent );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
parentId = parent.getId();
|
||||||
|
childId1 = child1.getId();
|
||||||
|
childId2 = child2.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void cleanup() {
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Parent parent = s.get( Parent.class, parentId );
|
||||||
|
parent.getChildren().clear();
|
||||||
|
s.delete( parent );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
parentId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-5855")
|
||||||
|
public void testSimpleAddDetached() {
|
||||||
|
// Create 2 detached Child objects.
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Child c1 = new Child( "Darwin" );
|
||||||
|
s.persist( c1 );
|
||||||
|
Child c2 = new Child( "Comet" );
|
||||||
|
s.persist( c2 );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Now Child c is detached.
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Parent p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
// add detached Child c
|
||||||
|
p.addChild( c1 );
|
||||||
|
// collection should still be uninitialized
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Add a detached Child and commit
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 3, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Add another detached Child, merge, and commit
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
p.addChild( c2 );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
s.merge( p );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 4, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-5855")
|
||||||
|
public void testSimpleAddTransient() {
|
||||||
|
// Add a transient Child and commit.
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Parent p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
// add transient Child
|
||||||
|
p.addChild( new Child( "Darwin" ) );
|
||||||
|
// collection should still be uninitialized
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 3, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Add another transient Child and commit again.
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
// add transient Child
|
||||||
|
p.addChild( new Child( "Comet" ) );
|
||||||
|
// collection should still be uninitialized
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
s.merge( p );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 4, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-5855")
|
||||||
|
public void testSimpleAddManaged() {
|
||||||
|
// Add 2 Child entities
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Child c1 = new Child( "Darwin" );
|
||||||
|
s.persist( c1 );
|
||||||
|
Child c2 = new Child( "Comet" );
|
||||||
|
s.persist( c2 );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Add a managed Child and commit
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Parent p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
// get the first Child so it is managed; add to collection
|
||||||
|
p.addChild( s.get( Child.class, c1.getId() ) );
|
||||||
|
// collection should still be uninitialized
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 3, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Add the other managed Child, merge and commit.
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
// get the second Child so it is managed; add to collection
|
||||||
|
p.addChild( s.get( Child.class, c2.getId() ) );
|
||||||
|
// collection should still be uninitialized
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
s.merge( p );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 4, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-5855")
|
||||||
|
public void testSimpleRemoveDetached() {
|
||||||
|
// Get the 2 Child entities and detach.
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Child c1 = s.get( Child.class, childId1 );
|
||||||
|
Child c2 = s.get( Child.class, childId2 );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Remove a detached entity element and commit
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Parent p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
// remove a detached element and commit
|
||||||
|
Hibernate.initialize( p.getChildren() );
|
||||||
|
p.removeChild( c1 );
|
||||||
|
for ( Child c : p.getChildren() ) {
|
||||||
|
if ( c.equals( c1 ) ) {
|
||||||
|
s.evict( c );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
//s.merge( p );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Remove a detached entity element, merge, and commit
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
Hibernate.initialize( p.getChildren() );
|
||||||
|
assertEquals( 1, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Remove a detached entity element, merge, and commit
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
// remove a detached element and commit
|
||||||
|
p.removeChild( c2 );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
p = (Parent) s.merge( p );
|
||||||
|
Hibernate.initialize( p );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Remove a detached entity element, merge, and commit
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 0, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* STILL WORKING ON THIS ONE...
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-5855")
|
||||||
|
public void testSimpleRemoveManaged() {
|
||||||
|
// Remove a managed entity element and commit
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Parent p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
// get c1 so it is managed, then remove and commit
|
||||||
|
p.removeChild( s.get( Child.class, childId1 ) );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 1, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
// get c1 so it is managed, then remove, merge and commit
|
||||||
|
p.removeChild( s.get( Child.class, childId2 ) );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
s.merge( p );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 0, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Entity(name = "Parent")
|
||||||
|
public static class Parent {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@OneToMany(cascade = CascadeType.ALL, mappedBy = "parent", orphanRemoval = true)
|
||||||
|
@LazyCollection(LazyCollectionOption.EXTRA )
|
||||||
|
@OrderColumn
|
||||||
|
private List<Child> children = new ArrayList<Child>();
|
||||||
|
|
||||||
|
public Parent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Child> getChildren() {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addChild(Child child) {
|
||||||
|
children.add(child);
|
||||||
|
child.setParent(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addChild(Child child, int i) {
|
||||||
|
children.add(i, child );
|
||||||
|
child.setParent(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeChild(Child child) {
|
||||||
|
children.remove(child);
|
||||||
|
child.setParent(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Child")
|
||||||
|
public static class Child {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
private Parent parent;
|
||||||
|
|
||||||
|
public Child() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Child(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Parent getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParent(Parent parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Child{" +
|
||||||
|
"id=" + id +
|
||||||
|
", name='" + name + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if ( this == o ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ( o == null || getClass() != o.getClass() ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Child child = (Child) o;
|
||||||
|
|
||||||
|
return name.equals( child.name );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return name.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,448 @@
|
||||||
|
/*
|
||||||
|
* 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.collection.delayedOperation;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.hibernate.Hibernate;
|
||||||
|
import org.hibernate.Session;
|
||||||
|
import org.hibernate.annotations.LazyCollection;
|
||||||
|
import org.hibernate.annotations.LazyCollectionOption;
|
||||||
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests delayed operations that are queued for a PersistentSet. The Set must be
|
||||||
|
* extra lazy to queue the operations.
|
||||||
|
* @author Gail Badner
|
||||||
|
*/
|
||||||
|
public class SetDelayedOperationTest extends BaseCoreFunctionalTestCase {
|
||||||
|
private Long parentId;
|
||||||
|
private Long childId1;
|
||||||
|
private Long childId2;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class[] getAnnotatedClasses() {
|
||||||
|
return new Class[] {
|
||||||
|
Parent.class,
|
||||||
|
Child.class
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
// start by cleaning up in case a test fails
|
||||||
|
if ( parentId != null ) {
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
Parent parent = new Parent();
|
||||||
|
Child child1 = new Child( "Sherman" );
|
||||||
|
Child child2 = new Child( "Yogi" );
|
||||||
|
parent.addChild( child1 );
|
||||||
|
parent.addChild( child2 );
|
||||||
|
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
s.persist( parent );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
parentId = parent.getId();
|
||||||
|
childId1 = child1.getId();
|
||||||
|
childId2 = child2.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void cleanup() {
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Parent parent = s.get( Parent.class, parentId );
|
||||||
|
parent.getChildren().clear();
|
||||||
|
s.delete( parent );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
parentId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-5855")
|
||||||
|
public void testSimpleAddDetached() {
|
||||||
|
// Create 2 detached Child objects.
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Child c1 = new Child( "Darwin" );
|
||||||
|
s.persist( c1 );
|
||||||
|
Child c2 = new Child( "Comet" );
|
||||||
|
s.persist( c2 );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Now Child c is detached.
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Parent p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
// add detached Child c
|
||||||
|
p.addChild( c1 );
|
||||||
|
// collection should still be uninitialized
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Add a detached Child and commit
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 3, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Add another detached Child, merge, and commit
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
p.addChild( c2 );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
s.merge( p );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 4, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-5855")
|
||||||
|
public void testSimpleAddTransient() {
|
||||||
|
// Add a transient Child and commit.
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Parent p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
// add transient Child
|
||||||
|
p.addChild( new Child( "Darwin" ) );
|
||||||
|
// collection should still be uninitialized
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 3, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Add another transient Child and commit again.
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
// add transient Child
|
||||||
|
p.addChild( new Child( "Comet" ) );
|
||||||
|
// collection should still be uninitialized
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
s.merge( p );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 4, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-5855")
|
||||||
|
public void testSimpleAddManaged() {
|
||||||
|
// Add 2 Child entities
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Child c1 = new Child( "Darwin" );
|
||||||
|
s.persist( c1 );
|
||||||
|
Child c2 = new Child( "Comet" );
|
||||||
|
s.persist( c2 );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Add a managed Child and commit
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Parent p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
// get the first Child so it is managed; add to collection
|
||||||
|
p.addChild( s.get( Child.class, c1.getId() ) );
|
||||||
|
// collection should still be uninitialized
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 3, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Add the other managed Child, merge and commit.
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
// get the second Child so it is managed; add to collection
|
||||||
|
p.addChild( s.get( Child.class, c2.getId() ) );
|
||||||
|
// collection should still be uninitialized
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
s.merge( p );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 4, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-5855")
|
||||||
|
public void testSimpleRemoveDetached() {
|
||||||
|
// Get the 2 Child entities and detach.
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Child c1 = s.get( Child.class, childId1 );
|
||||||
|
Child c2 = s.get( Child.class, childId2 );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Remove a detached entity element and commit
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Parent p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
// remove a detached element and commit
|
||||||
|
p.removeChild( c1 );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
s.merge( p );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Remove a detached entity element, merge, and commit
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 1, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Remove a detached entity element, merge, and commit
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
// remove a detached element and commit
|
||||||
|
p.removeChild( c2 );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
p = (Parent) s.merge( p );
|
||||||
|
Hibernate.initialize( p );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
// Remove a detached entity element, merge, and commit
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 0, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-5855")
|
||||||
|
public void testSimpleRemoveManaged() {
|
||||||
|
// Remove a managed entity element and commit
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Parent p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
// get c1 so it is managed, then remove and commit
|
||||||
|
p.removeChild( s.get( Child.class, childId1 ) );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 1, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
// get c1 so it is managed, then remove, merge and commit
|
||||||
|
p.removeChild( s.get( Child.class, childId2 ) );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
s.merge( p );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
p = s.get( Parent.class, parentId );
|
||||||
|
assertFalse( Hibernate.isInitialized( p.getChildren() ) );
|
||||||
|
assertEquals( 0, p.getChildren().size() );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Parent")
|
||||||
|
public static class Parent {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@OneToMany(cascade = CascadeType.ALL, mappedBy = "parent", orphanRemoval = true)
|
||||||
|
@LazyCollection( value = LazyCollectionOption.EXTRA)
|
||||||
|
private Set<Child> children = new HashSet<Child>();
|
||||||
|
|
||||||
|
public Parent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Child> getChildren() {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addChild(Child child) {
|
||||||
|
children.add(child);
|
||||||
|
child.setParent(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeChild(Child child) {
|
||||||
|
children.remove(child);
|
||||||
|
child.setParent(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Child")
|
||||||
|
public static class Child {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
private Parent parent;
|
||||||
|
|
||||||
|
public Child() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Child(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Parent getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParent(Parent parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Child{" +
|
||||||
|
"id=" + id +
|
||||||
|
", name='" + name + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if ( this == o ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ( o == null || getClass() != o.getClass() ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Child child = (Child) o;
|
||||||
|
|
||||||
|
return name.equals( child.name );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return name.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
/*
|
||||||
|
* 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.jpa.test.cascade;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class MergeTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMergeDetachedEntityWithNewOneToManyElements() {
|
||||||
|
Order order = new Order();
|
||||||
|
|
||||||
|
EntityManager em = getOrCreateEntityManager();
|
||||||
|
em.getTransaction().begin();
|
||||||
|
em.persist( order );
|
||||||
|
em.getTransaction().commit();
|
||||||
|
em.close();
|
||||||
|
|
||||||
|
Item item1 = new Item();
|
||||||
|
item1.name = "i1";
|
||||||
|
|
||||||
|
Item item2 = new Item();
|
||||||
|
item2.name = "i2";
|
||||||
|
|
||||||
|
order.addItem( item1 );
|
||||||
|
order.addItem( item2 );
|
||||||
|
|
||||||
|
em = getOrCreateEntityManager();
|
||||||
|
em.getTransaction().begin();
|
||||||
|
order = em.merge( order );
|
||||||
|
em.flush();
|
||||||
|
em.getTransaction().commit();
|
||||||
|
em.close();
|
||||||
|
|
||||||
|
em = getOrCreateEntityManager();
|
||||||
|
em.getTransaction().begin();
|
||||||
|
order = em.find( Order.class, order.id );
|
||||||
|
assertEquals( 2, order.items.size() );
|
||||||
|
em.remove( order );
|
||||||
|
em.getTransaction().commit();
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMergeLoadedEntityWithNewOneToManyElements() {
|
||||||
|
Order order = new Order();
|
||||||
|
|
||||||
|
EntityManager em = getOrCreateEntityManager();
|
||||||
|
em.getTransaction().begin();
|
||||||
|
em.persist( order );
|
||||||
|
em.getTransaction().commit();
|
||||||
|
em.close();
|
||||||
|
|
||||||
|
em = getOrCreateEntityManager();
|
||||||
|
em.getTransaction().begin();
|
||||||
|
order = em.find( Order.class, order.id );
|
||||||
|
Item item1 = new Item();
|
||||||
|
item1.name = "i1";
|
||||||
|
Item item2 = new Item();
|
||||||
|
item2.name = "i2";
|
||||||
|
order.addItem( item1 );
|
||||||
|
order.addItem( item2 );
|
||||||
|
order = em.merge( order );
|
||||||
|
em.flush();
|
||||||
|
em.getTransaction().commit();
|
||||||
|
em.close();
|
||||||
|
|
||||||
|
em = getOrCreateEntityManager();
|
||||||
|
em.getTransaction().begin();
|
||||||
|
order = em.find( Order.class, order.id );
|
||||||
|
assertEquals( 2, order.items.size() );
|
||||||
|
em.remove( order );
|
||||||
|
em.getTransaction().commit();
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Class[] getAnnotatedClasses() {
|
||||||
|
return new Class[] {
|
||||||
|
Order.class,
|
||||||
|
Item.class
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
private static class Order {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@OneToMany(cascade = CascadeType.ALL, mappedBy = "order", orphanRemoval = true)
|
||||||
|
private List<Item> items = new ArrayList<Item>();
|
||||||
|
|
||||||
|
public Order() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addItem(Item item) {
|
||||||
|
items.add( item );
|
||||||
|
item.order = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
private static class Item {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
private Order order;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue