diff --git a/spring-orm/src/main/java/org/springframework/orm/hibernate5/HibernateTemplate.java b/spring-orm/src/main/java/org/springframework/orm/hibernate5/HibernateTemplate.java index 975eec6925e..65a8bb43c78 100644 --- a/spring-orm/src/main/java/org/springframework/orm/hibernate5/HibernateTemplate.java +++ b/spring-orm/src/main/java/org/springframework/orm/hibernate5/HibernateTemplate.java @@ -45,6 +45,7 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.dao.DataAccessException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.lang.Nullable; +import org.springframework.transaction.support.ResourceHolderSupport; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; @@ -1117,8 +1118,8 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean criteria.setMaxResults(getMaxResults()); } - SessionHolder sessionHolder = - (SessionHolder) TransactionSynchronizationManager.getResource(obtainSessionFactory()); + ResourceHolderSupport sessionHolder = + (ResourceHolderSupport) TransactionSynchronizationManager.getResource(obtainSessionFactory()); if (sessionHolder != null && sessionHolder.hasTimeout()) { criteria.setTimeout(sessionHolder.getTimeToLiveInSeconds()); } @@ -1146,8 +1147,8 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean queryObject.setMaxResults(getMaxResults()); } - SessionHolder sessionHolder = - (SessionHolder) TransactionSynchronizationManager.getResource(obtainSessionFactory()); + ResourceHolderSupport sessionHolder = + (ResourceHolderSupport) TransactionSynchronizationManager.getResource(obtainSessionFactory()); if (sessionHolder != null && sessionHolder.hasTimeout()) { queryObject.setTimeout(sessionHolder.getTimeToLiveInSeconds()); } diff --git a/spring-orm/src/main/java/org/springframework/orm/hibernate5/SessionHolder.java b/spring-orm/src/main/java/org/springframework/orm/hibernate5/SessionHolder.java index 591a19fad2e..556f58c9a3c 100644 --- a/spring-orm/src/main/java/org/springframework/orm/hibernate5/SessionHolder.java +++ b/spring-orm/src/main/java/org/springframework/orm/hibernate5/SessionHolder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,18 +16,20 @@ package org.springframework.orm.hibernate5; +import javax.persistence.EntityManager; + import org.hibernate.FlushMode; import org.hibernate.Session; import org.hibernate.Transaction; import org.springframework.lang.Nullable; -import org.springframework.transaction.support.ResourceHolderSupport; -import org.springframework.util.Assert; +import org.springframework.orm.jpa.EntityManagerHolder; /** - * Session holder, wrapping a Hibernate Session and a Hibernate Transaction. - * HibernateTransactionManager binds instances of this class to the thread, - * for a given SessionFactory. + * Resource holder wrapping a Hibernate {@link Session} (plus an optional {@link Transaction}). + * {@link HibernateTransactionManager} binds instances of this class to the thread, + * for a given {@link org.hibernate.SessionFactory}. Extends {@link EntityManagerHolder} + * as of 5.1, automatically exposing an {@code EntityManager} handle on Hibernate 5.2+. * *

Note: This is an SPI class, not intended to be used by applications. * @@ -36,7 +38,7 @@ import org.springframework.util.Assert; * @see HibernateTransactionManager * @see SessionFactoryUtils */ -public class SessionHolder extends ResourceHolderSupport { +public class SessionHolder extends EntityManagerHolder { private final Session session; @@ -48,7 +50,7 @@ public class SessionHolder extends ResourceHolderSupport { public SessionHolder(Session session) { - Assert.notNull(session, "Session must not be null"); + super(EntityManager.class.isInstance(session) ? session : null); this.session = session; } @@ -59,6 +61,7 @@ public class SessionHolder extends ResourceHolderSupport { public void setTransaction(@Nullable Transaction transaction) { this.transaction = transaction; + setTransactionActive(transaction != null); } @Nullable diff --git a/spring-orm/src/main/java/org/springframework/orm/hibernate5/SpringSessionContext.java b/spring-orm/src/main/java/org/springframework/orm/hibernate5/SpringSessionContext.java index df7dd0cb5f1..1b7b6d89bf0 100644 --- a/spring-orm/src/main/java/org/springframework/orm/hibernate5/SpringSessionContext.java +++ b/spring-orm/src/main/java/org/springframework/orm/hibernate5/SpringSessionContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,12 +29,13 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform; import org.springframework.lang.Nullable; +import org.springframework.orm.jpa.EntityManagerHolder; import org.springframework.transaction.support.TransactionSynchronizationManager; /** - * Implementation of Hibernate 3.1's CurrentSessionContext interface - * that delegates to Spring's SessionFactoryUtils for providing a - * Spring-managed current Session. + * Implementation of Hibernate 3.1's {@link CurrentSessionContext} interface + * that delegates to Spring's {@link SessionFactoryUtils} for providing a + * Spring-managed current {@link Session}. * *

This CurrentSessionContext implementation can also be specified in custom * SessionFactory setup through the "hibernate.current_session_context_class" @@ -86,6 +87,7 @@ public class SpringSessionContext implements CurrentSessionContext { return (Session) value; } else if (value instanceof SessionHolder) { + // HibernateTransactionManager SessionHolder sessionHolder = (SessionHolder) value; Session session = sessionHolder.getSession(); if (!sessionHolder.isSynchronizedWithTransaction() && @@ -104,13 +106,18 @@ public class SpringSessionContext implements CurrentSessionContext { } return session; } + else if (value instanceof EntityManagerHolder) { + // JpaTransactionManager + return ((EntityManagerHolder) value).getEntityManager().unwrap(Session.class); + } if (this.transactionManager != null && this.jtaSessionContext != null) { try { if (this.transactionManager.getStatus() == Status.STATUS_ACTIVE) { Session session = this.jtaSessionContext.currentSession(); if (TransactionSynchronizationManager.isSynchronizationActive()) { - TransactionSynchronizationManager.registerSynchronization(new SpringFlushSynchronization(session)); + TransactionSynchronizationManager.registerSynchronization( + new SpringFlushSynchronization(session)); } return session; } diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/EntityManagerHolder.java b/spring-orm/src/main/java/org/springframework/orm/jpa/EntityManagerHolder.java index 9dcb4094126..0f8fbd7f424 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/EntityManagerHolder.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/EntityManagerHolder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,9 +24,12 @@ import org.springframework.transaction.support.ResourceHolderSupport; import org.springframework.util.Assert; /** - * Holder wrapping a JPA EntityManager. - * JpaTransactionManager binds instances of this class to the thread, - * for a given EntityManagerFactory. + * Resource holder wrapping a JPA {@link EntityManager}. + * {@link JpaTransactionManager} binds instances of this class to the thread, + * for a given {@link javax.persistence.EntityManagerFactory}. + * + *

Also serves as a base class for {@link org.springframework.orm.hibernate5.SessionHolder}, + * as of 5.1. * *

Note: This is an SPI class, not intended to be used by applications. * @@ -37,6 +40,7 @@ import org.springframework.util.Assert; */ public class EntityManagerHolder extends ResourceHolderSupport { + @Nullable private final EntityManager entityManager; private boolean transactionActive; @@ -45,13 +49,13 @@ public class EntityManagerHolder extends ResourceHolderSupport { private SavepointManager savepointManager; - public EntityManagerHolder(EntityManager entityManager) { - Assert.notNull(entityManager, "EntityManager must not be null"); + public EntityManagerHolder(@Nullable EntityManager entityManager) { this.entityManager = entityManager; } public EntityManager getEntityManager() { + Assert.state(this.entityManager != null, "No EntityManager available"); return this.entityManager; } diff --git a/spring-orm/src/test/java/org/springframework/orm/jpa/AbstractContainerEntityManagerFactoryIntegrationTests.java b/spring-orm/src/test/java/org/springframework/orm/jpa/AbstractContainerEntityManagerFactoryIntegrationTests.java index cd249657ef7..6c74ded9627 100644 --- a/spring-orm/src/test/java/org/springframework/orm/jpa/AbstractContainerEntityManagerFactoryIntegrationTests.java +++ b/spring-orm/src/test/java/org/springframework/orm/jpa/AbstractContainerEntityManagerFactoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,6 @@ package org.springframework.orm.jpa; import java.lang.reflect.Proxy; import java.util.List; import javax.persistence.EntityManager; -import javax.persistence.EntityNotFoundException; import javax.persistence.FlushModeType; import javax.persistence.NoResultException; import javax.persistence.Query; @@ -39,14 +38,14 @@ import static org.junit.Assert.*; * @author Rod Johnson * @author Juergen Hoeller */ -public abstract class AbstractContainerEntityManagerFactoryIntegrationTests extends AbstractEntityManagerFactoryIntegrationTests { +public abstract class AbstractContainerEntityManagerFactoryIntegrationTests + extends AbstractEntityManagerFactoryIntegrationTests { @Test public void testEntityManagerFactoryImplementsEntityManagerFactoryInfo() { - assertTrue(Proxy.isProxyClass(entityManagerFactory.getClass())); assertTrue("Must have introduced config interface", entityManagerFactory instanceof EntityManagerFactoryInfo); EntityManagerFactoryInfo emfi = (EntityManagerFactoryInfo) entityManagerFactory; - // assertEquals("Person", emfi.getPersistenceUnitName()); + assertEquals("Person", emfi.getPersistenceUnitName()); assertNotNull("PersistenceUnitInfo must be available", emfi.getPersistenceUnitInfo()); assertNotNull("Raw EntityManagerFactory must be available", emfi.getNativeEntityManagerFactory()); } @@ -78,7 +77,7 @@ public abstract class AbstractContainerEntityManagerFactoryIntegrationTests exte } @Test - @SuppressWarnings({ "unused", "unchecked" }) + @SuppressWarnings("unchecked") public void testEntityManagerProxyIsProxy() { assertTrue(Proxy.isProxyClass(sharedEntityManager.getClass())); Query q = sharedEntityManager.createQuery("select p from Person as p"); @@ -107,14 +106,13 @@ public abstract class AbstractContainerEntityManagerFactoryIntegrationTests exte try { Person notThere = sharedEntityManager.getReference(Person.class, 666); - // We may get here (as with Hibernate). - // Either behaviour is valid: throw exception on first access - // or on getReference itself. + // We may get here (as with Hibernate). Either behaviour is valid: + // throw exception on first access or on getReference itself. notThere.getFirstName(); - fail("Should have thrown an EntityNotFoundException"); + fail("Should have thrown an EntityNotFoundException or ObjectNotFoundException"); } - catch (EntityNotFoundException ex) { - // expected + catch (Exception ex) { + assertTrue(ex.getClass().getName().endsWith("NotFoundException")); } } @@ -209,6 +207,8 @@ public abstract class AbstractContainerEntityManagerFactoryIntegrationTests exte @Test @SuppressWarnings("unchecked") public void testQueryNoPersonsNotTransactional() { + endTransaction(); + EntityManager em = entityManagerFactory.createEntityManager(); Query q = em.createQuery("select p from Person as p"); List people = q.getResultList(); @@ -223,12 +223,12 @@ public abstract class AbstractContainerEntityManagerFactoryIntegrationTests exte } @Test - @SuppressWarnings({ "unused", "unchecked" }) + @SuppressWarnings("unchecked") public void testQueryNoPersonsShared() { - EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(entityManagerFactory); - Query q = em.createQuery("select p from Person as p"); + Query q = this.sharedEntityManager.createQuery("select p from Person as p"); q.setFlushMode(FlushModeType.AUTO); List people = q.getResultList(); + assertEquals(0, people.size()); try { assertNull(q.getSingleResult()); fail("Should have thrown NoResultException"); @@ -243,7 +243,7 @@ public abstract class AbstractContainerEntityManagerFactoryIntegrationTests exte public void testQueryNoPersonsSharedNotTransactional() { endTransaction(); - EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(entityManagerFactory); + EntityManager em = this.sharedEntityManager; Query q = em.createQuery("select p from Person as p"); q.setFlushMode(FlushModeType.AUTO); List people = q.getResultList(); diff --git a/spring-orm/src/test/java/org/springframework/orm/jpa/AbstractEntityManagerFactoryIntegrationTests.java b/spring-orm/src/test/java/org/springframework/orm/jpa/AbstractEntityManagerFactoryIntegrationTests.java index 7848387adfe..4c2a1d0d69e 100644 --- a/spring-orm/src/test/java/org/springframework/orm/jpa/AbstractEntityManagerFactoryIntegrationTests.java +++ b/spring-orm/src/test/java/org/springframework/orm/jpa/AbstractEntityManagerFactoryIntegrationTests.java @@ -48,12 +48,8 @@ import static org.junit.Assert.*; public abstract class AbstractEntityManagerFactoryIntegrationTests { protected static final String[] ECLIPSELINK_CONFIG_LOCATIONS = new String[] { - "/org/springframework/orm/jpa/eclipselink/eclipselink-manager.xml", "/org/springframework/orm/jpa/memdb.xml", - "/org/springframework/orm/jpa/inject.xml"}; - - protected static final String[] HIBERNATE_CONFIG_LOCATIONS = new String[] { - "/org/springframework/orm/jpa/hibernate/hibernate-manager.xml", "/org/springframework/orm/jpa/memdb.xml", - "/org/springframework/orm/jpa/inject.xml"}; + "/org/springframework/orm/jpa/eclipselink/eclipselink-manager.xml", + "/org/springframework/orm/jpa/memdb.xml", "/org/springframework/orm/jpa/inject.xml"}; private static ConfigurableApplicationContext applicationContext; diff --git a/spring-orm/src/test/java/org/springframework/orm/jpa/eclipselink/EclipseLinkEntityManagerFactoryIntegrationTests.java b/spring-orm/src/test/java/org/springframework/orm/jpa/eclipselink/EclipseLinkEntityManagerFactoryIntegrationTests.java index b01afaa547a..7324d348528 100644 --- a/spring-orm/src/test/java/org/springframework/orm/jpa/eclipselink/EclipseLinkEntityManagerFactoryIntegrationTests.java +++ b/spring-orm/src/test/java/org/springframework/orm/jpa/eclipselink/EclipseLinkEntityManagerFactoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,12 +31,6 @@ import static org.junit.Assert.*; */ public class EclipseLinkEntityManagerFactoryIntegrationTests extends AbstractContainerEntityManagerFactoryIntegrationTests { - @Override - protected String[] getConfigLocations() { - return ECLIPSELINK_CONFIG_LOCATIONS; - } - - @Test public void testCanCastNativeEntityManagerFactoryToEclipseLinkEntityManagerFactoryImpl() { EntityManagerFactoryInfo emfi = (EntityManagerFactoryInfo) entityManagerFactory; diff --git a/spring-orm/src/test/java/org/springframework/orm/jpa/hibernate/HibernateEntityManagerFactoryIntegrationTests.java b/spring-orm/src/test/java/org/springframework/orm/jpa/hibernate/HibernateEntityManagerFactoryIntegrationTests.java index c1e7d6f4bf5..85bea60131f 100644 --- a/spring-orm/src/test/java/org/springframework/orm/jpa/hibernate/HibernateEntityManagerFactoryIntegrationTests.java +++ b/spring-orm/src/test/java/org/springframework/orm/jpa/hibernate/HibernateEntityManagerFactoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,7 +41,8 @@ public class HibernateEntityManagerFactoryIntegrationTests extends AbstractConta @Override protected String[] getConfigLocations() { - return HIBERNATE_CONFIG_LOCATIONS; + return new String[] {"/org/springframework/orm/jpa/hibernate/hibernate-manager.xml", + "/org/springframework/orm/jpa/memdb.xml", "/org/springframework/orm/jpa/inject.xml"}; } diff --git a/spring-orm/src/test/java/org/springframework/orm/jpa/hibernate/HibernateMultiEntityManagerFactoryIntegrationTests.java b/spring-orm/src/test/java/org/springframework/orm/jpa/hibernate/HibernateMultiEntityManagerFactoryIntegrationTests.java index a30127ef1de..c6765eb47dc 100644 --- a/spring-orm/src/test/java/org/springframework/orm/jpa/hibernate/HibernateMultiEntityManagerFactoryIntegrationTests.java +++ b/spring-orm/src/test/java/org/springframework/orm/jpa/hibernate/HibernateMultiEntityManagerFactoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.orm.jpa.AbstractContainerEntityManagerFactoryIntegrationTests; +import org.springframework.orm.jpa.EntityManagerFactoryInfo; import static org.junit.Assert.*; @@ -44,6 +45,15 @@ public class HibernateMultiEntityManagerFactoryIntegrationTests extends Abstract } + @Test + public void testEntityManagerFactoryImplementsEntityManagerFactoryInfo() { + assertTrue("Must have introduced config interface", this.entityManagerFactory instanceof EntityManagerFactoryInfo); + EntityManagerFactoryInfo emfi = (EntityManagerFactoryInfo) this.entityManagerFactory; + assertEquals("Drivers", emfi.getPersistenceUnitName()); + assertNotNull("PersistenceUnitInfo must be available", emfi.getPersistenceUnitInfo()); + assertNotNull("Raw EntityManagerFactory must be available", emfi.getNativeEntityManagerFactory()); + } + @Test public void testEntityManagerFactory2() { EntityManager em = this.entityManagerFactory2.createEntityManager(); diff --git a/spring-orm/src/test/java/org/springframework/orm/jpa/hibernate/HibernateNativeEntityManagerFactoryIntegrationTests.java b/spring-orm/src/test/java/org/springframework/orm/jpa/hibernate/HibernateNativeEntityManagerFactoryIntegrationTests.java new file mode 100644 index 00000000000..b623363f223 --- /dev/null +++ b/spring-orm/src/test/java/org/springframework/orm/jpa/hibernate/HibernateNativeEntityManagerFactoryIntegrationTests.java @@ -0,0 +1,70 @@ +/* + * Copyright 2002-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.orm.jpa.hibernate; + +import java.util.List; + +import org.hibernate.SessionFactory; +import org.hibernate.query.Query; +import org.junit.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.orm.jpa.AbstractContainerEntityManagerFactoryIntegrationTests; +import org.springframework.orm.jpa.EntityManagerFactoryInfo; +import org.springframework.orm.jpa.domain.Person; + +import static org.junit.Assert.*; + +/** + * Hibernate-specific JPA tests with native SessionFactory setup and getCurrentSession interaction. + * + * @author Juergen Hoeller + * @since 5.1 + */ +public class HibernateNativeEntityManagerFactoryIntegrationTests extends AbstractContainerEntityManagerFactoryIntegrationTests { + + @Autowired + private SessionFactory sessionFactory; + + + @Override + protected String[] getConfigLocations() { + return new String[] {"/org/springframework/orm/jpa/hibernate/hibernate-manager-native.xml", + "/org/springframework/orm/jpa/memdb.xml", "/org/springframework/orm/jpa/inject.xml"}; + } + + + @Test + public void testEntityManagerFactoryImplementsEntityManagerFactoryInfo() { + assertFalse("Must not have introduced config interface", entityManagerFactory instanceof EntityManagerFactoryInfo); + } + + @Test + @SuppressWarnings("unchecked") + public void testCurrentSession() { + // Add with JDBC + String firstName = "Tony"; + insertPerson(firstName); + + Query q = sessionFactory.getCurrentSession().createQuery("select p from Person as p"); + List people = q.getResultList(); + + assertEquals(1, people.size()); + assertEquals(firstName, people.get(0).getFirstName()); + } + +} diff --git a/spring-orm/src/test/resources/org/springframework/orm/jpa/eclipselink/eclipselink-manager.xml b/spring-orm/src/test/resources/org/springframework/orm/jpa/eclipselink/eclipselink-manager.xml index ecc5a2b6747..7eeffe60a21 100644 --- a/spring-orm/src/test/resources/org/springframework/orm/jpa/eclipselink/eclipselink-manager.xml +++ b/spring-orm/src/test/resources/org/springframework/orm/jpa/eclipselink/eclipselink-manager.xml @@ -24,4 +24,8 @@ + + + + diff --git a/spring-orm/src/test/resources/org/springframework/orm/jpa/hibernate/hibernate-manager-multi.xml b/spring-orm/src/test/resources/org/springframework/orm/jpa/hibernate/hibernate-manager-multi.xml index 0f626d29547..0338b85d4d3 100644 --- a/spring-orm/src/test/resources/org/springframework/orm/jpa/hibernate/hibernate-manager-multi.xml +++ b/spring-orm/src/test/resources/org/springframework/orm/jpa/hibernate/hibernate-manager-multi.xml @@ -22,5 +22,9 @@ - + + + + + diff --git a/spring-orm/src/test/resources/org/springframework/orm/jpa/hibernate/hibernate-manager-native.xml b/spring-orm/src/test/resources/org/springframework/orm/jpa/hibernate/hibernate-manager-native.xml new file mode 100644 index 00000000000..8944d5adaeb --- /dev/null +++ b/spring-orm/src/test/resources/org/springframework/orm/jpa/hibernate/hibernate-manager-native.xml @@ -0,0 +1,31 @@ + + + + + + + org.springframework.orm.jpa.domain.DriversLicense + org.springframework.orm.jpa.domain.Person + + + + + + org.hibernate.dialect.HSQLDialect + update + org.hibernate.cache.HashtableCacheProvider + + + + + + + + + + + + + diff --git a/spring-orm/src/test/resources/org/springframework/orm/jpa/hibernate/hibernate-manager.xml b/spring-orm/src/test/resources/org/springframework/orm/jpa/hibernate/hibernate-manager.xml index bafddcbd5c2..0e763eeb747 100644 --- a/spring-orm/src/test/resources/org/springframework/orm/jpa/hibernate/hibernate-manager.xml +++ b/spring-orm/src/test/resources/org/springframework/orm/jpa/hibernate/hibernate-manager.xml @@ -15,6 +15,7 @@ + org.springframework.orm.hibernate5.SpringSessionContext org.hibernate.cache.HashtableCacheProvider @@ -23,6 +24,10 @@ + + + + diff --git a/spring-orm/src/test/resources/org/springframework/orm/jpa/memdb.xml b/spring-orm/src/test/resources/org/springframework/orm/jpa/memdb.xml index 247c28f7f6e..8b720dc3e34 100644 --- a/spring-orm/src/test/resources/org/springframework/orm/jpa/memdb.xml +++ b/spring-orm/src/test/resources/org/springframework/orm/jpa/memdb.xml @@ -7,10 +7,6 @@ - - - - @@ -18,14 +14,4 @@ - - diff --git a/src/docs/asciidoc/data-access.adoc b/src/docs/asciidoc/data-access.adoc index b09a8840fca..59add5267ed 100644 --- a/src/docs/asciidoc/data-access.adoc +++ b/src/docs/asciidoc/data-access.adoc @@ -5649,6 +5649,12 @@ application uses to refer to them, for example, in `@PersistenceUnit` and Use this option for full JPA capabilities in a Spring-based application environment. This includes web containers such as Tomcat as well as stand-alone applications and integration tests with sophisticated persistence requirements. + +If you'd like to specifically configure a Hibernate setup, an immediate alternative is +to go with Hibernate 5.2/5.3 and set up a native Hibernate `LocalSessionFactoryBean` +instead of a plain JPA `LocalContainerEntityManagerFactoryBean`, letting it interact +with JPA access code as well as native Hibernate access code. +See <> for details. ==== The `LocalContainerEntityManagerFactoryBean` gives full control over @@ -5979,6 +5985,15 @@ to JDBC access code that accesses the same `DataSource`, provided that the regis Spring provides dialects for the EclipseLink and Hibernate JPA implementations. See the next section for details on the `JpaDialect` mechanism. +[NOTE] +==== +As an immediate alternative, Spring's native `HibernateTransactionManager` is capable +of interacting with JPA access code as of Spring Framework 5.1 and Hibernate 5.2/5.3, +adapting to several Hibernate specifics and providing JDBC interaction out of the box. +This makes particular sense in combination with `LocalSessionFactoryBean` setup. +See <> for details. +==== + [[orm-jpa-dialect]] ==== JpaDialect and JpaVendorAdapter @@ -6048,6 +6063,31 @@ might require special definitions in your server configuration, making the deplo less portable, but will be set up for the server's JTA environment out of the box. +[[orm-jpa-hibernate]] +==== Native Hibernate setup and native Hibernate transactions for JPA interaction + +As of Spring Framework 5.1 and Hibernate 5.2/5.3, a native `LocalSessionFactoryBean` +setup in combination with `HibernateTransactionManager` allows for interaction with +`@PersistenceContext` and other JPA access code out of the box. A Hibernate +`SessionFactory` natively implements JPA's `EntityManagerFactory` interface now, +and a Hibernate `Session` handle natively is a JPA `EntityManager` as well. +Spring's JPA support facilities automatically detect native Hibernate Sessions. + +Such native Hibernate setup can therefore serve as a replacement for a standard JPA +`LocalContainerEntityManagerFactoryBean` and `JpaTransactionManager` combination +in many scenarios, allowing for interaction with `SessionFactory.getCurrentSession()` +(and also `HibernateTemplate`) next to `@PersistenceContext EntityManager` within +the same local transaction. Such a setup also provides stronger Hibernate integration +and more configuration flexibility, not being constrained by JPA bootstrap contracts. + +There is no need for `HibernateJpaVendorAdapter` configuration in such a scenario +since Spring's native Hibernate setup provides even more features out of the box: +e.g. custom Hibernate Integrator setup, Hibernate 5.3 bean container integration, +as well as stronger optimizations for read-only transactions. Last but not least, +native Hibernate setup can also be expressed through `LocalSessionFactoryBuilder`, +seamlessly integrating with `@Bean` style configuration (no `FactoryBean` involved). + + [[oxm]]