/*
* 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.event.internal;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.hibernate.event.spi.EntityCopyObserver;
import org.hibernate.event.spi.EventSource;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* 2011/10/20 Unit test for code added in MergeContext for performance improvement.
*
* @author Wim Ockerman @ CISCO
*/
public class MergeContextTest extends BaseCoreFunctionalTestCase {
private EventSource session = null;
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { Simple.class };
}
@Before
public void setUp() {
session = (EventSource) openSession();
}
@After
public void tearDown() {
session.close();
session = null;
}
@Test
public void testMergeToManagedEntityFillFollowedByInvertMapping() {
MergeContext cache = new MergeContext( session, new DoNothingEntityCopyObserver() );
Object mergeEntity = new Simple( 1 );
Object managedEntity = new Simple( 2 );
cache.put(mergeEntity, managedEntity);
checkCacheConsistency( cache, 1 );
assertTrue( cache.containsKey( mergeEntity ) );
assertFalse( cache.containsKey( managedEntity ) );
assertTrue( cache.containsValue( managedEntity ) );
assertTrue( cache.invertMap().containsKey( managedEntity ) );
assertFalse( cache.invertMap().containsKey( mergeEntity ) );
assertTrue( cache.invertMap().containsValue( mergeEntity ) );
cache.clear();
checkCacheConsistency( cache, 0 );
assertFalse(cache.containsKey(mergeEntity));
assertFalse(cache.invertMap().containsKey(managedEntity));
}
@Test
public void testMergeToManagedEntityFillFollowedByInvert() {
MergeContext cache = new MergeContext( session, new DoNothingEntityCopyObserver() );
Object mergeEntity = new Simple( 1 );
Object managedEntity = new Simple( 2 );
cache.put(mergeEntity, managedEntity);
checkCacheConsistency( cache, 1 );
assertTrue(cache.containsKey(mergeEntity));
assertFalse( cache.containsKey( managedEntity ) );
assertTrue( cache.invertMap().containsKey( managedEntity ) );
assertFalse( cache.invertMap().containsKey( mergeEntity ) );
}
@Test
public void testMergeToManagedEntityFillFollowedByInvertUsingPutAll() {
MergeContext cache = new MergeContext( session, new DoNothingEntityCopyObserver() );
Map<Object,Object> input = new HashMap<Object,Object>();
Object mergeEntity1 = new Simple( 1 );
//
Object managedEntity1 = 1;
input.put(mergeEntity1, managedEntity1);
Object mergeEntity2 = new Simple( 3 );
Object managedEntity2 = 2;
input.put(mergeEntity2, managedEntity2);
cache.putAll(input);
checkCacheConsistency( cache, 2 );
assertTrue(cache.containsKey(mergeEntity1));
assertFalse(cache.containsKey(managedEntity1));
assertTrue(cache.containsKey(mergeEntity2));
assertFalse(cache.containsKey(managedEntity2));
assertTrue(cache.invertMap().containsKey(managedEntity1));
assertFalse(cache.invertMap().containsKey(mergeEntity1));
assertTrue(cache.invertMap().containsKey(managedEntity2));
assertFalse(cache.invertMap().containsKey(mergeEntity2));
}
@Test
public void testMergeToManagedEntityFillFollowedByInvertUsingPutWithSetOperatedOnArg() {
MergeContext cache = new MergeContext( session, new DoNothingEntityCopyObserver() );
Object mergeEntity = new Simple( 1 );
Object managedEntity = new Simple( 2 );
cache.put(mergeEntity, managedEntity, true);
checkCacheConsistency( cache, 1 );
assertTrue(cache.containsKey(mergeEntity));
assertFalse( cache.containsKey( managedEntity ) );
assertTrue( cache.invertMap().containsKey( managedEntity ) );
assertFalse( cache.invertMap().containsKey( mergeEntity ) );
cache.clear();
checkCacheConsistency( cache, 0 );
cache.put(mergeEntity, managedEntity, false);
assertFalse( cache.isOperatedOn( mergeEntity ) );
checkCacheConsistency( cache, 1 );
assertTrue(cache.containsKey(mergeEntity));
assertFalse(cache.containsKey(managedEntity));
}
@Test
public void testMergeToManagedEntityFillFollowedByIterateEntrySet() {
MergeContext cache = new MergeContext( session, new DoNothingEntityCopyObserver() );
Object mergeEntity = new Simple( 1 );
Object managedEntity = new Simple( 2 );
cache.put( mergeEntity, managedEntity, true );
checkCacheConsistency( cache, 1 );
Iterator it = cache.entrySet().iterator();
assertTrue( it.hasNext() );
Map.Entry entry = ( Map.Entry ) it.next();
assertSame( mergeEntity, entry.getKey() );
assertSame( managedEntity, entry.getValue() );
assertFalse( it.hasNext() );
}
@Test
public void testMergeToManagedEntityFillFollowedByModifyEntrySet() {
MergeContext cache = new MergeContext( session, new DoNothingEntityCopyObserver() );
Object mergeEntity = new Simple( 1 );
Object managedEntity = new Simple( 2 );
cache.put( mergeEntity, managedEntity, true );
Iterator it = cache.entrySet().iterator();
try {
it.remove();
fail( "should have thrown UnsupportedOperationException" );
}
catch ( UnsupportedOperationException ex ) {
// expected
}
Map.Entry entry = (Map.Entry) cache.entrySet().iterator().next();
try {
cache.entrySet().remove( entry );
fail( "should have thrown UnsupportedOperationException" );
}
catch ( UnsupportedOperationException ex ) {
// expected
}
Map.Entry anotherEntry = new Map.Entry() {
private Object key = new Simple( 3 );
private Object value = 4;
@Override
public Object getKey() {
return key;
}
@Override
public Object getValue() {
return value;
}
@Override
public Object setValue(Object value) {
Object oldValue = this.value;
this.value = value;
return oldValue;
}
};
try {
cache.entrySet().add( anotherEntry );
fail( "should have thrown UnsupportedOperationException" );
}
catch ( UnsupportedOperationException ex ) {
// expected
}
}
@Test
public void testMergeToManagedEntityFillFollowedByModifyKeys() {
MergeContext cache = new MergeContext( session, new DoNothingEntityCopyObserver() );
Object mergeEntity = new Simple( 1 );
Object managedEntity = new Simple( 2 );
cache.put( mergeEntity, managedEntity, true );
Iterator it = cache.keySet().iterator();
try {
it.remove();
fail( "should have thrown UnsupportedOperationException" );
}
catch ( UnsupportedOperationException ex ) {
// expected
}
try {
cache.keySet().remove( mergeEntity );
fail( "should have thrown UnsupportedOperationException" );
}
catch ( UnsupportedOperationException ex ) {
// expected
}
Object newmanagedEntity = new Simple( 3 );
try {
cache.keySet().add( newmanagedEntity );
fail( "should have thrown UnsupportedOperationException" );
}
catch ( UnsupportedOperationException ex ) {
// expected
}
}
@Test
public void testMergeToManagedEntityFillFollowedByModifyValues() {
MergeContext cache = new MergeContext( session, new DoNothingEntityCopyObserver() );
Object mergeEntity = new Simple( 1 );
Object managedEntity = new Simple( 2 );
cache.put( mergeEntity, managedEntity, true );
Iterator it = cache.values().iterator();
try {
it.remove();
fail( "should have thrown UnsupportedOperationException" );
}
catch ( UnsupportedOperationException ex ) {
// expected
}
try {
cache.values().remove( managedEntity );
fail( "should have thrown UnsupportedOperationException" );
}
catch ( UnsupportedOperationException ex ) {
// expected
}
Object newmanagedEntity = new Simple( 3 );
try {
cache.values().add( newmanagedEntity );
fail( "should have thrown UnsupportedOperationException" );
}
catch ( UnsupportedOperationException ex ) {
// expected
}
}
@Test
public void testMergeToManagedEntityFillFollowedByModifyKeyOfEntrySetElement() {
MergeContext cache = new MergeContext( session, new DoNothingEntityCopyObserver() );
Simple mergeEntity = new Simple( 1 );
Simple managedEntity = new Simple( 0 );
cache.put(mergeEntity, managedEntity, true);
Map.Entry entry = (Map.Entry) cache.entrySet().iterator().next();
( ( Simple ) entry.getKey() ).setValue( 2 );
assertEquals( 2, mergeEntity.getValue() );
checkCacheConsistency( cache, 1 );
entry = (Map.Entry) cache.entrySet().iterator().next();
assertSame( mergeEntity, entry.getKey() );
assertSame( managedEntity, entry.getValue() );
}
@Test
public void testMergeToManagedEntityFillFollowedByModifyValueOfEntrySetElement() {
MergeContext cache = new MergeContext( session, new DoNothingEntityCopyObserver() );
Simple mergeEntity = new Simple( 1 );
Simple managedEntity = new Simple( 0 );
cache.put(mergeEntity, managedEntity, true);
Map.Entry entry = (Map.Entry) cache.entrySet().iterator().next();
( ( Simple ) entry.getValue() ).setValue( 2 );
assertEquals( 2, managedEntity.getValue() );
checkCacheConsistency( cache, 1 );
entry = (Map.Entry) cache.entrySet().iterator().next();
assertSame( mergeEntity, entry.getKey() );
assertSame( managedEntity, entry.getValue() );
}
@Test
public void testReplaceManagedEntity() {
MergeContext cache = new MergeContext( session, new DoNothingEntityCopyObserver() );
Simple mergeEntity = new Simple( 1 );
Simple managedEntity = new Simple( 0 );
cache.put(mergeEntity, managedEntity);
Simple managedEntityNew = new Simple( 0 );
try {
cache.put( mergeEntity, managedEntityNew );
}
catch( IllegalArgumentException ex) {
// expected; cannot replace the managed entity result for a particular merge entity.
}
}
@Test
public void testManagedEntityAssociatedWithNewAndExistingMergeEntities() {
MergeContext cache = new MergeContext( session, new DoNothingEntityCopyObserver() );
session.getTransaction().begin();
Simple mergeEntity = new Simple( 1 );
Simple managedEntity = new Simple( 0 );
cache.put(mergeEntity, managedEntity);
cache.put( new Simple( 1 ), managedEntity );
}
@Test
public void testManagedAssociatedWith2ExistingMergeEntities() {
MergeContext cache = new MergeContext( session, new DoNothingEntityCopyObserver() );
session.getTransaction().begin();
Simple mergeEntity1 = new Simple( 1 );
session.persist( mergeEntity1 );
Simple managedEntity1 = new Simple( 1 );
cache.put( mergeEntity1, managedEntity1 );
Simple managedEntity2 = new Simple( 2 );
try {
cache.put( mergeEntity1, managedEntity2 );
fail( "should have thrown IllegalArgumentException");
}
catch( IllegalArgumentException ex ) {
// expected; cannot change managed entity associated with a merge entity
}
finally {
session.getTransaction().rollback();
}
}
@Test
public void testRemoveNonExistingEntity() {
MergeContext cache = new MergeContext( session, new DoNothingEntityCopyObserver() );
try {
cache.remove( new Simple( 1 ) );
}
catch (UnsupportedOperationException ex) {
// expected; remove is not supported.
}
}
private void checkCacheConsistency(MergeContext cache, int expectedSize) {
Set entrySet = cache.entrySet();
Set cacheKeys = cache.keySet();
Collection cacheValues = cache.values();
Map invertedMap = cache.invertMap();
assertEquals( expectedSize, entrySet.size() );
assertEquals( expectedSize, cache.size() );
assertEquals( expectedSize, cacheKeys.size() );
assertEquals( expectedSize, cacheValues.size() );
assertEquals( expectedSize, invertedMap.size() );
for ( Object entry : cache.entrySet() ) {
Map.Entry mapEntry = ( Map.Entry ) entry;
assertSame( cache.get( mapEntry.getKey() ), mapEntry.getValue() );
assertTrue( cacheKeys.contains( mapEntry.getKey() ) );
assertTrue( cacheValues.contains( mapEntry.getValue() ) );
assertSame( mapEntry.getKey(), invertedMap.get( mapEntry.getValue() ) );
}
}
@Entity
private static class Simple {
@Id
private int value;
public Simple(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
@Override
public String toString() {
return "Simple{" +
"value=" + value +
'}';
}
}
private class DoNothingEntityCopyObserver implements EntityCopyObserver {
@Override
public void entityCopyDetected(Object managedEntity, Object mergeEntity1, Object mergeEntity2, EventSource session) {
}
@Override
public void topLevelMergeComplete(EventSource session) {
}
@Override
public void clear() {
}
}
private class ExceptionThrowingEntityCopyObserver implements EntityCopyObserver {
@Override
public void entityCopyDetected(Object managedEntity, Object mergeEntity1, Object mergeEntity2, EventSource session) {
throw new IllegalStateException( "Entity copies not allowed." );
}
@Override
public void topLevelMergeComplete(EventSource session) {
}
@Override
public void clear() {
}
}
}