1 /*
2  * Copyright 2008-2020 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      https://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.springframework.data.jpa.provider;
17
18 import static org.springframework.data.jpa.provider.JpaClassUtils.*;
19 import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.*;
20
21 import java.util.Collections;
22 import java.util.NoSuchElementException;
23
24 import javax.persistence.EntityManager;
25 import javax.persistence.Query;
26 import javax.persistence.metamodel.Metamodel;
27
28 import org.eclipse.persistence.jpa.JpaQuery;
29 import org.eclipse.persistence.queries.ScrollableCursor;
30 import org.hibernate.ScrollMode;
31 import org.hibernate.ScrollableResults;
32 import org.hibernate.proxy.HibernateProxy;
33 import org.springframework.data.util.CloseableIterator;
34 import org.springframework.lang.Nullable;
35 import org.springframework.transaction.support.TransactionSynchronizationManager;
36 import org.springframework.util.Assert;
37 import org.springframework.util.ConcurrentReferenceHashMap;
38
39 /**
40  * Enumeration representing persistence providers to be used.
41  *
42  * @author Oliver Gierke
43  * @author Thomas Darimont
44  * @author Mark Paluch
45  * @author Jens Schauder
46  */

47 public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor {
48
49     /**
50      * Hibernate persistence provider.
51      * <p>
52      * Since Hibernate 4.3 the location of the HibernateEntityManager moved to the org.hibernate.jpa package. In order to
53      * support both locations we interpret both classnames as a Hibernate {@code PersistenceProvider}.
54      *
55      * @see <a href="https://jira.spring.io/browse/DATAJPA-444">DATAJPA-444</a>
56      */

57     HIBERNATE(//
58             Collections.singletonList(HIBERNATE_ENTITY_MANAGER_INTERFACE), //
59             Collections.singletonList(HIBERNATE_JPA_METAMODEL_TYPE)) {
60
61         @Override
62         public String extractQueryString(Query query) {
63             return HibernateUtils.getHibernateQuery(query);
64         }
65
66         /**
67          * Return custom placeholder ({@code *}) as Hibernate does create invalid queries for count queries for objects with
68          * compound keys.
69          *
70          * @see <a href="https://hibernate.atlassian.net/browse/HHH-4044">HHH-4044</a>
71          * @see <a href="https://hibernate.atlassian.net/browse/HHH-3096">HHH-3096</a>
72          */

73         @Override
74         public String getCountQueryPlaceholder() {
75             return "*";
76         }
77
78         /*
79          * (non-Javadoc)
80          * @see org.springframework.data.jpa.repository.support.ProxyIdAccessor#isProxy(java.lang.Object)
81          */

82         @Override
83         public boolean shouldUseAccessorFor(Object entity) {
84             return entity instanceof HibernateProxy;
85         }
86
87         /*
88          * (non-Javadoc)
89          * @see org.springframework.data.jpa.repository.support.ProxyIdAccessor#getIdentifierFrom(java.lang.Object)
90          */

91         @Override
92         public Object getIdentifierFrom(Object entity) {
93             return ((HibernateProxy) entity).getHibernateLazyInitializer().getIdentifier();
94         }
95
96         /*
97          * (non-Javadoc)
98          * @see org.springframework.data.jpa.provider.PersistenceProvider#executeQueryWithResultStream(javax.persistence.Query)
99          */

100         @Override
101         public CloseableIterator<Object> executeQueryWithResultStream(Query jpaQuery) {
102             return new HibernateScrollableResultsIterator(jpaQuery);
103         }
104     },
105
106     /**
107      * EclipseLink persistence provider.
108      */

109     ECLIPSELINK(Collections.singleton(ECLIPSELINK_ENTITY_MANAGER_INTERFACE),
110             Collections.singleton(ECLIPSELINK_JPA_METAMODEL_TYPE)) {
111
112         @Override
113         public String extractQueryString(Query query) {
114             return ((JpaQuery<?>) query).getDatabaseQuery().getJPQLString();
115         }
116
117         /*
118          * (non-Javadoc)
119          * @see org.springframework.data.jpa.repository.support.ProxyIdAccessor#isProxy(java.lang.Object)
120          */

121         @Override
122         public boolean shouldUseAccessorFor(Object entity) {
123             return false;
124         }
125
126         /*
127          * (non-Javadoc)
128          * @see org.springframework.data.jpa.repository.support.ProxyIdAccessor#getIdentifierFrom(java.lang.Object)
129          */

130         @Nullable
131         @Override
132         public Object getIdentifierFrom(Object entity) {
133             return null;
134         }
135
136         /*
137          * (non-Javadoc)
138          * @see org.springframework.data.jpa.provider.PersistenceProvider#executeQueryWithResultStream(javax.persistence.Query)
139          */

140         @Override
141         public CloseableIterator<Object> executeQueryWithResultStream(Query jpaQuery) {
142             return new EclipseLinkScrollableResultsIterator<>(jpaQuery);
143         }
144     },
145
146     /**
147      * Unknown special provider. Use standard JPA.
148      */

149     GENERIC_JPA(Collections.singleton(GENERIC_JPA_ENTITY_MANAGER_INTERFACE), Collections.emptySet()) {
150
151         /*
152          * (non-Javadoc)
153          * @see org.springframework.data.jpa.repository.query.QueryExtractor#extractQueryString(javax.persistence.Query)
154          */

155         @Nullable
156         @Override
157         public String extractQueryString(Query query) {
158             return null;
159         }
160
161         /*
162          * (non-Javadoc)
163          * @see org.springframework.data.jpa.repository.support.PersistenceProvider#canExtractQuery()
164          */

165         @Override
166         public boolean canExtractQuery() {
167             return false;
168         }
169
170         /*
171          * (non-Javadoc)
172          * @see org.springframework.data.jpa.repository.support.ProxyIdAccessor#isProxy(java.lang.Object)
173          */

174         @Override
175         public boolean shouldUseAccessorFor(Object entity) {
176             return false;
177         }
178
179         /*
180          * (non-Javadoc)
181          * @see org.springframework.data.jpa.repository.support.ProxyIdAccessor#getIdentifierFrom(java.lang.Object)
182          */

183         @Nullable
184         @Override
185         public Object getIdentifierFrom(Object entity) {
186             return null;
187         }
188     };
189
190     static ConcurrentReferenceHashMap<Class<?>, PersistenceProvider> CACHE = new ConcurrentReferenceHashMap<>();
191     private final Iterable<String> entityManagerClassNames;
192     private final Iterable<String> metamodelClassNames;
193     /**
194      * Creates a new {@link PersistenceProvider}.
195      *
196      * @param entityManagerClassNames the names of the provider specific {@link EntityManager} implementations. Must not
197      *          be {@literal null} or empty.
198      * @param metamodelClassNames must not be {@literal null}.
199      */

200     PersistenceProvider(Iterable<String> entityManagerClassNames, Iterable<String> metamodelClassNames) {
201
202         this.entityManagerClassNames = entityManagerClassNames;
203         this.metamodelClassNames = metamodelClassNames;
204     }
205
206     /**
207      * Caches the given {@link PersistenceProvider} for the given source type.
208      *
209      * @param type must not be {@literal null}.
210      * @param provider must not be {@literal null}.
211      * @return the {@code PersistenceProvider} passed in as an argument. Guaranteed to be not {@code null}.
212      */

213     private static PersistenceProvider cacheAndReturn(Class<?> type, PersistenceProvider provider) {
214         CACHE.put(type, provider);
215         return provider;
216     }
217
218     /**
219      * Determines the {@link PersistenceProvider} from the given {@link EntityManager}. If no special one can be
220      * determined {@link #GENERIC_JPA} will be returned.
221      *
222      * @param em must not be {@literal null}.
223      * @return will never be {@literal null}.
224      */

225     public static PersistenceProvider fromEntityManager(EntityManager em) {
226
227         Assert.notNull(em, "EntityManager must not be null!");
228
229         Class<?> entityManagerType = em.getDelegate().getClass();
230         PersistenceProvider cachedProvider = CACHE.get(entityManagerType);
231
232         if (cachedProvider != null) {
233             return cachedProvider;
234         }
235
236         for (PersistenceProvider provider : values()) {
237             for (String entityManagerClassName : provider.entityManagerClassNames) {
238                 if (isEntityManagerOfType(em, entityManagerClassName)) {
239                     return cacheAndReturn(entityManagerType, provider);
240                 }
241             }
242         }
243
244         return cacheAndReturn(entityManagerType, GENERIC_JPA);
245     }
246
247     /**
248      * Determines the {@link PersistenceProvider} from the given {@link Metamodel}. If no special one can be determined
249      * {@link #GENERIC_JPA} will be returned.
250      *
251      * @param metamodel must not be {@literal null}.
252      * @return will never be {@literal null}.
253      */

254     public static PersistenceProvider fromMetamodel(Metamodel metamodel) {
255
256         Assert.notNull(metamodel, "Metamodel must not be null!");
257
258         Class<? extends Metamodel> metamodelType = metamodel.getClass();
259         PersistenceProvider cachedProvider = CACHE.get(metamodelType);
260
261         if (cachedProvider != null) {
262             return cachedProvider;
263         }
264
265         for (PersistenceProvider provider : values()) {
266             for (String metamodelClassName : provider.metamodelClassNames) {
267                 if (isMetamodelOfType(metamodel, metamodelClassName)) {
268                     return cacheAndReturn(metamodelType, provider);
269                 }
270             }
271         }
272
273         return cacheAndReturn(metamodelType, GENERIC_JPA);
274     }
275
276     /**
277      * Returns the placeholder to be used for simple count queries. Default implementation returns {@code x}.
278      *
279      * @return a placeholder for count queries. Guaranteed to be not {@code null}.
280      */

281     public String getCountQueryPlaceholder() {
282         return "x";
283     }
284
285     /*
286      * (non-Javadoc)
287      * @see org.springframework.data.jpa.repository.query.QueryExtractor#canExtractQuery()
288      */

289     @Override
290     public boolean canExtractQuery() {
291         return true;
292     }
293
294     /**
295      * Holds the PersistenceProvider specific interface names.
296      *
297      * @author Thomas Darimont
298      * @author Jens Schauder
299      */

300     interface Constants {
301
302         String GENERIC_JPA_ENTITY_MANAGER_INTERFACE = "javax.persistence.EntityManager";
303         String ECLIPSELINK_ENTITY_MANAGER_INTERFACE = "org.eclipse.persistence.jpa.JpaEntityManager";
304         // needed as Spring only exposes that interface via the EM proxy
305         String HIBERNATE_ENTITY_MANAGER_INTERFACE = "org.hibernate.jpa.HibernateEntityManager";
306
307         String HIBERNATE_JPA_METAMODEL_TYPE = "org.hibernate.metamodel.internal.MetamodelImpl";
308         String ECLIPSELINK_JPA_METAMODEL_TYPE = "org.eclipse.persistence.internal.jpa.metamodel.MetamodelImpl";
309     }
310
311     public CloseableIterator<Object> executeQueryWithResultStream(Query jpaQuery) {
312         throw new UnsupportedOperationException(
313                 "Streaming results is not implement for this PersistenceProvider: " + name());
314     }
315
316     /**
317      * {@link CloseableIterator} for Hibernate.
318      *
319      * @author Thomas Darimont
320      * @author Oliver Gierke
321      * @since 1.8
322      */

323     private static class HibernateScrollableResultsIterator implements CloseableIterator<Object> {
324
325         private final @Nullable ScrollableResults scrollableResults;
326
327         /**
328          * Creates a new {@link HibernateScrollableResultsIterator} for the given {@link Query}.
329          *
330          * @param jpaQuery must not be {@literal null}.
331          */

332         HibernateScrollableResultsIterator(Query jpaQuery) {
333
334             org.hibernate.query.Query<?> query = jpaQuery.unwrap(org.hibernate.query.Query.class);
335             this.scrollableResults = query.setReadOnly(TransactionSynchronizationManager.isCurrentTransactionReadOnly())//
336                     .scroll(ScrollMode.FORWARD_ONLY);
337         }
338
339         /*
340          * (non-Javadoc)
341          * @see java.util.Iterator#next()
342          */

343         @Override
344         public Object next() {
345
346             if (scrollableResults == null) {
347                 throw new NoSuchElementException("No ScrollableResults");
348             }
349
350             // Cast needed for Hibernate 6 compatibility
351             Object[] row = (Object[]) scrollableResults.get();
352
353             return row.length == 1 ? row[0] : row;
354         }
355
356         /*
357          * (non-Javadoc)
358          * @see java.util.Iterator#hasNext()
359          */

360         @Override
361         public boolean hasNext() {
362             return scrollableResults != null && scrollableResults.next();
363         }
364
365         /*
366          * (non-Javadoc)
367          * @see org.springframework.data.util.CloseableIterator#close()
368          */

369         @Override
370         public void close() {
371
372             if (scrollableResults != null) {
373                 scrollableResults.close();
374             }
375         }
376     }
377
378     /**
379      * {@link CloseableIterator} for EclipseLink.
380      *
381      * @author Thomas Darimont
382      * @author Oliver Gierke
383      * @param <T>
384      * @since 1.8
385      */

386     @SuppressWarnings("unchecked")
387     private static class EclipseLinkScrollableResultsIterator<T> implements CloseableIterator<T> {
388
389         private final @Nullable ScrollableCursor scrollableCursor;
390
391         /**
392          * Creates a new {@link EclipseLinkScrollableResultsIterator} for the given JPA {@link Query}.
393          *
394          * @param jpaQuery must not be {@literal null}.
395          */

396         EclipseLinkScrollableResultsIterator(Query jpaQuery) {
397
398             jpaQuery.setHint("eclipselink.cursor.scrollable"true);
399
400             this.scrollableCursor = (ScrollableCursor) jpaQuery.getSingleResult();
401         }
402
403         /*
404          * (non-Javadoc)
405          * @see java.util.Iterator#hasNext()
406          */

407         @Override
408         public boolean hasNext() {
409             return scrollableCursor != null && scrollableCursor.hasNext();
410         }
411
412         /*
413          * (non-Javadoc)
414          * @see java.util.Iterator#next()
415          */

416         @Override
417         public T next() {
418
419             if (scrollableCursor == null) {
420                 throw new NoSuchElementException("No ScrollableCursor");
421             }
422
423             return (T) scrollableCursor.next();
424         }
425
426         /*
427          * (non-Javadoc)
428          * @see org.springframework.data.util.CloseableIterator#close()
429          */

430         @Override
431         public void close() {
432
433             if (scrollableCursor != null) {
434                 scrollableCursor.close();
435             }
436         }
437     }
438 }
439