10
10
import java .io .ObjectInputStream ;
11
11
import java .io .ObjectOutputStream ;
12
12
import java .io .Serializable ;
13
+ import java .util .ArrayList ;
13
14
import java .util .HashMap ;
14
15
import java .util .HashSet ;
15
16
import java .util .LinkedHashMap ;
16
17
import java .util .LinkedList ;
17
18
import java .util .List ;
18
19
import java .util .Map ;
20
+ import java .util .Objects ;
19
21
import java .util .Queue ;
20
22
import java .util .Set ;
21
23
import java .util .concurrent .ConcurrentLinkedQueue ;
44
46
import org .hibernate .internal .CoreMessageLogger ;
45
47
import org .hibernate .proxy .HibernateProxy ;
46
48
import org .hibernate .proxy .LazyInitializer ;
49
+ import org .hibernate .type .CollectionType ;
50
+ import org .hibernate .type .EntityType ;
47
51
import org .hibernate .type .Type ;
48
52
49
53
/**
@@ -1008,12 +1012,56 @@ private static class InsertActionSorter implements ExecutableList.Sorter<Abstrac
1008
1012
*/
1009
1013
public static final InsertActionSorter INSTANCE = new InsertActionSorter ();
1010
1014
1015
+ private static class BatchIdentifier {
1016
+
1017
+ private final String entityName ;
1018
+
1019
+ private Set <String > parentEntityNames = new HashSet <>( );
1020
+
1021
+ private Set <String > childEntityNames = new HashSet <>( );
1022
+
1023
+ public BatchIdentifier (
1024
+ String entityName ) {
1025
+ this .entityName = entityName ;
1026
+ }
1027
+
1028
+ @ Override
1029
+ public boolean equals (Object o ) {
1030
+ if ( this == o ) {
1031
+ return true ;
1032
+ }
1033
+ if ( !( o instanceof BatchIdentifier ) ) {
1034
+ return false ;
1035
+ }
1036
+ BatchIdentifier that = (BatchIdentifier ) o ;
1037
+ return Objects .equals ( entityName , that .entityName );
1038
+ }
1039
+
1040
+ @ Override
1041
+ public int hashCode () {
1042
+ return Objects .hash ( entityName );
1043
+ }
1044
+
1045
+ public String getEntityName () {
1046
+ return entityName ;
1047
+ }
1048
+
1049
+ public Set <String > getParentEntityNames () {
1050
+ return parentEntityNames ;
1051
+ }
1052
+
1053
+ public Set <String > getChildEntityNames () {
1054
+ return childEntityNames ;
1055
+ }
1056
+ }
1057
+
1011
1058
// the mapping of entity names to their latest batch numbers.
1012
- private Map <String , Integer > latestBatches ;
1013
- private Map <Object , Integer > entityBatchNumber ;
1059
+ private List <BatchIdentifier > latestBatches ;
1060
+
1061
+ private Map <Object , BatchIdentifier > entityBatchIdentifier ;
1014
1062
1015
1063
// the map of batch numbers to EntityInsertAction lists
1016
- private Map <Integer , List <AbstractEntityInsertAction >> actionBatches ;
1064
+ private Map <BatchIdentifier , List <AbstractEntityInsertAction >> actionBatches ;
1017
1065
1018
1066
public InsertActionSorter () {
1019
1067
}
@@ -1023,100 +1071,99 @@ public InsertActionSorter() {
1023
1071
*/
1024
1072
public void sort (List <AbstractEntityInsertAction > insertions ) {
1025
1073
// optimize the hash size to eliminate a rehash.
1026
- this .latestBatches = new HashMap < String , Integer >( );
1027
- this .entityBatchNumber = new HashMap <Object , Integer >( insertions .size () + 1 , 1.0f );
1028
- this .actionBatches = new HashMap <Integer , List < AbstractEntityInsertAction > >();
1074
+ this .latestBatches = new ArrayList <>( );
1075
+ this .entityBatchIdentifier = new HashMap <>( insertions .size () + 1 , 1.0f );
1076
+ this .actionBatches = new HashMap <>();
1029
1077
1030
- // the list of entity names that indicate the batch number
1031
1078
for ( AbstractEntityInsertAction action : insertions ) {
1032
- // remove the current element from insertions. It will be added back later.
1033
- String entityName = action .getEntityName ();
1079
+ BatchIdentifier batchIdentifier = new BatchIdentifier ( action .getEntityName () );
1034
1080
1035
1081
// the entity associated with the current action.
1036
1082
Object currentEntity = action .getInstance ();
1083
+ int index = latestBatches .indexOf ( batchIdentifier );
1037
1084
1038
- Integer batchNumber ;
1039
- if ( latestBatches .containsKey ( entityName ) ) {
1040
- // There is already an existing batch for this type of entity.
1041
- // Check to see if the latest batch is acceptable.
1042
- batchNumber = findBatchNumber ( action , entityName );
1085
+ if ( index != -1 ) {
1086
+ batchIdentifier = latestBatches .get ( index );
1043
1087
}
1044
1088
else {
1045
- // add an entry for this type of entity.
1046
- // we can be assured that all referenced entities have already
1047
- // been processed,
1048
- // so specify that this entity is with the latest batch.
1049
- // doing the batch number beforeQuery adding the name to the list is
1050
- // a faster way to get an accurate number.
1051
-
1052
- batchNumber = actionBatches .size ();
1053
- latestBatches .put ( entityName , batchNumber );
1089
+ latestBatches .add ( batchIdentifier );
1054
1090
}
1055
- entityBatchNumber .put ( currentEntity , batchNumber );
1056
- addToBatch ( batchNumber , action );
1091
+ addParentChildEntityNames ( action , batchIdentifier );
1092
+ entityBatchIdentifier .put ( currentEntity , batchIdentifier );
1093
+ addToBatch (batchIdentifier , action );
1057
1094
}
1058
1095
insertions .clear ();
1059
1096
1097
+ for ( int i = 0 ; i < latestBatches .size (); i ++ ) {
1098
+ BatchIdentifier batchIdentifier = latestBatches .get ( i );
1099
+ String entityName = batchIdentifier .getEntityName ();
1100
+
1101
+ //Make sure that child entries are not before parents
1102
+ for ( int j = i - 1 ; j >= 0 ; j -- ) {
1103
+ BatchIdentifier prevBatchIdentifier = latestBatches .get ( j );
1104
+ if (prevBatchIdentifier .getParentEntityNames ().contains ( entityName )) {
1105
+ latestBatches .remove ( i );
1106
+ latestBatches .add ( j , batchIdentifier );
1107
+ }
1108
+ }
1109
+
1110
+ //Make sure that parent entries are not after children
1111
+ for ( int j = i + 1 ; j < latestBatches .size (); j ++ ) {
1112
+ BatchIdentifier nextBatchIdentifier = latestBatches .get ( j );
1113
+ //Take care of unidirectional @OneToOne associations but exclude bidirectional @ManyToMany
1114
+ if (nextBatchIdentifier .getChildEntityNames ().contains ( entityName ) &&
1115
+ !batchIdentifier .getChildEntityNames ().contains ( nextBatchIdentifier .getEntityName () )) {
1116
+ latestBatches .remove ( i );
1117
+ latestBatches .add ( j , batchIdentifier );
1118
+ }
1119
+ }
1120
+ }
1121
+
1060
1122
// now rebuild the insertions list. There is a batch for each entry in the name list.
1061
- for ( int i = 0 ; i < actionBatches . size (); i ++ ) {
1062
- List <AbstractEntityInsertAction > batch = actionBatches .get ( i );
1123
+ for ( BatchIdentifier rootIdentifier : latestBatches ) {
1124
+ List <AbstractEntityInsertAction > batch = actionBatches .get ( rootIdentifier );
1063
1125
insertions .addAll ( batch );
1064
1126
}
1065
1127
}
1066
1128
1067
1129
/**
1068
- * Finds an acceptable batch for this entity to be a member as part of the {@link InsertActionSorter}
1130
+ * Add parent and child entity names so that we know how to rearrange dependencies
1069
1131
*
1070
1132
* @param action The action being sorted
1071
- * @param entityName The name of the entity affected by the action
1072
- * @return An appropriate batch number; todo document this process better
1133
+ * @param batchIdentifier The batch identifier of the entity affected by the action
1073
1134
*/
1074
- private Integer findBatchNumber (AbstractEntityInsertAction action , String entityName ) {
1075
- // loop through all the associated entities and make sure they have been
1076
- // processed beforeQuery the latest
1077
- // batch associated with this entity type.
1078
-
1079
- // the current batch number is the latest batch for this entity type.
1080
- Integer latestBatchNumberForType = latestBatches .get ( entityName );
1081
-
1082
- // loop through all the associations of the current entity and make sure that they are processed
1083
- // beforeQuery the current batch number
1135
+ private void addParentChildEntityNames (AbstractEntityInsertAction action , BatchIdentifier batchIdentifier ) {
1084
1136
Object [] propertyValues = action .getState ();
1085
1137
Type [] propertyTypes = action .getPersister ().getClassMetadata ().getPropertyTypes ();
1086
1138
1087
1139
for ( int i = 0 ; i < propertyValues .length ; i ++ ) {
1088
1140
Object value = propertyValues [i ];
1089
1141
Type type = propertyTypes [i ];
1090
1142
if ( type .isEntityType () && value != null ) {
1091
- // find the batch number associated with the current association, if any.
1092
- Integer associationBatchNumber = entityBatchNumber .get ( value );
1093
- if ( associationBatchNumber != null && associationBatchNumber .compareTo ( latestBatchNumberForType ) > 0 ) {
1094
- // create a new batch for this type. The batch number is the number of current batches.
1095
- latestBatchNumberForType = actionBatches .size ();
1096
- latestBatches .put ( entityName , latestBatchNumberForType );
1097
- // since this entity will now be processed in the latest possible batch,
1098
- // we can be assured that it will come afterQuery all other associations,
1099
- // there's not need to continue checking.
1100
- break ;
1101
- }
1143
+ EntityType entityType = (EntityType ) type ;
1144
+ String entityName = entityType .getName ();
1145
+ batchIdentifier .getParentEntityNames ().add ( entityName );
1146
+ }
1147
+ else if ( type .isCollectionType () && value != null ) {
1148
+ CollectionType collectionType = (CollectionType ) type ;
1149
+ String entityName = collectionType .getAssociatedEntityName ( ( (SessionImplementor ) action .getSession () ).getSessionFactory () );
1150
+ batchIdentifier .getChildEntityNames ().add ( entityName );
1102
1151
}
1103
1152
}
1104
- return latestBatchNumberForType ;
1105
1153
}
1106
1154
1107
- private void addToBatch (Integer batchNumber , AbstractEntityInsertAction action ) {
1108
- List <AbstractEntityInsertAction > actions = actionBatches .get ( batchNumber );
1155
+ private void addToBatch (BatchIdentifier batchIdentifier , AbstractEntityInsertAction action ) {
1156
+ List <AbstractEntityInsertAction > actions = actionBatches .get ( batchIdentifier );
1109
1157
1110
1158
if ( actions == null ) {
1111
- actions = new LinkedList <AbstractEntityInsertAction >();
1112
- actionBatches .put ( batchNumber , actions );
1159
+ actions = new LinkedList <>();
1160
+ actionBatches .put ( batchIdentifier , actions );
1113
1161
}
1114
1162
actions .add ( action );
1115
1163
}
1116
1164
1117
1165
}
1118
1166
1119
-
1120
1167
private static abstract class ListProvider <T extends Executable & Comparable & Serializable > {
1121
1168
abstract ExecutableList <T > get (ActionQueue instance );
1122
1169
abstract ExecutableList <T > init (ActionQueue instance );
0 commit comments