HHH-5855 : Merge causes a duplicated "insert" of a child entity in lazy collection
This commit is contained in:
parent
6d08b3526c
commit
efa72a8333
|
@ -14,6 +14,7 @@ import java.util.HashSet;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.FlushMode;
|
||||
|
@ -471,6 +472,20 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
|
|||
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,
|
||||
* add the queued elements to the underlying collection.
|
||||
|
@ -1125,6 +1140,40 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
|
|||
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
|
||||
* 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 {
|
||||
private Object value;
|
||||
final class SimpleAdd extends AbstractValueDelayedOperation {
|
||||
|
||||
public SimpleAdd(Object value) {
|
||||
this.value = value;
|
||||
public SimpleAdd(Object addedValue) {
|
||||
super( addedValue, null );
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void operate() {
|
||||
bag.add( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAddedInstance() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getOrphan() {
|
||||
return null;
|
||||
bag.add( getAddedInstance() );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import java.util.Collection;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
|
@ -511,131 +510,81 @@ public class PersistentList extends AbstractPersistentCollection implements List
|
|||
}
|
||||
}
|
||||
|
||||
final class SimpleAdd implements DelayedOperation {
|
||||
private Object value;
|
||||
final class SimpleAdd extends AbstractValueDelayedOperation {
|
||||
|
||||
public SimpleAdd(Object value) {
|
||||
this.value = value;
|
||||
public SimpleAdd(Object addedValue) {
|
||||
super( addedValue, null );
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void operate() {
|
||||
list.add( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAddedInstance() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getOrphan() {
|
||||
return null;
|
||||
list.add( getAddedInstance() );
|
||||
}
|
||||
}
|
||||
|
||||
final class Add implements DelayedOperation {
|
||||
abstract class AbstractListValueDelayedOperation extends AbstractValueDelayedOperation {
|
||||
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.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void operate() {
|
||||
list.add( index, value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAddedInstance() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getOrphan() {
|
||||
return null;
|
||||
protected final int getIndex() {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
final class Set implements DelayedOperation {
|
||||
private int index;
|
||||
private Object value;
|
||||
private Object old;
|
||||
final class Add extends AbstractListValueDelayedOperation {
|
||||
|
||||
public Set(int index, Object value, Object old) {
|
||||
this.index = index;
|
||||
this.value = value;
|
||||
this.old = old;
|
||||
public Add(int index, Object addedValue) {
|
||||
super( index, addedValue, null );
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void operate() {
|
||||
list.set( index, value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAddedInstance() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getOrphan() {
|
||||
return old;
|
||||
list.add( getIndex(), getAddedInstance() );
|
||||
}
|
||||
}
|
||||
|
||||
final class Remove implements DelayedOperation {
|
||||
private int index;
|
||||
private Object old;
|
||||
final class Set extends AbstractListValueDelayedOperation {
|
||||
|
||||
public Remove(int index, Object old) {
|
||||
this.index = index;
|
||||
this.old = old;
|
||||
public Set(int index, Object addedValue, Object orphan) {
|
||||
super( index, addedValue, orphan );
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void operate() {
|
||||
list.remove( index );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAddedInstance() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getOrphan() {
|
||||
return old;
|
||||
list.set( getIndex(), getAddedInstance() );
|
||||
}
|
||||
}
|
||||
|
||||
final class SimpleRemove implements DelayedOperation {
|
||||
private Object value;
|
||||
final class Remove extends AbstractListValueDelayedOperation {
|
||||
|
||||
public SimpleRemove(Object value) {
|
||||
this.value = value;
|
||||
public Remove(int index, Object orphan) {
|
||||
super( index, null, orphan );
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void operate() {
|
||||
list.remove( value );
|
||||
list.remove( getIndex() );
|
||||
}
|
||||
}
|
||||
|
||||
final class SimpleRemove extends AbstractValueDelayedOperation {
|
||||
|
||||
public SimpleRemove(Object orphan) {
|
||||
super( null, orphan );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAddedInstance() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getOrphan() {
|
||||
return value;
|
||||
@SuppressWarnings("unchecked")
|
||||
public void operate() {
|
||||
list.remove( getOrphan() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 value;
|
||||
private Object old;
|
||||
|
||||
public Put(Object index, Object value, Object old) {
|
||||
|
||||
protected AbstractMapValueDelayedOperation(Object index, Object addedValue, Object orphan) {
|
||||
super( addedValue, orphan );
|
||||
this.index = index;
|
||||
this.value = value;
|
||||
this.old = old;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void operate() {
|
||||
map.put( index, value );
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object getAddedInstance() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object getOrphan() {
|
||||
return old;
|
||||
protected final Object getIndex() {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
final class Remove implements DelayedOperation {
|
||||
private Object index;
|
||||
private Object old;
|
||||
|
||||
public Remove(Object index, Object old) {
|
||||
this.index = index;
|
||||
this.old = old;
|
||||
final class Put extends AbstractMapValueDelayedOperation {
|
||||
|
||||
public Put(Object index, Object addedValue, Object orphan) {
|
||||
super( index, addedValue, orphan );
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
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
|
||||
public Object getAddedInstance() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getOrphan() {
|
||||
return old;
|
||||
@SuppressWarnings("unchecked")
|
||||
public void operate() {
|
||||
map.remove( getIndex() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import java.util.Collection;
|
|||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
|
@ -460,51 +461,29 @@ public class PersistentSet extends AbstractPersistentCollection implements java.
|
|||
}
|
||||
}
|
||||
|
||||
final class SimpleAdd implements DelayedOperation {
|
||||
private Object value;
|
||||
|
||||
public SimpleAdd(Object value) {
|
||||
this.value = value;
|
||||
final class SimpleAdd extends AbstractValueDelayedOperation {
|
||||
|
||||
public SimpleAdd(Object addedValue) {
|
||||
super( addedValue, null );
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void operate() {
|
||||
set.add( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAddedInstance() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getOrphan() {
|
||||
return null;
|
||||
set.add( getAddedInstance() );
|
||||
}
|
||||
}
|
||||
|
||||
final class SimpleRemove implements DelayedOperation {
|
||||
private Object value;
|
||||
|
||||
public SimpleRemove(Object value) {
|
||||
this.value = value;
|
||||
final class SimpleRemove extends AbstractValueDelayedOperation {
|
||||
|
||||
public SimpleRemove(Object orphan) {
|
||||
super( null, orphan );
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void operate() {
|
||||
set.remove( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAddedInstance() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getOrphan() {
|
||||
return value;
|
||||
set.remove( getOrphan() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.hibernate.EntityMode;
|
|||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.collection.internal.AbstractPersistentCollection;
|
||||
import org.hibernate.collection.spi.PersistentCollection;
|
||||
import org.hibernate.engine.jdbc.Size;
|
||||
import org.hibernate.engine.spi.CollectionEntry;
|
||||
|
@ -47,9 +48,6 @@ import org.hibernate.proxy.LazyInitializer;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import org.dom4j.Element;
|
||||
import org.dom4j.Node;
|
||||
|
||||
/**
|
||||
* A type that handles Hibernate <tt>PersistentCollection</tt>s (including arrays).
|
||||
*
|
||||
|
@ -135,7 +133,7 @@ public abstract class CollectionType extends AbstractType implements Association
|
|||
|
||||
@Override
|
||||
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
|
||||
|
@ -649,6 +647,10 @@ public abstract class CollectionType extends AbstractType implements Association
|
|||
return null;
|
||||
}
|
||||
if ( !Hibernate.isInitialized( original ) ) {
|
||||
if ( ( (PersistentCollection) original ).hasQueuedOperations() ) {
|
||||
final AbstractPersistentCollection pc = (AbstractPersistentCollection) original;
|
||||
pc.replaceQueuedOperationValues( getPersister( session ), copyCache );
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import org.hibernate.Criteria;
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.NullPrecedence;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
|
@ -406,8 +407,12 @@ public class OrderByTest extends BaseCoreFunctionalTestCase {
|
|||
assertEquals( 2, forum.getPosts().size() );
|
||||
assertEquals( "post1", forum.getPosts().get( 0 ).getName() );
|
||||
assertEquals( "post2", forum.getPosts().get( 1 ).getName() );
|
||||
Hibernate.initialize( forum.getPosts() );
|
||||
assertEquals( 2, forum.getPosts().size() );
|
||||
assertEquals( 1, forum.getUsers().size() );
|
||||
assertEquals( "john", forum.getUsers().get( 0 ).getName() );
|
||||
Hibernate.initialize( forum.getUsers() );
|
||||
assertEquals( 1, forum.getUsers().size() );
|
||||
}
|
||||
|
||||
@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>
|
||||
</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>
|
|
@ -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.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
@ -69,4 +70,37 @@ public class PersistentBagTest extends BaseCoreFunctionalTestCase {
|
|||
session.getTransaction().commit();
|
||||
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