HHH-8999 : NullPointerException when updating or deleting multiple entities of same type with non-comparable IDs

(cherry picked from commit 6853fdae70)
This commit is contained in:
Gail Badner 2016-06-21 00:52:22 -07:00
parent 52733a2869
commit cde859c57d
15 changed files with 501 additions and 158 deletions

View File

@ -13,6 +13,7 @@ import org.hibernate.mapping.Collection;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.type.Type;
import org.hibernate.type.VersionType;
import org.hibernate.type.descriptor.java.IncomparableComparator;
/**
* Standard CacheDataDescription implementation.
@ -37,6 +38,12 @@ public class CacheDataDescriptionImpl implements CacheDataDescription {
this.mutable = mutable;
this.versioned = versioned;
this.versionComparator = versionComparator;
if ( versioned &&
( versionComparator == null || IncomparableComparator.class.isInstance( versionComparator ) ) ) {
throw new IllegalArgumentException(
"versionComparator must not be null or an instance of " + IncomparableComparator.class.getName()
);
}
this.keyType = keyType;
}

View File

@ -26,7 +26,8 @@ public interface CacheDataDescription {
/**
* Is the data to be cached considered versioned?
*
* If {@code true}, it is illegal for {@link #getVersionComparator} to return {@code null}.
* If {@code true}, it is illegal for {@link #getVersionComparator} to return {@code null}
* or an instance of {@link org.hibernate.type.descriptor.java.IncomparableComparator}.
*
* @return {@code true} if the data is versioned; {@code false} otherwise.
*/

View File

