1919import static org .springframework .data .jpa .provider .PersistenceProvider .Constants .*;
2020
2121import jakarta .persistence .EntityManager ;
22+ import jakarta .persistence .EntityManagerFactory ;
2223import jakarta .persistence .Query ;
2324import jakarta .persistence .metamodel .IdentifiableType ;
2425import jakarta .persistence .metamodel .Metamodel ;
@@ -65,14 +66,20 @@ public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor, Quer
6566 * @see <a href="https://github.com/spring-projects/spring-data-jpa/issues/846">DATAJPA-444</a>
6667 */
6768 HIBERNATE (//
69+ Collections .singletonList (HIBERNATE_ENTITY_MANAGER_FACTORY_INTERFACE ), //
6870 Collections .singletonList (HIBERNATE_ENTITY_MANAGER_INTERFACE ), //
6971 Collections .singletonList (HIBERNATE_JPA_METAMODEL_TYPE )) {
7072
7173 @ Override
72- public @ Nullable String extractQueryString (Query query ) {
74+ public @ Nullable String extractQueryString (Object query ) {
7375 return HibernateUtils .getHibernateQuery (query );
7476 }
7577
78+ @ Override
79+ public boolean isNativeQuery (Object query ) {
80+ return HibernateUtils .isNativeQuery (query );
81+ }
82+
7683 /**
7784 * Return custom placeholder ({@code *}) as Hibernate does create invalid queries for count queries for objects with
7885 * compound keys.
@@ -115,14 +122,20 @@ public String getCommentHintKey() {
115122 /**
116123 * EclipseLink persistence provider.
117124 */
118- ECLIPSELINK (Collections .singleton (ECLIPSELINK_ENTITY_MANAGER_INTERFACE ),
125+ ECLIPSELINK (List .of (ECLIPSELINK_ENTITY_MANAGER_FACTORY_INTERFACE1 , ECLIPSELINK_ENTITY_MANAGER_FACTORY_INTERFACE2 ),
126+ Collections .singleton (ECLIPSELINK_ENTITY_MANAGER_INTERFACE ),
119127 Collections .singleton (ECLIPSELINK_JPA_METAMODEL_TYPE )) {
120128
121129 @ Override
122- public String extractQueryString (Query query ) {
130+ public String extractQueryString (Object query ) {
123131 return ((JpaQuery <?>) query ).getDatabaseQuery ().getJPQLString ();
124132 }
125133
134+ @ Override
135+ public boolean isNativeQuery (Object query ) {
136+ return false ;
137+ }
138+
126139 @ Override
127140 public boolean shouldUseAccessorFor (Object entity ) {
128141 return false ;
@@ -152,13 +165,19 @@ public String getCommentHintValue(String comment) {
152165 /**
153166 * Unknown special provider. Use standard JPA.
154167 */
155- GENERIC_JPA (Collections .singleton (GENERIC_JPA_ENTITY_MANAGER_INTERFACE ), Collections .emptySet ()) {
168+ GENERIC_JPA (Collections .singleton (GENERIC_JPA_ENTITY_MANAGER_INTERFACE ),
169+ Collections .singleton (GENERIC_JPA_ENTITY_MANAGER_INTERFACE ), Collections .emptySet ()) {
156170
157171 @ Override
158- public @ Nullable String extractQueryString (Query query ) {
172+ public @ Nullable String extractQueryString (Object query ) {
159173 return null ;
160174 }
161175
176+ @ Override
177+ public boolean isNativeQuery (Object query ) {
178+ return false ;
179+ }
180+
162181 @ Override
163182 public boolean canExtractQuery () {
164183 return false ;
@@ -196,6 +215,7 @@ public boolean shouldUseAccessorFor(Object entity) {
196215 private static final Collection <PersistenceProvider > ALL = List .of (HIBERNATE , ECLIPSELINK , GENERIC_JPA );
197216
198217 private static final ConcurrentReferenceHashMap <Class <?>, PersistenceProvider > CACHE = new ConcurrentReferenceHashMap <>();
218+ private final Iterable <String > entityManagerFactoryClassNames ;
199219 private final Iterable <String > entityManagerClassNames ;
200220 private final Iterable <String > metamodelClassNames ;
201221
@@ -204,24 +224,38 @@ public boolean shouldUseAccessorFor(Object entity) {
204224 /**
205225 * Creates a new {@link PersistenceProvider}.
206226 *
227+ * @param entityManagerFactoryClassNames the names of the provider specific
228+ * {@link jakarta.persistence.EntityManagerFactory} implementations. Must not be {@literal null} or empty.
207229 * @param entityManagerClassNames the names of the provider specific {@link EntityManager} implementations. Must not
208230 * be {@literal null} or empty.
209231 * @param metamodelClassNames must not be {@literal null}.
210232 */
211- PersistenceProvider (Iterable <String > entityManagerClassNames , Iterable <String > metamodelClassNames ) {
233+ PersistenceProvider (Iterable <String > entityManagerFactoryClassNames , Iterable <String > entityManagerClassNames ,
234+ Iterable <String > metamodelClassNames ) {
212235
236+ this .entityManagerFactoryClassNames = entityManagerFactoryClassNames ;
213237 this .entityManagerClassNames = entityManagerClassNames ;
214238 this .metamodelClassNames = metamodelClassNames ;
215239
216240 boolean present = false ;
217- for (String entityManagerClassName : entityManagerClassNames ) {
241+ for (String emfClassName : entityManagerFactoryClassNames ) {
218242
219- if (ClassUtils .isPresent (entityManagerClassName , PersistenceProvider .class .getClassLoader ())) {
243+ if (ClassUtils .isPresent (emfClassName , PersistenceProvider .class .getClassLoader ())) {
220244 present = true ;
221245 break ;
222246 }
223247 }
224248
249+ if (!present ) {
250+ for (String entityManagerClassName : entityManagerClassNames ) {
251+
252+ if (ClassUtils .isPresent (entityManagerClassName , PersistenceProvider .class .getClassLoader ())) {
253+ present = true ;
254+ break ;
255+ }
256+ }
257+ }
258+
225259 this .present = present ;
226260 }
227261
@@ -266,6 +300,36 @@ public static PersistenceProvider fromEntityManager(EntityManager em) {
266300 return cacheAndReturn (entityManagerType , GENERIC_JPA );
267301 }
268302
303+ /**
304+ * Determines the {@link PersistenceProvider} from the given {@link EntityManager}. If no special one can be
305+ * determined {@link #GENERIC_JPA} will be returned.
306+ *
307+ * @param emf must not be {@literal null}.
308+ * @return will never be {@literal null}.
309+ */
310+ public static PersistenceProvider fromEntityManagerFactory (EntityManagerFactory emf ) {
311+
312+ Assert .notNull (emf , "EntityManager must not be null" );
313+
314+ Class <?> entityManagerType = emf .getPersistenceUnitUtil ().getClass ();
315+ PersistenceProvider cachedProvider = CACHE .get (entityManagerType );
316+
317+ if (cachedProvider != null ) {
318+ return cachedProvider ;
319+ }
320+
321+ for (PersistenceProvider provider : ALL ) {
322+ for (String emfClassName : provider .entityManagerFactoryClassNames ) {
323+ if (isOfType (emf .getPersistenceUnitUtil (), emfClassName ,
324+ emf .getPersistenceUnitUtil ().getClass ().getClassLoader ())) {
325+ return cacheAndReturn (entityManagerType , provider );
326+ }
327+ }
328+ }
329+
330+ return cacheAndReturn (entityManagerType , GENERIC_JPA );
331+ }
332+
269333 /**
270334 * Determines the {@link PersistenceProvider} from the given {@link Metamodel}. If no special one can be determined
271335 * {@link #GENERIC_JPA} will be returned.
@@ -350,9 +414,15 @@ public boolean isPresent() {
350414 */
351415 interface Constants {
352416
417+ String GENERIC_JPA_ENTITY_MANAGER_FACTORY_INTERFACE = "jakarta.persistence.EntityManagerFactory" ;
353418 String GENERIC_JPA_ENTITY_MANAGER_INTERFACE = "jakarta.persistence.EntityManager" ;
419+
420+ String ECLIPSELINK_ENTITY_MANAGER_FACTORY_INTERFACE1 = "org.eclipse.persistence.internal.jpa.EntityManagerFactoryDelegate" ;
421+ String ECLIPSELINK_ENTITY_MANAGER_FACTORY_INTERFACE2 = "org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl" ;
354422 String ECLIPSELINK_ENTITY_MANAGER_INTERFACE = "org.eclipse.persistence.jpa.JpaEntityManager" ;
423+
355424 // needed as Spring only exposes that interface via the EM proxy
425+ String HIBERNATE_ENTITY_MANAGER_FACTORY_INTERFACE = "org.hibernate.jpa.internal.PersistenceUnitUtilImpl" ;
356426 String HIBERNATE_ENTITY_MANAGER_INTERFACE = "org.hibernate.engine.spi.SessionImplementor" ;
357427
358428 String HIBERNATE_JPA_METAMODEL_TYPE = "org.hibernate.metamodel.model.domain.JpaMetamodel" ;
0 commit comments