| 
13 | 13 | import org.hibernate.loader.ast.spi.MultiNaturalIdLoadOptions;  | 
14 | 14 | import org.hibernate.loader.ast.spi.MultiNaturalIdLoader;  | 
15 | 15 | import org.hibernate.metamodel.mapping.EntityMappingType;  | 
 | 16 | +import org.hibernate.metamodel.mapping.NaturalIdMapping;  | 
16 | 17 | 
 
  | 
17 |  | -import java.util.Collections;  | 
18 | 18 | import java.util.List;  | 
19 | 19 | 
 
  | 
 | 20 | +import static java.util.Collections.emptyList;  | 
20 | 21 | import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;  | 
21 | 22 | import static org.hibernate.internal.util.collections.CollectionHelper.isEmpty;  | 
22 | 23 | import static org.hibernate.loader.ast.internal.LoaderHelper.upgradeLock;  | 
 | 24 | +import static org.hibernate.loader.ast.internal.MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER;  | 
23 | 25 | 
 
  | 
24 | 26 | /**  | 
25 | 27 |  * @author Jan Schatteman  | 
26 | 28 |  */  | 
27 | 29 | public abstract class AbstractMultiNaturalIdLoader<E> implements MultiNaturalIdLoader<E> {  | 
28 | 30 | 	private final EntityMappingType entityDescriptor;  | 
29 | 31 | 
 
  | 
30 |  | -	protected MultiNaturalIdLoadOptions options;  | 
31 |  | - | 
32 | 32 | 	public AbstractMultiNaturalIdLoader(EntityMappingType entityDescriptor) {  | 
33 | 33 | 		this.entityDescriptor = entityDescriptor;  | 
34 | 34 | 	}  | 
35 | 35 | 
 
  | 
36 | 36 | 	@Override  | 
37 | 37 | 	public <K> List<E> multiLoad(K[] naturalIds, MultiNaturalIdLoadOptions options, SharedSessionContractImplementor session) {  | 
38 | 38 | 		assert naturalIds != null;  | 
39 |  | - | 
40 |  | -		this.options = options;  | 
41 |  | - | 
42 | 39 | 		if ( naturalIds.length == 0 ) {  | 
43 |  | -			return Collections.emptyList();  | 
 | 40 | +			return emptyList();  | 
 | 41 | +		}  | 
 | 42 | +		else {  | 
 | 43 | +			return options.isOrderReturnEnabled()  | 
 | 44 | +					? performOrderedMultiLoad( naturalIds, options, session )  | 
 | 45 | +					: performUnorderedMultiLoad( naturalIds, options, session );  | 
44 | 46 | 		}  | 
45 |  | - | 
46 |  | -		return options.isOrderReturnEnabled()  | 
47 |  | -				? performOrderedMultiLoad( naturalIds, options, session )  | 
48 |  | -				: performUnorderedMultiLoad( naturalIds, options, session );  | 
49 | 47 | 	}  | 
50 | 48 | 
 
  | 
51 |  | -	private <K> List<E> performUnorderedMultiLoad(K[] naturalIds, MultiNaturalIdLoadOptions options, SharedSessionContractImplementor session) {  | 
52 |  | -		if ( MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER.isTraceEnabled() ) {  | 
53 |  | -			MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER.tracef( "Unordered MultiLoad Starting - `%s`", getEntityDescriptor().getEntityName() );  | 
 | 49 | +	private <K> List<E> performUnorderedMultiLoad(  | 
 | 50 | +			K[] naturalIds,  | 
 | 51 | +			MultiNaturalIdLoadOptions loadOptions,  | 
 | 52 | +			SharedSessionContractImplementor session) {  | 
 | 53 | +		if ( MULTI_KEY_LOAD_LOGGER.isTraceEnabled() ) {  | 
 | 54 | +			MULTI_KEY_LOAD_LOGGER.tracef( "Unordered MultiLoad starting: "  | 
 | 55 | +					+ getEntityDescriptor().getEntityName() );  | 
54 | 56 | 		}  | 
 | 57 | +		return unorderedMultiLoad( naturalIds, loadOptions, session );  | 
 | 58 | +	}  | 
55 | 59 | 
 
  | 
56 |  | -		return unorderedMultiLoad(  | 
57 |  | -				naturalIds,  | 
58 |  | -				session,  | 
59 |  | -				options.getLockOptions() == null ? LockOptions.NONE : options.getLockOptions()  | 
60 |  | -		);  | 
 | 60 | +	private static LockOptions lockOptions(MultiNaturalIdLoadOptions loadOptions) {  | 
 | 61 | +		final LockOptions lockOptions = loadOptions.getLockOptions();  | 
 | 62 | +		return lockOptions == null ? LockOptions.NONE : lockOptions;  | 
61 | 63 | 	}  | 
62 | 64 | 
 
  | 
63 |  | -	protected <K> List<E> unorderedMultiLoad(K[] naturalIds, SharedSessionContractImplementor session, LockOptions lockOptions) {  | 
 | 65 | +	private <K> List<E> unorderedMultiLoad(  | 
 | 66 | +			K[] naturalIds,  | 
 | 67 | +			MultiNaturalIdLoadOptions loadOptions,  | 
 | 68 | +			SharedSessionContractImplementor session) {  | 
64 | 69 | 		final List<E> results = arrayList( naturalIds.length );  | 
 | 70 | +		final LockOptions lockOptions = lockOptions( loadOptions );  | 
65 | 71 | 		final Object[] unresolvedIds =  | 
66 |  | -				checkPersistenceContextForCachedResults( naturalIds, session, lockOptions, results );  | 
 | 72 | +				checkPersistenceContextForCachedResults( naturalIds, loadOptions, session, lockOptions, results );  | 
67 | 73 | 		if ( !isEmpty( unresolvedIds ) ) {  | 
68 |  | -			results.addAll( loadEntitiesWithUnresolvedIds(unresolvedIds, session, lockOptions) );  | 
 | 74 | +			results.addAll( loadEntitiesWithUnresolvedIds( unresolvedIds, loadOptions, lockOptions, session ) );  | 
69 | 75 | 		}  | 
70 |  | - | 
71 | 76 | 		return results;  | 
72 | 77 | 	}  | 
73 | 78 | 
 
  | 
74 |  | -	protected abstract List<E> loadEntitiesWithUnresolvedIds(Object[] unresolvedIds, SharedSessionContractImplementor session, LockOptions lockOptions);  | 
 | 79 | +	protected abstract List<E> loadEntitiesWithUnresolvedIds(  | 
 | 80 | +			Object[] unresolvedIds,  | 
 | 81 | +			MultiNaturalIdLoadOptions loadOptions,  | 
 | 82 | +			LockOptions lockOptions,  | 
 | 83 | +			SharedSessionContractImplementor session);  | 
75 | 84 | 
 
  | 
76 | 85 | 	private <K> List<E> performOrderedMultiLoad(K[] naturalIds, MultiNaturalIdLoadOptions options, SharedSessionContractImplementor session) {  | 
77 |  | -		if ( MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER.isTraceEnabled() ) {  | 
78 |  | -			MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER.tracef( "Ordered MultiLoad Starting - `%s`", getEntityDescriptor().getEntityName() );  | 
 | 86 | +		if ( MULTI_KEY_LOAD_LOGGER.isTraceEnabled() ) {  | 
 | 87 | +			MULTI_KEY_LOAD_LOGGER.trace( "Ordered MultiLoad starting: "  | 
 | 88 | +					+ getEntityDescriptor().getEntityName() );  | 
79 | 89 | 		}  | 
80 |  | - | 
81 |  | -		return orderedMultiLoad(  | 
82 |  | -				naturalIds,  | 
83 |  | -				session,  | 
84 |  | -				options.getLockOptions() == null ? LockOptions.NONE : options.getLockOptions()  | 
85 |  | -		);  | 
 | 90 | +		return orderedMultiLoad( naturalIds, options, session );  | 
86 | 91 | 	}  | 
87 | 92 | 
 
  | 
88 |  | -	protected <K> List<E> orderedMultiLoad( K[] naturalIds, SharedSessionContractImplementor session, LockOptions lockOptions ) {  | 
89 |  | - | 
90 |  | -		unorderedMultiLoad( naturalIds, session, lockOptions );  | 
91 |  | - | 
92 |  | -		return sortResults( naturalIds, session, lockOptions );  | 
 | 93 | +	private <K> List<E> orderedMultiLoad(  | 
 | 94 | +			K[] naturalIds,  | 
 | 95 | +			MultiNaturalIdLoadOptions loadOptions,  | 
 | 96 | +			SharedSessionContractImplementor session) {  | 
 | 97 | +		unorderedMultiLoad( naturalIds, loadOptions, session );  | 
 | 98 | +		return sortResults( naturalIds, loadOptions, session );  | 
93 | 99 | 	}  | 
94 | 100 | 
 
  | 
95 |  | -	protected <K> List<E> sortResults( K[] naturalIds, SharedSessionContractImplementor session, LockOptions lockOptions ) {  | 
96 |  | -		List<E> results = arrayList(naturalIds.length);  | 
97 |  | -		for ( int i = 0; i < naturalIds.length; i++ ) {  | 
98 |  | -			final PersistenceContext persistenceContext = session.getPersistenceContextInternal();  | 
99 |  | - | 
100 |  | -			Object id = persistenceContext.getNaturalIdResolutions().findCachedIdByNaturalId( naturalIds[i], getEntityDescriptor() );  | 
101 |  | - | 
102 |  | -			// Id can be null if a non-existent natural id is requested  | 
103 |  | -			Object entity = (id == null) ? null : persistenceContext.getEntity( new EntityKey( id, getEntityDescriptor().getEntityPersister() ) );  | 
104 |  | -			if ( entity != null && !options.isReturnOfDeletedEntitiesEnabled() ) {  | 
105 |  | -				// make sure it is not DELETED  | 
106 |  | -				final EntityEntry entry = persistenceContext.getEntry( entity );  | 
107 |  | -				if ( entry.getStatus().isDeletedOrGone() ) {  | 
108 |  | -					// the entity is locally deleted, and the options ask that we not return such entities...  | 
109 |  | -					entity = null;  | 
110 |  | -				}  | 
111 |  | -				else {  | 
112 |  | -					entity = persistenceContext.proxyFor( entity );  | 
113 |  | -				}  | 
 | 101 | +	private <K> List<E> sortResults(  | 
 | 102 | +			K[] naturalIds,  | 
 | 103 | +			MultiNaturalIdLoadOptions loadOptions,  | 
 | 104 | +			SharedSessionContractImplementor session) {  | 
 | 105 | +		final PersistenceContext context = session.getPersistenceContextInternal();  | 
 | 106 | +		final List<E> results = arrayList( naturalIds.length );  | 
 | 107 | +		for ( K naturalId : naturalIds ) {  | 
 | 108 | +			final Object entity = entityForNaturalId( context, naturalId );  | 
 | 109 | +			final Object result;  | 
 | 110 | +			if ( entity == null  | 
 | 111 | +				// the entity is locally deleted, and the options ask that we not return such entities  | 
 | 112 | +				|| !loadOptions.isReturnOfDeletedEntitiesEnabled()  | 
 | 113 | +					&& context.getEntry( entity ).getStatus().isDeletedOrGone() ) {  | 
 | 114 | +				result = null;  | 
114 | 115 | 			}  | 
115 |  | -			results.add( (E) entity );  | 
 | 116 | +			else {  | 
 | 117 | +				result = context.proxyFor( entity );  | 
 | 118 | +			}  | 
 | 119 | +			results.add( (E) result );  | 
116 | 120 | 		}  | 
117 |  | - | 
118 | 121 | 		return results;  | 
119 | 122 | 	}  | 
120 | 123 | 
 
  | 
121 |  | -	private <K> Object[] checkPersistenceContextForCachedResults( K[] naturalIds, SharedSessionContractImplementor session, LockOptions lockOptions, List<E> results ) {  | 
122 |  | -		List<K> unresolvedIds = arrayList(naturalIds.length);  | 
123 |  | - | 
124 |  | -		final PersistenceContext persistenceContext = session.getPersistenceContextInternal();  | 
125 |  | -		for ( int i = 0; i < naturalIds.length; i++ ) {  | 
126 |  | - | 
127 |  | -			final Object normalizedNaturalId = getEntityDescriptor().getNaturalIdMapping().normalizeInput( naturalIds[i] );  | 
128 |  | -			Object id = persistenceContext.getNaturalIdResolutions().findCachedIdByNaturalId( normalizedNaturalId, getEntityDescriptor() );  | 
129 |  | - | 
130 |  | -			// Id can be null if a non-existent natural id is requested, or a mutable natural id was changed and then deleted  | 
131 |  | -			Object entity = id == null ? null : persistenceContext.getEntity( new EntityKey( id, getEntityDescriptor().getEntityPersister() ) );  | 
 | 124 | +	private <K> Object entityForNaturalId(PersistenceContext context, K naturalId) {  | 
 | 125 | +		final EntityMappingType descriptor = getEntityDescriptor();  | 
 | 126 | +		final Object id = context.getNaturalIdResolutions().findCachedIdByNaturalId( naturalId, descriptor );  | 
 | 127 | +		// id can be null if a non-existent natural id is requested, or a mutable natural id was changed and then deleted  | 
 | 128 | +		return id == null ? null : context.getEntity( new EntityKey( id, descriptor.getEntityPersister() ) );  | 
 | 129 | +	}  | 
132 | 130 | 
 
  | 
 | 131 | +	private <K> Object[] checkPersistenceContextForCachedResults(  | 
 | 132 | +			K[] naturalIds,  | 
 | 133 | +			MultiNaturalIdLoadOptions loadOptions,  | 
 | 134 | +			SharedSessionContractImplementor session,  | 
 | 135 | +			LockOptions lockOptions,  | 
 | 136 | +			List<E> results ) {  | 
 | 137 | +		final List<K> unresolvedIds = arrayList( naturalIds.length );  | 
 | 138 | +		final PersistenceContext context = session.getPersistenceContextInternal();  | 
 | 139 | +		final NaturalIdMapping naturalIdMapping = getEntityDescriptor().getNaturalIdMapping();  | 
 | 140 | +		for ( K naturalId : naturalIds ) {  | 
 | 141 | +			final Object entity = entityForNaturalId( context, naturalIdMapping.normalizeInput( naturalId ) );  | 
133 | 142 | 			if ( entity != null ) {  | 
134 | 143 | 				// Entity is already in the persistence context  | 
135 |  | -				final EntityEntry entry = persistenceContext.getEntry( entity );  | 
136 |  | -				// either a managed entry, or a deleted one with returnDeleted enabled  | 
137 |  | -				if ( !entry.getStatus().isDeletedOrGone() || options.isReturnOfDeletedEntitiesEnabled() ) {  | 
138 |  | -					results.add( (E) persistenceContext.proxyFor(entity) );  | 
139 |  | -					upgradeLock( entity, entry, lockOptions, session.getSession().asEventSource() );  | 
 | 144 | +				final EntityEntry entry = context.getEntry( entity );  | 
 | 145 | +				if ( loadOptions.isReturnOfDeletedEntitiesEnabled()  | 
 | 146 | +						|| !entry.getStatus().isDeletedOrGone() ) {  | 
 | 147 | +					// either a managed entry, or a deleted one with returnDeleted enabled  | 
 | 148 | +					upgradeLock( entity, entry, lockOptions, session );  | 
 | 149 | +					final Object result = context.proxyFor( entity );  | 
 | 150 | +					results.add( (E) result );  | 
140 | 151 | 				}  | 
141 | 152 | 			}  | 
142 | 153 | 			else {  | 
143 |  | -				// entity either doesn't exist or hasn't been loaded in the PC yet, in both cases we add the natural id  | 
144 |  | -				// to the ids that still need to be recovered, in case the id corresponds to a non-existent  | 
145 |  | -				// instance nothing will be in the results for it, which is ok in unordered multiload  | 
146 |  | -				unresolvedIds.add(naturalIds[i]);  | 
 | 154 | +				// entity either doesn't exist or hasn't been loaded in the PC yet, in both cases we add  | 
 | 155 | +				// the natural id to the ids that still need to be recovered, in case the id corresponds  | 
 | 156 | +				// to a nonexistent instance nothing will be in the results for it, which is ok in  | 
 | 157 | +				// unordered multiload  | 
 | 158 | +				unresolvedIds.add( naturalId );  | 
147 | 159 | 			}  | 
148 | 160 | 		}  | 
149 |  | - | 
150 |  | -		return unresolvedIds.toArray( new Object[0] );  | 
 | 161 | +		return unresolvedIds.toArray();  | 
151 | 162 | 	}  | 
152 | 163 | 
 
  | 
153 | 164 | 	@Override  | 
 | 
0 commit comments