@ -12,6 +12,7 @@ import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@ -62,6 +63,9 @@ public class ActionQueue {
private UnresolvedEntityInsertActions unresolvedInsertions;
// NOTE: ExecutableList fields must be instantiated via ListProvider#init or #getOrInit
// to ensure that they are instantiated consistently.
// Object insertions, updates, and deletions have list semantics because
// they must happen in the right order so as to respect referential
// integrity
@ -88,84 +92,121 @@ public class ActionQueue {
private BeforeTransactionCompletionProcessQueue beforeTransactionProcesses;
/**
* An array containing providers for all the ExecutableLists in execution order
* An LinkedHashMap containing providers for all the ExecutableLists, inserted in execution order
*/
private static final ListProvider[] EXECUTABLE_LISTS;
private static final LinkedHashMap<Class<? extends Executable>,ListProvider> EXECUTABLE_LISTS_MAP;
static {
EXECUTABLE_LISTS = new ListProvider[8];
EXECUTABLE_LISTS[0] = new ListProvider() {
ExecutableList<?> get(ActionQueue instance) {
return instance.orphanRemovals;
}
EXECUTABLE_LISTS_MAP = new LinkedHashMap<Class<? extends Executable>,ListProvider>( 8 );
ExecutableList<?> init(ActionQueue instance) {
return instance.orphanRemovals = new ExecutableList<OrphanRemovalAction>();
}
};
EXECUTABLE_LISTS[1] = new ListProvider() {
ExecutableList<?> get(ActionQueue instance) {
return instance.insertions;
}
ExecutableList<?> init(ActionQueue instance) {
return instance.insertions = new ExecutableList<AbstractEntityInsertAction>( new InsertActionSorter() );
}
};
EXECUTABLE_LISTS[2] = new ListProvider() {
ExecutableList<?> get(ActionQueue instance) {
return instance.updates;
}
ExecutableList<?> init(ActionQueue instance) {
return instance.updates = new ExecutableList<EntityUpdateAction>();
}
};
EXECUTABLE_LISTS[3] = new ListProvider() {
ExecutableList<?> get(ActionQueue instance) {
return instance.collectionQueuedOps;
}
ExecutableList<?> init(ActionQueue instance) {
return instance.collectionQueuedOps = new ExecutableList<QueuedOperationCollectionAction>();
}
};
EXECUTABLE_LISTS[4] = new ListProvider() {
ExecutableList<?> get(ActionQueue instance) {
return instance.collectionRemovals;
}
ExecutableList<?> init(ActionQueue instance) {
return instance.collectionRemovals = new ExecutableList<CollectionRemoveAction>();
}
};
EXECUTABLE_LISTS[5] = new ListProvider() {
ExecutableList<?> get(ActionQueue instance) {
return instance.collectionUpdates;
}
ExecutableList<?> init(ActionQueue instance) {
return instance.collectionUpdates = new ExecutableList<CollectionUpdateAction>();
}
};
EXECUTABLE_LISTS[6] = new ListProvider() {
ExecutableList<?> get(ActionQueue instance) {
return instance.collectionCreations;
}
ExecutableList<?> init(ActionQueue instance) {
return instance.collectionCreations = new ExecutableList<CollectionRecreateAction>();
}
};
EXECUTABLE_LISTS[7] = new ListProvider() {
ExecutableList<?> get(ActionQueue instance) {
return instance.deletions;
}
ExecutableList<?> init(ActionQueue instance) {
return instance.deletions = new ExecutableList<EntityDeleteAction>();
}
};
EXECUTABLE_LISTS_MAP.put(
OrphanRemovalAction.class,
new ListProvider<OrphanRemovalAction>() {
ExecutableList<OrphanRemovalAction> get(ActionQueue instance) {
return instance.orphanRemovals;
}
ExecutableList<OrphanRemovalAction> init(ActionQueue instance) {
// OrphanRemovalAction executables never require sorting.
return instance.orphanRemovals = new ExecutableList<OrphanRemovalAction>( false );
}
}
);
EXECUTABLE_LISTS_MAP.put(
AbstractEntityInsertAction.class,
new ListProvider<AbstractEntityInsertAction>() {
ExecutableList<AbstractEntityInsertAction> get(ActionQueue instance) {
return instance.insertions;
}
ExecutableList<AbstractEntityInsertAction> init(ActionQueue instance) {
if ( instance.isOrderInsertsEnabled() ) {
return instance.insertions = new ExecutableList<AbstractEntityInsertAction>(
new InsertActionSorter()
);
}
else {
return instance.insertions = new ExecutableList<AbstractEntityInsertAction>(
false
);
}
}
}
);
EXECUTABLE_LISTS_MAP.put(
EntityUpdateAction.class,
new ListProvider<EntityUpdateAction>() {
ExecutableList<EntityUpdateAction> get(ActionQueue instance) {
return instance.updates;
}
ExecutableList<EntityUpdateAction> init(ActionQueue instance) {
return instance.updates = new ExecutableList<EntityUpdateAction>(
instance.isOrderUpdatesEnabled()
);
}
}
);
EXECUTABLE_LISTS_MAP.put(
QueuedOperationCollectionAction.class,
new ListProvider<QueuedOperationCollectionAction>() {
ExecutableList<QueuedOperationCollectionAction> get(ActionQueue instance) {
return instance.collectionQueuedOps;
}
ExecutableList<QueuedOperationCollectionAction> init(ActionQueue instance) {
return instance.collectionQueuedOps = new ExecutableList<QueuedOperationCollectionAction>(
instance.isOrderUpdatesEnabled()
);
}
}
);
EXECUTABLE_LISTS_MAP.put(
CollectionRemoveAction.class,
new ListProvider<CollectionRemoveAction>() {
ExecutableList<CollectionRemoveAction> get(ActionQueue instance) {
return instance.collectionRemovals;
}
ExecutableList<CollectionRemoveAction> init(ActionQueue instance) {
return instance.collectionRemovals = new ExecutableList<CollectionRemoveAction>(
instance.isOrderUpdatesEnabled()
);
}
}
);
EXECUTABLE_LISTS_MAP.put(
CollectionUpdateAction.class,
new ListProvider<CollectionUpdateAction>() {
ExecutableList<CollectionUpdateAction> get(ActionQueue instance) {
return instance.collectionUpdates;
}
ExecutableList<CollectionUpdateAction> init(ActionQueue instance) {
return instance.collectionUpdates = new ExecutableList<CollectionUpdateAction>(
instance.isOrderUpdatesEnabled()
);
}
}
);
EXECUTABLE_LISTS_MAP.put(
CollectionRecreateAction.class,
new ListProvider<CollectionRecreateAction>() {
ExecutableList<CollectionRecreateAction> get(ActionQueue instance) {
return instance.collectionCreations;
}
ExecutableList<CollectionRecreateAction> init(ActionQueue instance) {
return instance.collectionCreations = new ExecutableList<CollectionRecreateAction>(
instance.isOrderUpdatesEnabled()
);
}
}
);
EXECUTABLE_LISTS_MAP.put(
EntityDeleteAction.class,
new ListProvider<EntityDeleteAction>() {
ExecutableList<EntityDeleteAction> get(ActionQueue instance) {
return instance.deletions;
}
ExecutableList<EntityDeleteAction> init(ActionQueue instance) {
// EntityDeleteAction executables never require sorting.
return instance.deletions = new ExecutableList<EntityDeleteAction>( false );
}
}
);
}
/**
@ -179,8 +220,8 @@ public class ActionQueue {
}
public void clear() {
for ( int i = 0; i < EXECUTABLE_LISTS.length; ++i ) {
ExecutableList<?> l = EXECUTABLE_LISTS[i].get(this);
for ( ListProvider listProvider : EXECUTABLE_LISTS_MAP.values() ) {
ExecutableList<?> l = listProvider.get( this );
if( l != null ) {
l.clear();
}
@ -233,10 +274,7 @@ public class ActionQueue {
}
else {
LOG.trace( "Adding resolved non-early insert action." );
if( insertions == null ) {
insertions = new ExecutableList<AbstractEntityInsertAction>( new InsertActionSorter() );
}
insertions.add(insert);
addAction( AbstractEntityInsertAction.class, insert );
}
insert.makeEntityManaged();
if( unresolvedInsertions != null ) {
@ -246,6 +284,11 @@ public class ActionQueue {
}
}
@SuppressWarnings("unchecked")
private <T extends Executable & Comparable & Serializable> void addAction(Class<T> executableClass, T action) {
EXECUTABLE_LISTS_MAP.get( executableClass ).getOrInit( this ).add( action );
}
/**
* Adds an entity (IDENTITY) insert action
*
@ -262,10 +305,7 @@ public class ActionQueue {
* @param action The action representing the entity deletion
*/
public void addAction(EntityDeleteAction action) {
if( deletions == null ) {
deletions = new ExecutableList<EntityDeleteAction>();
}
deletions.add( action );
addAction( EntityDeleteAction.class, action );
}
/**
@ -274,10 +314,7 @@ public class ActionQueue {
* @param action The action representing the orphan removal
*/
public void addAction(OrphanRemovalAction action) {
if( orphanRemovals == null ) {
orphanRemovals = new ExecutableList<OrphanRemovalAction>();
}
orphanRemovals.add( action );
addAction( OrphanRemovalAction.class, action );
}
/**
@ -286,10 +323,7 @@ public class ActionQueue {
* @param action The action representing the entity update
*/
public void addAction(EntityUpdateAction action) {
if( updates == null ) {
updates = new ExecutableList<EntityUpdateAction>();
}
updates.add( action );
addAction( EntityUpdateAction.class, action );
}
/**
@ -298,10 +332,7 @@ public class ActionQueue {
* @param action The action representing the (re)creation of a collection
*/
public void addAction(CollectionRecreateAction action) {
if( collectionCreations == null) {
collectionCreations = new ExecutableList<CollectionRecreateAction>();
}
collectionCreations.add( action );
addAction( CollectionRecreateAction.class, action );
}
/**
@ -310,10 +341,7 @@ public class ActionQueue {
* @param action The action representing the removal of a collection
*/
public void addAction(CollectionRemoveAction action) {
if( collectionRemovals == null ) {
collectionRemovals = new ExecutableList<CollectionRemoveAction>();
}
collectionRemovals.add( action );
addAction( CollectionRemoveAction.class, action );
}
/**
@ -322,10 +350,7 @@ public class ActionQueue {
* @param action The action representing the update of a collection
*/
public void addAction(CollectionUpdateAction action) {
if( collectionUpdates == null ) {
collectionUpdates = new ExecutableList<CollectionUpdateAction>();
}
collectionUpdates.add( action );
addAction( CollectionUpdateAction.class, action );
}
/**
@ -334,10 +359,7 @@ public class ActionQueue {
* @param action The action representing the queued operation
*/
public void addAction(QueuedOperationCollectionAction action) {
if( collectionQueuedOps == null) {
collectionQueuedOps = new ExecutableList<QueuedOperationCollectionAction>();
}
collectionQueuedOps.add( action );
addAction( QueuedOperationCollectionAction.class, action );
}
/**
@ -428,8 +450,8 @@ public class ActionQueue {
throw new IllegalStateException( "About to execute actions, but there are unresolved entity insert actions." );
}
for ( int i = 0; i < EXECUTABLE_LISTS.length; ++i ) {
ExecutableList<?> l = EXECUTABLE_LISTS[i].get(this);
for ( ListProvider listProvider : EXECUTABLE_LISTS_MAP.values() ) {
ExecutableList<?> l = listProvider.get( this );
if ( l != null && !l.isEmpty() ) {
executeActions( l );
}
@ -503,9 +525,9 @@ public class ActionQueue {
if ( tables.isEmpty() ) {
return false;
}
for ( int i = 0; i < EXECUTABLE_LISTS.length; ++i ) {
ExecutableList<?> l = EXECUTABLE_LISTS[i].get(this);
if ( areTablesToBeUpdated(l, tables) ) {
for ( ListProvider listProvider : EXECUTABLE_LISTS_MAP.values() ) {
ExecutableList<?> l = listProvider.get( this );
if ( areTablesToBeUpdated( l, tables ) ) {
return true;
}
}
@ -708,7 +730,7 @@ public class ActionQueue {
}
public void sortCollectionActions() {
if ( session.getFactory().getSessionFactoryOptions().isOrderUpdatesEnabled() ) {
if ( isOrderUpdatesEnabled() ) {
// sort the updates by fk
if( collectionCreations != null ) {
collectionCreations.sort();
@ -726,15 +748,23 @@ public class ActionQueue {
}
public void sortActions() {
if ( session.getFactory().getSessionFactoryOptions().isOrderUpdatesEnabled() && updates != null ) {
if ( isOrderUpdatesEnabled() && updates != null ) {
// sort the updates by pk
updates.sort();
}
if ( session.getFactory().getSessionFactoryOptions().isOrderInsertsEnabled() && insertions != null ) {
if ( isOrderInsertsEnabled() && insertions != null ) {
insertions.sort();
}
}
private boolean isOrderUpdatesEnabled() {
return session.getFactory().getSessionFactoryOptions().isOrderUpdatesEnabled();
}
private boolean isOrderInsertsEnabled() {
return session.getFactory().getSessionFactoryOptions().isOrderInsertsEnabled();
}
public void clearFromFlushNeededCheck(int previousCollectionRemovalSize) {
if( collectionCreations != null ) {
collectionCreations.clear();
@ -813,7 +843,7 @@ public class ActionQueue {
}
unresolvedInsertions.serialize( oos );
for ( ListProvider p : EXECUTABLE_LISTS ) {
for ( ListProvider p : EXECUTABLE_LISTS_MAP.values() ) {
ExecutableList<?> l = p.get(this);
if( l == null ) {
oos.writeBoolean(false);
@ -843,8 +873,7 @@ public class ActionQueue {
rtn.unresolvedInsertions = UnresolvedEntityInsertActions.deserialize( ois, session );
for ( int i = 0; i < EXECUTABLE_LISTS.length; ++i ) {
ListProvider provider = EXECUTABLE_LISTS[i];
for ( ListProvider provider : EXECUTABLE_LISTS_MAP.values() ) {
ExecutableList<?> l = provider.get(rtn);
boolean notNull = ois.readBoolean();
if( notNull ) {
@ -1088,8 +1117,15 @@ public class ActionQueue {
}
private static abstract class ListProvider {
abstract ExecutableList<?> get(ActionQueue instance);
abstract ExecutableList<?> init(ActionQueue instance);
private static abstract class ListProvider<T extends Executable & Comparable & Serializable> {
abstract ExecutableList<T> get(ActionQueue instance);
abstract ExecutableList<T> init(ActionQueue instance);
ExecutableList<T> getOrInit( ActionQueue instance ) {
ExecutableList<T> list = get( instance );
if ( list == null ) {
list = init( instance );
}
return list;
}
}
}

View File

@ -54,6 +54,7 @@ public class ExecutableList<E extends Executable & Comparable & Serializable> im
private final ArrayList<E> executables;
private final Sorter<E> sorter;
private final boolean requiresSorting;
private boolean sorted;
/**
@ -77,7 +78,20 @@ public class ExecutableList<E extends Executable & Comparable & Serializable> im
* @param initialCapacity The initial capacity for instantiating the internal List
*/
public ExecutableList(int initialCapacity) {
this( initialCapacity, null );
// pass true for requiresSorting argument to maintain original behavior
this( initialCapacity, true );
}
public ExecutableList(boolean requiresSorting) {
this( INIT_QUEUE_LIST_SIZE, requiresSorting );
}
public ExecutableList(int initialCapacity, boolean requiresSorting) {
this.sorter = null;
this.executables = new ArrayList<E>( initialCapacity );
this.querySpaces = null;
this.requiresSorting = requiresSorting;
this.sorted = requiresSorting;
}
/**
@ -99,6 +113,8 @@ public class ExecutableList<E extends Executable & Comparable & Serializable> im
this.sorter = sorter;
this.executables = new ArrayList<E>( initialCapacity );
this.querySpaces = null;
// require sorting by default, even if sorter is null to maintain original behavior
this.requiresSorting = true;
this.sorted = true;
}
@ -162,7 +178,7 @@ public class ExecutableList<E extends Executable & Comparable & Serializable> im
public void clear() {
executables.clear();
querySpaces = null;
sorted = true;
sorted = requiresSorting;
}
/**
@ -199,17 +215,19 @@ public class ExecutableList<E extends Executable & Comparable & Serializable> im
return false;
}
// see if the addition invalidated the sorting
if ( sorter != null ) {
// we don't have intrinsic insight into the sorter's algorithm, so invalidate sorting
sorted = false;
}
else {
// otherwise, we added to the end of the list. So check the comparison between the incoming
// executable and the one previously at the end of the list using the Comparable contract
if ( previousLast != null && previousLast.compareTo( executable ) > 0 ) {
// if it was sorted before the addition, then check if the addition invalidated the sorting
if ( sorted ) {
if ( sorter != null ) {
// we don't have intrinsic insight into the sorter's algorithm, so invalidate sorting
sorted = false;
}
else {
// otherwise, we added to the end of the list. So check the comparison between the incoming
// executable and the one previously at the end of the list using the Comparable contract
if ( previousLast != null && previousLast.compareTo( executable ) > 0 ) {
sorted = false;
}
}
}
Serializable[] querySpaces = executable.getPropertySpaces();
@ -225,7 +243,8 @@ public class ExecutableList<E extends Executable & Comparable & Serializable> im
*/
@SuppressWarnings("unchecked")
public void sort() {
if ( sorted ) {
if ( sorted || !requiresSorting ) {
// nothing to do
return;
}

View File

@ -11,6 +11,7 @@ import java.io.InputStream;
import java.sql.Blob;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Comparator;
import org.hibernate.HibernateException;
import org.hibernate.engine.jdbc.BinaryStream;
@ -71,6 +72,12 @@ public class ByteArrayTypeDescriptor extends AbstractTypeDescriptor<Byte[]> {
return bytes;
}
@Override
@SuppressWarnings({ "unchecked" })
public Comparator<Byte[]> getComparator() {
return IncomparableComparator.INSTANCE;
}
@SuppressWarnings({ "unchecked" })
@Override
public <X> X unwrap(Byte[] value, Class<X> type, WrapperOptions options) {

View File

@ -10,6 +10,7 @@ import java.io.Reader;
import java.io.StringReader;
import java.sql.Clob;
import java.util.Arrays;
import java.util.Comparator;
import org.hibernate.engine.jdbc.CharacterStream;
import org.hibernate.engine.jdbc.internal.CharacterStreamImpl;
@ -51,6 +52,12 @@ public class CharacterArrayTypeDescriptor extends AbstractTypeDescriptor<Charact
return hashCode;
}
@Override
@SuppressWarnings({ "unchecked" })
public Comparator<Character[]> getComparator() {
return IncomparableComparator.INSTANCE;
}
@SuppressWarnings({ "unchecked" })
@Override
public <X> X unwrap(Character[] value, Class<X> type, WrapperOptions options) {

View File

@ -11,6 +11,7 @@ import java.io.InputStream;
import java.sql.Blob;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Comparator;
import org.hibernate.HibernateException;
import org.hibernate.engine.jdbc.BinaryStream;
@ -72,6 +73,12 @@ public class PrimitiveByteArrayTypeDescriptor extends AbstractTypeDescriptor<byt
return bytes;
}
@Override
@SuppressWarnings({ "unchecked" })
public Comparator<byte[]> getComparator() {
return IncomparableComparator.INSTANCE;
}
@SuppressWarnings({ "unchecked" })
public <X> X unwrap(byte[] value, Class<X> type, WrapperOptions options) {
if ( value == null ) {

View File

@ -10,6 +10,7 @@ import java.io.Reader;
import java.io.StringReader;
import java.sql.Clob;
import java.util.Arrays;
import java.util.Comparator;
import org.hibernate.engine.jdbc.CharacterStream;
import org.hibernate.engine.jdbc.internal.CharacterStreamImpl;
@ -51,6 +52,12 @@ public class PrimitiveCharacterArrayTypeDescriptor extends AbstractTypeDescripto
return hashCode;
}
@Override
@SuppressWarnings({ "unchecked" })
public Comparator<char[]> getComparator() {
return IncomparableComparator.INSTANCE;
}
@SuppressWarnings({ "unchecked" })
public <X> X unwrap(char[] value, Class<X> type, WrapperOptions options) {
if ( value == null ) {

View File

@ -0,0 +1,255 @@
/*
* 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.engine.spi;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Iterator;
import java.util.Set;
import org.hibernate.HibernateException;
import org.hibernate.action.spi.AfterTransactionCompletionProcess;
import org.hibernate.action.spi.BeforeTransactionCompletionProcess;
import org.hibernate.action.spi.Executable;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* @author Anton Marsden
* @author Gail Badner
*/
public class NonSortedExecutableListTest extends BaseUnitTestCase {
// For testing, we need an Executable that is also Comparable and Serializable
private static class AnExecutable implements Executable, Comparable, Serializable {
private final int n;
private Serializable[] spaces;
private transient boolean afterDeserializeCalled;
public AnExecutable(int n, String... spaces) {
this.n = n;
this.spaces = spaces;
}
@Override
public int compareTo(Object o) {
return new Integer(n).compareTo( new Integer(( (AnExecutable) o ).n ));
}
@Override
public int hashCode() {
return n;
}
@Override
public boolean equals(Object obj) {
if ( this == obj )
return true;
if ( obj == null )
return false;
AnExecutable other = (AnExecutable) obj;
return n == other.n;
}
@Override
public Serializable[] getPropertySpaces() {
return spaces;
}
@Override
public void beforeExecutions() throws HibernateException {
}
@Override
public void execute() throws HibernateException {
}
@Override
public AfterTransactionCompletionProcess getAfterTransactionCompletionProcess() {
return null;
}
@Override
public BeforeTransactionCompletionProcess getBeforeTransactionCompletionProcess() {
return null;
}
@Override
public void afterDeserialize(SharedSessionContractImplementor session) {
this.afterDeserializeCalled = true;
}
public String toString() {
return String.valueOf(n);
}
}
private ExecutableList<AnExecutable> l;
private AnExecutable action1 = new AnExecutable( 0, "a" );
private AnExecutable action2 = new AnExecutable( 1, "b", "c" );
private AnExecutable action3 = new AnExecutable( 2, "b", "d" );
private AnExecutable action4 = new AnExecutable( 3 );
@Before
public void setUp() {
// false indicates sorting is not required.
l = new ExecutableList<AnExecutable>( false );
}
@After
public void tearDown() {
l = null;
}
@Test
public void testAdd() {
Assert.assertEquals( 0, l.size() );
l.add( action1 );
Assert.assertEquals( action1, l.get( 0 ) );
Assert.assertEquals( 1, l.size() );
l.add( action2 );
Assert.assertEquals( action2, l.get( 1 ) );
l.add( action3 );
Assert.assertEquals( action3, l.get( 2 ) );
Assert.assertEquals( 3, l.size() );
}
@Test
public void testClear() {
Assert.assertTrue( l.isEmpty() );
l.add( action1 );
Assert.assertFalse( l.isEmpty() );
l.add( action2 );
l.clear();
Assert.assertTrue( l.isEmpty() );
Assert.assertEquals( 0, l.size() );
}
@Test
public void testIterator() {
l.add( action1 );
l.add( action2 );
l.add( action3 );
Iterator<AnExecutable> iterator = l.iterator();
Assert.assertEquals(action1, iterator.next());
Assert.assertEquals(action2, iterator.next());
Assert.assertEquals(action3, iterator.next());
Assert.assertFalse(iterator.hasNext());
}
@Test
public void testRemoveLastN() {
l.add( action1 );
l.add( action2 );
l.add( action3 );
l.removeLastN( 0 );
Assert.assertEquals( 3, l.size() );
l.removeLastN( 2 );
Assert.assertEquals( 1, l.size() );
Assert.assertEquals( action1, l.get( 0 ) );
}
@Test
public void testGetSpaces() {
l.add( action1 );
Set<Serializable> ss = l.getQuerySpaces();
Assert.assertEquals( 1, ss.size() );
Assert.assertTrue( ss.contains( "a" ) );
l.add( action2 );
l.add( action3 );
l.add( action4 );
Set<Serializable> ss2 = l.getQuerySpaces();
Assert.assertEquals( 4, ss2.size() );
Assert.assertTrue( ss2.contains( "a" ) );
Assert.assertTrue( ss2.contains( "b" ) );
Assert.assertTrue( ss2.contains( "c" ) );
Assert.assertTrue( ss2.contains( "d" ) );
Assert.assertTrue( ss == ss2 ); // same Set (cached)
// now remove action4
l.remove( 3 );
ss2 = l.getQuerySpaces();
Assert.assertTrue( ss == ss2 ); // same Set (action4 has no spaces)
Assert.assertEquals( 4, ss2.size() );
l.remove( 2 );
ss2 = l.getQuerySpaces();
Assert.assertTrue( ss != ss2 ); // Different Set because it has been rebuilt. This would be incorrect if
// Set.clear() was used
}
@Test
public void testSort() {
l.add( action4 );
l.add( action3 );
l.add( action2 );
l.add( action1 );
// sort should have no affect
l.sort();
Assert.assertEquals( action4, l.get( 0 ) );
Assert.assertEquals( action3, l.get( 1 ) );
Assert.assertEquals( action2, l.get( 2 ) );
Assert.assertEquals( action1, l.get( 3 ) );
}
@Test
public void testSerializeDeserialize() throws IOException, ClassNotFoundException {
l.add( action4 );
l.add( action3 );
l.add( action2 );
l.add( action1 );
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream( baos );
l.writeExternal( oos );
// this OOS stream needs to be flushed...
oos.flush();
ByteArrayInputStream bin = new ByteArrayInputStream( baos.toByteArray() );
ObjectInputStream ois = new ObjectInputStream( bin );
l = new ExecutableList<NonSortedExecutableListTest.AnExecutable>( false );
l.readExternal( ois );
Assert.assertEquals( 4, l.size() );
Assert.assertEquals( action4, l.get( 0 ) );
Assert.assertEquals( action3, l.get( 1 ) );
Assert.assertEquals( action2, l.get( 2 ) );
Assert.assertEquals( action1, l.get( 3 ) );
Assert.assertFalse( l.get( 0 ).afterDeserializeCalled );
Assert.assertFalse( l.get( 1 ).afterDeserializeCalled );
Assert.assertFalse( l.get( 2 ).afterDeserializeCalled );
Assert.assertFalse( l.get( 3 ).afterDeserializeCalled );
l.afterDeserialize( null );
Assert.assertTrue( l.get( 0 ).afterDeserializeCalled );
Assert.assertTrue( l.get( 1 ).afterDeserializeCalled );
Assert.assertTrue( l.get( 2 ).afterDeserializeCalled );
Assert.assertTrue( l.get( 3 ).afterDeserializeCalled );
Assert.assertEquals( action4, l.get( 0 ) );
Assert.assertEquals( action3, l.get( 1 ) );
Assert.assertEquals( action2, l.get( 2 ) );
Assert.assertEquals( action1, l.get( 3 ) );
// sort after deserializing; it should still have no affect
l.sort();
Assert.assertEquals( action4, l.get( 0 ) );
Assert.assertEquals( action3, l.get( 1 ) );
Assert.assertEquals( action2, l.get( 2 ) );
Assert.assertEquals( action1, l.get( 3 ) );
}
}

View File

@ -28,7 +28,7 @@ import org.junit.Test;
/**
* @author Anton Marsden
*/
public class ExecutableListTest extends BaseUnitTestCase {
public class SortedExecutableListTest extends BaseUnitTestCase {
// For testing, we need an Executable that is also Comparable and Serializable
private static class AnExecutable implements Executable, Comparable, Serializable {
@ -214,7 +214,7 @@ public class ExecutableListTest extends BaseUnitTestCase {
oos.flush();
ByteArrayInputStream bin = new ByteArrayInputStream( baos.toByteArray() );
ObjectInputStream ois = new ObjectInputStream( bin );
l = new ExecutableList<ExecutableListTest.AnExecutable>();
l = new ExecutableList<SortedExecutableListTest.AnExecutable>();
l.readExternal( ois );
Assert.assertEquals( 4, l.size() );
@ -226,15 +226,26 @@ public class ExecutableListTest extends BaseUnitTestCase {
Assert.assertFalse(l.get(0).afterDeserializeCalled);
Assert.assertFalse(l.get(1).afterDeserializeCalled);
Assert.assertFalse(l.get(2).afterDeserializeCalled);
Assert.assertFalse(l.get(3).afterDeserializeCalled);
Assert.assertFalse( l.get( 3 ).afterDeserializeCalled );
l.afterDeserialize( null );
Assert.assertTrue(l.get(0).afterDeserializeCalled);
Assert.assertTrue(l.get(1).afterDeserializeCalled);
Assert.assertTrue(l.get(2).afterDeserializeCalled);
Assert.assertTrue(l.get(3).afterDeserializeCalled);
Assert.assertTrue( l.get( 0 ).afterDeserializeCalled );
Assert.assertTrue( l.get( 1 ).afterDeserializeCalled );
Assert.assertTrue( l.get( 2 ).afterDeserializeCalled );
Assert.assertTrue( l.get( 3 ).afterDeserializeCalled );
Assert.assertEquals( action4, l.get( 0 ) );
Assert.assertEquals( action3, l.get( 1 ) );
Assert.assertEquals( action2, l.get( 2 ) );
Assert.assertEquals( action1, l.get( 3 ) );
// sort after deserializing
l.sort();
Assert.assertEquals( action1, l.get( 0 ) );
Assert.assertEquals( action2, l.get( 1 ) );
Assert.assertEquals( action3, l.get( 2 ) );
Assert.assertEquals( action4, l.get( 3 ) );
}
}

View File

@ -19,7 +19,6 @@ import org.junit.Test;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
@ -70,7 +69,6 @@ public class ByteArrayIdTest extends BaseCoreFunctionalTestCase {
*/
@Test
@TestForIssue(jiraKey = "HHH-8999")
@FailureExpected(jiraKey = "HHH-8999")
public void testMultipleDeletions() {
Session s = openSession();
s.getTransaction().begin();
@ -94,7 +92,6 @@ public class ByteArrayIdTest extends BaseCoreFunctionalTestCase {
*/
@Test
@TestForIssue(jiraKey = "HHH-8999")
@FailureExpected(jiraKey = "HHH-8999")
public void testMultipleUpdates() {
Session s = openSession();
s.getTransaction().begin();

View File

@ -19,7 +19,6 @@ import org.junit.Test;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
@ -70,7 +69,6 @@ public class CharacterArrayIdTest extends BaseCoreFunctionalTestCase {
*/
@Test
@TestForIssue(jiraKey = "HHH-8999")
@FailureExpected(jiraKey = "HHH-8999")
public void testMultipleDeletions() {
Session s = openSession();
s.getTransaction().begin();
@ -94,7 +92,6 @@ public class CharacterArrayIdTest extends BaseCoreFunctionalTestCase {
*/
@Test
@TestForIssue(jiraKey = "HHH-8999")
@FailureExpected(jiraKey = "HHH-8999")
public void testMultipleUpdates() {
Session s = openSession();
s.getTransaction().begin();

View File

@ -19,7 +19,6 @@ import org.junit.Test;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
@ -70,7 +69,6 @@ public class PrimitiveByteArrayIdTest extends BaseCoreFunctionalTestCase {
*/
@Test
@TestForIssue(jiraKey = "HHH-8999")
@FailureExpected(jiraKey = "HHH-8999")
public void testMultipleDeletions() {
Session s = openSession();
s.getTransaction().begin();
@ -94,7 +92,6 @@ public class PrimitiveByteArrayIdTest extends BaseCoreFunctionalTestCase {
*/
@Test
@TestForIssue(jiraKey = "HHH-8999")
@FailureExpected(jiraKey = "HHH-8999")
public void testMultipleUpdates() {
Session s = openSession();
s.getTransaction().begin();

View File

@ -19,7 +19,6 @@ import org.junit.Test;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
@ -70,7 +69,6 @@ public class PrimitiveCharacterArrayIdTest extends BaseCoreFunctionalTestCase {
*/
@Test
@TestForIssue(jiraKey = "HHH-8999")
@FailureExpected(jiraKey = "HHH-8999")
public void testMultipleDeletions() {
Session s = openSession();
s.getTransaction().begin();
@ -94,7 +92,6 @@ public class PrimitiveCharacterArrayIdTest extends BaseCoreFunctionalTestCase {
*/
@Test
@TestForIssue(jiraKey = "HHH-8999")
@FailureExpected(jiraKey = "HHH-8999")
public void testMultipleUpdates() {
Session s = openSession();
s.getTransaction().begin();

View File

@ -24,7 +24,6 @@ import org.hibernate.Session;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.hibernate.type.LongType;
@ -34,7 +33,6 @@ public class UserTypeNonComparableIdTest extends BaseCoreFunctionalTestCase {
@Test
@TestForIssue(jiraKey = "HHH-8999")
@FailureExpected(jiraKey = "HHH-8999")
public void testUserTypeId() {
Session s = openSession();
s.beginTransaction();