HHH-16717 Type pollution fix for ExecutableList having to implement Comparable

This commit is contained in:
Sanne Grinovero 2023-05-26 17:14:21 +01:00 committed by Sanne Grinovero
parent 7fc927194b
commit 9da4ef0239
7 changed files with 108 additions and 41 deletions

View File

@ -10,11 +10,11 @@ import java.io.Serializable;
import org.hibernate.action.spi.AfterTransactionCompletionProcess;
import org.hibernate.action.spi.BeforeTransactionCompletionProcess;
import org.hibernate.action.spi.Executable;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.spi.access.CollectionDataAccess;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.spi.ComparableExecutable;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.event.spi.EventSource;
import org.hibernate.internal.FastSessionServices;
@ -27,7 +27,7 @@ import org.hibernate.pretty.MessageHelper;
*
* @author Gavin King
*/
public abstract class CollectionAction implements Executable, Serializable, Comparable<CollectionAction> {
public abstract class CollectionAction implements ComparableExecutable {
private transient CollectionPersister persister;
private transient EventSource session;
@ -122,6 +122,16 @@ public abstract class CollectionAction implements Executable, Serializable, Comp
return finalKey;
}
@Override
public String getPrimarySortClassifier() {
return collectionRole;
}
@Override
public Object getSecondarySortIndex() {
return key;
}
protected final EventSource getSession() {
return session;
}
@ -145,15 +155,15 @@ public abstract class CollectionAction implements Executable, Serializable, Comp
}
@Override
public int compareTo(CollectionAction action) {
public int compareTo(ComparableExecutable o) {
// sort first by role name
final int roleComparison = collectionRole.compareTo( action.collectionRole );
final int roleComparison = collectionRole.compareTo( o.getPrimarySortClassifier() );
if ( roleComparison != 0 ) {
return roleComparison;
}
else {
//then by fk
return persister.getAttributeMapping().getKeyDescriptor().compare( key, action.key );
return persister.getAttributeMapping().getKeyDescriptor().compare( key, o.getSecondarySortIndex() );
// //noinspection unchecked
// final JavaType<Object> javaType = (JavaType<Object>) persister.getAttributeMapping().getKeyDescriptor().getJavaType();
// return javaType.getComparator().compare( key, action.key );

View File

@ -11,7 +11,7 @@ import java.io.Serializable;
import org.hibernate.AssertionFailure;
import org.hibernate.action.spi.AfterTransactionCompletionProcess;
import org.hibernate.action.spi.BeforeTransactionCompletionProcess;
import org.hibernate.action.spi.Executable;
import org.hibernate.engine.spi.ComparableExecutable;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.event.spi.EventSource;
import org.hibernate.internal.FastSessionServices;
@ -26,7 +26,7 @@ import org.hibernate.pretty.MessageHelper;
* @author Gavin King
*/
public abstract class EntityAction
implements Comparable<EntityAction>, Executable, Serializable, AfterTransactionCompletionProcess {
implements ComparableExecutable, AfterTransactionCompletionProcess {
private final String entityName;
private final Object id;
@ -151,13 +151,23 @@ public abstract class EntityAction
}
@Override
public int compareTo(EntityAction action) {
public int compareTo(ComparableExecutable o) {
//sort first by entity name
final int roleComparison = entityName.compareTo( action.getEntityName() );
final int roleComparison = entityName.compareTo( o.getPrimarySortClassifier() );
return roleComparison != 0
? roleComparison
//then by id
: persister.getIdentifierType().compare( id, action.getId(), session.getSessionFactory() );
: persister.getIdentifierType().compare( id, o.getSecondarySortIndex(), session.getSessionFactory() );
}
@Override
public String getPrimarySortClassifier() {
return entityName;
}
@Override
public Object getSecondarySortIndex() {
return id;
}
/**

View File

@ -592,7 +592,7 @@ public class ActionQueue {
* @param list The list of Executable elements to be performed
*
*/
private <E extends Executable & Comparable<? super E> & Serializable> void executeActions(ExecutableList<E> list)
private <E extends ComparableExecutable> void executeActions(ExecutableList<E> list)
throws HibernateException {
if ( list == null || list.isEmpty() ) {
return;
@ -601,7 +601,7 @@ public class ActionQueue {
// 1) we explicitly iterate list here to perform Executable#execute()
// 2) ExecutableList#getQuerySpaces also iterates the Executables to collect query spaces.
try {
for ( E e : list ) {
for ( ComparableExecutable e : list ) {
try {
e.execute();
}
@ -1354,7 +1354,7 @@ public class ActionQueue {
}
private abstract static class ListProvider<T extends Executable & Comparable<? super T> & Serializable> {
private abstract static class ListProvider<T extends ComparableExecutable> {
abstract ExecutableList<T> get(ActionQueue instance);
abstract ExecutableList<T> init(ActionQueue instance);
ExecutableList<T> getOrInit( ActionQueue instance ) {

View File

@ -0,0 +1,35 @@
/*
* 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.Serializable;
import org.hibernate.action.spi.Executable;
/**
* We frequently need the union type of Executable, Comparable of ComparableExecutable, Serializable;
* this interface represents such union; this helps to simplify several generic signatures.
* Secondarily, it helps to avoid triggering type pollution by not needing to typecheck
* for a very specific Comparable type; we represent the common needs to resolve sorting
* by exposing primary and secondary sorting attributes.
*/
public interface ComparableExecutable extends Executable, Comparable<ComparableExecutable>, Serializable {
/**
* This affect sorting order of the executables, when sorting is enabled.
* @return the primary sorting attribute; typically the entity name or collection role.
*/
String getPrimarySortClassifier();
/**
* This affect sorting order of the executables, when sorting is enabled.
* @return the secondary sorting attribute, applied when getPrimarySortClassifier
* matches during a comparison; typically the entity key or collection key.
*/
Object getSecondarySortIndex();
}

View File

@ -29,24 +29,20 @@ import org.hibernate.internal.util.collections.CollectionHelper;
*
* @author Steve Ebersole
* @author Anton Marsden
*
* @param <E> Intersection type describing {@link Executable} implementations
*/
public class ExecutableList<E extends Executable & Comparable<? super E> & Serializable>
public class ExecutableList<E extends ComparableExecutable>
implements Serializable, Iterable<E>, Externalizable {
public static final int INIT_QUEUE_LIST_SIZE = 5;
/**
* Provides a sorting interface for {@link ExecutableList}.
*
* @param <E>
*/
public interface Sorter<E extends Executable> {
public interface Sorter<ComparableExecutable> {
/**
* Sorts the list.
*/
void sort(List<E> l);
void sort(List<ComparableExecutable> l);
}
private final ArrayList<E> executables;
@ -123,7 +119,7 @@ public class ExecutableList<E extends Executable & Comparable<? super E> & Seria
*/
public Set<Serializable> getQuerySpaces() {
if ( querySpaces == null ) {
for ( E e : executables ) {
for ( ComparableExecutable e : executables ) {
Serializable[] propertySpaces = e.getPropertySpaces();
if ( propertySpaces != null && propertySpaces.length > 0 ) {
if( querySpaces == null ) {
@ -132,7 +128,7 @@ public class ExecutableList<E extends Executable & Comparable<? super E> & Seria
Collections.addAll( querySpaces, propertySpaces );
}
}
if( querySpaces == null ) {
if ( querySpaces == null ) {
return Collections.emptySet();
}
}
@ -153,10 +149,10 @@ public class ExecutableList<E extends Executable & Comparable<? super E> & Seria
*
* @return the entry that was removed
*/
public E remove(int index) {
public ComparableExecutable remove(int index) {
// removals are generally safe with regard to sorting...
final E e = executables.remove( index );
final ComparableExecutable e = executables.remove( index );
// If the executable being removed defined query spaces we need to recalculate the overall query spaces for
// this list. The problem is that we don't know how many other executable instances in the list also
@ -187,7 +183,7 @@ public class ExecutableList<E extends Executable & Comparable<? super E> & Seria
public void removeLastN(int n) {
if ( n > 0 ) {
int size = executables.size();
for ( Executable e : executables.subList( size - n, size ) ) {
for ( ComparableExecutable e : executables.subList( size - n, size ) ) {
if ( e.getPropertySpaces() != null && e.getPropertySpaces().length > 0 ) {
// querySpaces could now be incorrect
querySpaces = null;
@ -206,7 +202,7 @@ public class ExecutableList<E extends Executable & Comparable<? super E> & Seria
* @return true if the object was added to the list
*/
public boolean add(E executable) {
final E previousLast = sorter != null || executables.isEmpty() ? null : executables.get( executables.size() - 1 );
final ComparableExecutable previousLast = sorter != null || executables.isEmpty() ? null : executables.get( executables.size() - 1 );
boolean added = executables.add( executable );
if ( !added ) {
@ -291,7 +287,7 @@ public class ExecutableList<E extends Executable & Comparable<? super E> & Seria
oos.writeBoolean( sorted );
oos.writeInt( executables.size() );
for ( E e : executables ) {
for ( ComparableExecutable e : executables ) {
oos.writeObject( e );
}
@ -347,7 +343,7 @@ public class ExecutableList<E extends Executable & Comparable<? super E> & Seria
* @param session The session with which to associate the {@code Executable}s
*/
public void afterDeserialize(EventSource session) {
for ( E e : executables ) {
for ( ComparableExecutable e : executables ) {
e.afterDeserialize( session );
}
}

View File

@ -18,9 +18,8 @@ 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.engine.spi.ComparableExecutable;
import org.hibernate.engine.spi.ExecutableList;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.event.spi.EventSource;
import org.hibernate.testing.orm.junit.BaseUnitTest;
@ -38,7 +37,7 @@ import static org.assertj.core.api.Assertions.assertThat;
public class NonSortedExecutableListTest {
// For testing, we need an Executable that is also Comparable and Serializable
private static class AnExecutable implements Executable, Comparable<AnExecutable>, Serializable {
private static class AnExecutable implements ComparableExecutable {
private final int n;
private final Serializable[] spaces;
@ -55,8 +54,9 @@ public class NonSortedExecutableListTest {
}
@Override
public int compareTo(AnExecutable o) {
return Integer.compare( n, o.n );
public int compareTo(ComparableExecutable o) {
Integer index = (Integer) o.getSecondarySortIndex();
return Integer.compare( n, index.intValue() );
}
@Override
@ -106,6 +106,15 @@ public class NonSortedExecutableListTest {
return String.valueOf(n);
}
@Override
public String getPrimarySortClassifier() {
return toString();
}
@Override
public Object getSecondarySortIndex() {
return Integer.valueOf( n );
}
}
private ExecutableList<AnExecutable> actionList;
@ -303,4 +312,3 @@ public class NonSortedExecutableListTest {
assertThat( actionList ).element( 3 ).extracting( AnExecutable::wasAfterDeserializeCalled ).isEqualTo( true );
}
}

View File

@ -18,9 +18,8 @@ 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.engine.spi.ComparableExecutable;
import org.hibernate.engine.spi.ExecutableList;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.event.spi.EventSource;
import org.hibernate.testing.orm.junit.BaseUnitTest;
@ -37,7 +36,7 @@ import static org.assertj.core.api.Assertions.assertThat;
public class SortedExecutableListTest {
// For testing, we need an Executable that is also Comparable and Serializable
private static class AnExecutable implements Executable, Comparable<AnExecutable>, Serializable {
private static class AnExecutable implements ComparableExecutable {
private final int n;
private Serializable[] spaces;
@ -53,8 +52,9 @@ public class SortedExecutableListTest {
}
@Override
public int compareTo(AnExecutable o) {
return Integer.compare( n, o.n );
public int compareTo(ComparableExecutable o) {
Integer index = (Integer) o.getSecondarySortIndex();
return Integer.compare( n, index.intValue() );
}
@Override
@ -101,9 +101,18 @@ public class SortedExecutableListTest {
}
public String toString() {
return String.valueOf(n);
return String.valueOf( n );
}
@Override
public String getPrimarySortClassifier() {
return toString();
}
@Override
public Object getSecondarySortIndex() {
return Integer.valueOf( n );
}
}
private ExecutableList<AnExecutable> actionList;
@ -288,4 +297,3 @@ public class SortedExecutableListTest {
assertThat( actionList ).element( 3 ).isEqualTo( action4 );
}
}