LocalSessionFactoryBean and HibernateTransactionManager for JPA setup

SessionHolder extends EntityManagerHolder now, allowing for @PersistenceContext and co to interact with HibernateTransactionManager's thread-bound transactions, and SpringSessionContext is capable of interacting with JpaTransactionManager by detecting a plain EntityManagerHolder as well.

Issue: SPR-17002
This commit is contained in:
Juergen Hoeller 2018-07-04 15:07:09 +02:00
parent a5dd0f0c09
commit 094c9b8bd2
16 changed files with 226 additions and 70 deletions

View File

@ -45,6 +45,7 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.transaction.support.ResourceHolderSupport;
import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
@ -1117,8 +1118,8 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
criteria.setMaxResults(getMaxResults()); criteria.setMaxResults(getMaxResults());
} }
SessionHolder sessionHolder = ResourceHolderSupport sessionHolder =
(SessionHolder) TransactionSynchronizationManager.getResource(obtainSessionFactory()); (ResourceHolderSupport) TransactionSynchronizationManager.getResource(obtainSessionFactory());
if (sessionHolder != null && sessionHolder.hasTimeout()) { if (sessionHolder != null && sessionHolder.hasTimeout()) {
criteria.setTimeout(sessionHolder.getTimeToLiveInSeconds()); criteria.setTimeout(sessionHolder.getTimeToLiveInSeconds());
} }
@ -1146,8 +1147,8 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean
queryObject.setMaxResults(getMaxResults()); queryObject.setMaxResults(getMaxResults());
} }
SessionHolder sessionHolder = ResourceHolderSupport sessionHolder =
(SessionHolder) TransactionSynchronizationManager.getResource(obtainSessionFactory()); (ResourceHolderSupport) TransactionSynchronizationManager.getResource(obtainSessionFactory());
if (sessionHolder != null && sessionHolder.hasTimeout()) { if (sessionHolder != null && sessionHolder.hasTimeout()) {
queryObject.setTimeout(sessionHolder.getTimeToLiveInSeconds()); queryObject.setTimeout(sessionHolder.getTimeToLiveInSeconds());
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,18 +16,20 @@
package org.springframework.orm.hibernate5; package org.springframework.orm.hibernate5;
import javax.persistence.EntityManager;
import org.hibernate.FlushMode; import org.hibernate.FlushMode;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.Transaction; import org.hibernate.Transaction;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.transaction.support.ResourceHolderSupport; import org.springframework.orm.jpa.EntityManagerHolder;
import org.springframework.util.Assert;
/** /**
* Session holder, wrapping a Hibernate Session and a Hibernate Transaction. * Resource holder wrapping a Hibernate {@link Session} (plus an optional {@link Transaction}).
* HibernateTransactionManager binds instances of this class to the thread, * {@link HibernateTransactionManager} binds instances of this class to the thread,
* for a given SessionFactory. * for a given {@link org.hibernate.SessionFactory}. Extends {@link EntityManagerHolder}
* as of 5.1, automatically exposing an {@code EntityManager} handle on Hibernate 5.2+.
* *
* <p>Note: This is an SPI class, not intended to be used by applications. * <p>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 HibernateTransactionManager
* @see SessionFactoryUtils * @see SessionFactoryUtils
*/ */
public class SessionHolder extends ResourceHolderSupport { public class SessionHolder extends EntityManagerHolder {
private final Session session; private final Session session;
@ -48,7 +50,7 @@ public class SessionHolder extends ResourceHolderSupport {
public SessionHolder(Session session) { public SessionHolder(Session session) {
Assert.notNull(session, "Session must not be null"); super(EntityManager.class.isInstance(session) ? session : null);
this.session = session; this.session = session;
} }
@ -59,6 +61,7 @@ public class SessionHolder extends ResourceHolderSupport {
public void setTransaction(@Nullable Transaction transaction) { public void setTransaction(@Nullable Transaction transaction) {
this.transaction = transaction; this.transaction = transaction;
setTransactionActive(transaction != null);
} }
@Nullable @Nullable

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.orm.jpa.EntityManagerHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionSynchronizationManager;
/** /**
* Implementation of Hibernate 3.1's CurrentSessionContext interface * Implementation of Hibernate 3.1's {@link CurrentSessionContext} interface
* that delegates to Spring's SessionFactoryUtils for providing a * that delegates to Spring's {@link SessionFactoryUtils} for providing a
* Spring-managed current Session. * Spring-managed current {@link Session}.
* *
* <p>This CurrentSessionContext implementation can also be specified in custom * <p>This CurrentSessionContext implementation can also be specified in custom
* SessionFactory setup through the "hibernate.current_session_context_class" * SessionFactory setup through the "hibernate.current_session_context_class"
@ -86,6 +87,7 @@ public class SpringSessionContext implements CurrentSessionContext {
return (Session) value; return (Session) value;
} }
else if (value instanceof SessionHolder) { else if (value instanceof SessionHolder) {
// HibernateTransactionManager
SessionHolder sessionHolder = (SessionHolder) value; SessionHolder sessionHolder = (SessionHolder) value;
Session session = sessionHolder.getSession(); Session session = sessionHolder.getSession();
if (!sessionHolder.isSynchronizedWithTransaction() && if (!sessionHolder.isSynchronizedWithTransaction() &&
@ -104,13 +106,18 @@ public class SpringSessionContext implements CurrentSessionContext {
} }
return session; return session;
} }
else if (value instanceof EntityManagerHolder) {
// JpaTransactionManager
return ((EntityManagerHolder) value).getEntityManager().unwrap(Session.class);
}
if (this.transactionManager != null && this.jtaSessionContext != null) { if (this.transactionManager != null && this.jtaSessionContext != null) {
try { try {
if (this.transactionManager.getStatus() == Status.STATUS_ACTIVE) { if (this.transactionManager.getStatus() == Status.STATUS_ACTIVE) {
Session session = this.jtaSessionContext.currentSession(); Session session = this.jtaSessionContext.currentSession();
if (TransactionSynchronizationManager.isSynchronizationActive()) { if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.registerSynchronization(new SpringFlushSynchronization(session)); TransactionSynchronizationManager.registerSynchronization(
new SpringFlushSynchronization(session));
} }
return session; return session;
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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; import org.springframework.util.Assert;
/** /**
* Holder wrapping a JPA EntityManager. * Resource holder wrapping a JPA {@link EntityManager}.
* JpaTransactionManager binds instances of this class to the thread, * {@link JpaTransactionManager} binds instances of this class to the thread,
* for a given EntityManagerFactory. * for a given {@link javax.persistence.EntityManagerFactory}.
*
* <p>Also serves as a base class for {@link org.springframework.orm.hibernate5.SessionHolder},
* as of 5.1.
* *
* <p>Note: This is an SPI class, not intended to be used by applications. * <p>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 { public class EntityManagerHolder extends ResourceHolderSupport {
@Nullable
private final EntityManager entityManager; private final EntityManager entityManager;
private boolean transactionActive; private boolean transactionActive;
@ -45,13 +49,13 @@ public class EntityManagerHolder extends ResourceHolderSupport {
private SavepointManager savepointManager; private SavepointManager savepointManager;
public EntityManagerHolder(EntityManager entityManager) { public EntityManagerHolder(@Nullable EntityManager entityManager) {
Assert.notNull(entityManager, "EntityManager must not be null");
this.entityManager = entityManager; this.entityManager = entityManager;
} }
public EntityManager getEntityManager() { public EntityManager getEntityManager() {
Assert.state(this.entityManager != null, "No EntityManager available");
return this.entityManager; return this.entityManager;
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.lang.reflect.Proxy;
import java.util.List; import java.util.List;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import javax.persistence.EntityNotFoundException;
import javax.persistence.FlushModeType; import javax.persistence.FlushModeType;
import javax.persistence.NoResultException; import javax.persistence.NoResultException;
import javax.persistence.Query; import javax.persistence.Query;
@ -39,14 +38,14 @@ import static org.junit.Assert.*;
* @author Rod Johnson * @author Rod Johnson
* @author Juergen Hoeller * @author Juergen Hoeller
*/ */
public abstract class AbstractContainerEntityManagerFactoryIntegrationTests extends AbstractEntityManagerFactoryIntegrationTests { public abstract class AbstractContainerEntityManagerFactoryIntegrationTests
extends AbstractEntityManagerFactoryIntegrationTests {
@Test @Test
public void testEntityManagerFactoryImplementsEntityManagerFactoryInfo() { public void testEntityManagerFactoryImplementsEntityManagerFactoryInfo() {
assertTrue(Proxy.isProxyClass(entityManagerFactory.getClass()));
assertTrue("Must have introduced config interface", entityManagerFactory instanceof EntityManagerFactoryInfo); assertTrue("Must have introduced config interface", entityManagerFactory instanceof EntityManagerFactoryInfo);
EntityManagerFactoryInfo emfi = (EntityManagerFactoryInfo) entityManagerFactory; EntityManagerFactoryInfo emfi = (EntityManagerFactoryInfo) entityManagerFactory;
// assertEquals("Person", emfi.getPersistenceUnitName()); assertEquals("Person", emfi.getPersistenceUnitName());
assertNotNull("PersistenceUnitInfo must be available", emfi.getPersistenceUnitInfo()); assertNotNull("PersistenceUnitInfo must be available", emfi.getPersistenceUnitInfo());
assertNotNull("Raw EntityManagerFactory must be available", emfi.getNativeEntityManagerFactory()); assertNotNull("Raw EntityManagerFactory must be available", emfi.getNativeEntityManagerFactory());
} }
@ -78,7 +77,7 @@ public abstract class AbstractContainerEntityManagerFactoryIntegrationTests exte
} }
@Test @Test
@SuppressWarnings({ "unused", "unchecked" }) @SuppressWarnings("unchecked")
public void testEntityManagerProxyIsProxy() { public void testEntityManagerProxyIsProxy() {
assertTrue(Proxy.isProxyClass(sharedEntityManager.getClass())); assertTrue(Proxy.isProxyClass(sharedEntityManager.getClass()));
Query q = sharedEntityManager.createQuery("select p from Person as p"); Query q = sharedEntityManager.createQuery("select p from Person as p");
@ -107,14 +106,13 @@ public abstract class AbstractContainerEntityManagerFactoryIntegrationTests exte
try { try {
Person notThere = sharedEntityManager.getReference(Person.class, 666); Person notThere = sharedEntityManager.getReference(Person.class, 666);
// We may get here (as with Hibernate). // We may get here (as with Hibernate). Either behaviour is valid:
// Either behaviour is valid: throw exception on first access // throw exception on first access or on getReference itself.
// or on getReference itself.
notThere.getFirstName(); notThere.getFirstName();
fail("Should have thrown an EntityNotFoundException"); fail("Should have thrown an EntityNotFoundException or ObjectNotFoundException");
} }
catch (EntityNotFoundException ex) { catch (Exception ex) {
// expected assertTrue(ex.getClass().getName().endsWith("NotFoundException"));
} }
} }
@ -209,6 +207,8 @@ public abstract class AbstractContainerEntityManagerFactoryIntegrationTests exte
@Test @Test
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void testQueryNoPersonsNotTransactional() { public void testQueryNoPersonsNotTransactional() {
endTransaction();
EntityManager em = entityManagerFactory.createEntityManager(); EntityManager em = entityManagerFactory.createEntityManager();
Query q = em.createQuery("select p from Person as p"); Query q = em.createQuery("select p from Person as p");
List<Person> people = q.getResultList(); List<Person> people = q.getResultList();
@ -223,12 +223,12 @@ public abstract class AbstractContainerEntityManagerFactoryIntegrationTests exte
} }
@Test @Test
@SuppressWarnings({ "unused", "unchecked" }) @SuppressWarnings("unchecked")
public void testQueryNoPersonsShared() { public void testQueryNoPersonsShared() {
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(entityManagerFactory); Query q = this.sharedEntityManager.createQuery("select p from Person as p");
Query q = em.createQuery("select p from Person as p");
q.setFlushMode(FlushModeType.AUTO); q.setFlushMode(FlushModeType.AUTO);
List<Person> people = q.getResultList(); List<Person> people = q.getResultList();
assertEquals(0, people.size());
try { try {
assertNull(q.getSingleResult()); assertNull(q.getSingleResult());
fail("Should have thrown NoResultException"); fail("Should have thrown NoResultException");
@ -243,7 +243,7 @@ public abstract class AbstractContainerEntityManagerFactoryIntegrationTests exte
public void testQueryNoPersonsSharedNotTransactional() { public void testQueryNoPersonsSharedNotTransactional() {
endTransaction(); endTransaction();
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(entityManagerFactory); EntityManager em = this.sharedEntityManager;
Query q = em.createQuery("select p from Person as p"); Query q = em.createQuery("select p from Person as p");
q.setFlushMode(FlushModeType.AUTO); q.setFlushMode(FlushModeType.AUTO);
List<Person> people = q.getResultList(); List<Person> people = q.getResultList();

View File

@ -48,12 +48,8 @@ import static org.junit.Assert.*;
public abstract class AbstractEntityManagerFactoryIntegrationTests { public abstract class AbstractEntityManagerFactoryIntegrationTests {
protected static final String[] ECLIPSELINK_CONFIG_LOCATIONS = new String[] { 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/eclipselink/eclipselink-manager.xml",
"/org/springframework/orm/jpa/inject.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"};
private static ConfigurableApplicationContext applicationContext; private static ConfigurableApplicationContext applicationContext;

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 { public class EclipseLinkEntityManagerFactoryIntegrationTests extends AbstractContainerEntityManagerFactoryIntegrationTests {
@Override
protected String[] getConfigLocations() {
return ECLIPSELINK_CONFIG_LOCATIONS;
}
@Test @Test
public void testCanCastNativeEntityManagerFactoryToEclipseLinkEntityManagerFactoryImpl() { public void testCanCastNativeEntityManagerFactoryToEclipseLinkEntityManagerFactoryImpl() {
EntityManagerFactoryInfo emfi = (EntityManagerFactoryInfo) entityManagerFactory; EntityManagerFactoryInfo emfi = (EntityManagerFactoryInfo) entityManagerFactory;

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -41,7 +41,8 @@ public class HibernateEntityManagerFactoryIntegrationTests extends AbstractConta
@Override @Override
protected String[] getConfigLocations() { 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"};
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.beans.factory.annotation.Autowired;
import org.springframework.orm.jpa.AbstractContainerEntityManagerFactoryIntegrationTests; import org.springframework.orm.jpa.AbstractContainerEntityManagerFactoryIntegrationTests;
import org.springframework.orm.jpa.EntityManagerFactoryInfo;
import static org.junit.Assert.*; 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 @Test
public void testEntityManagerFactory2() { public void testEntityManagerFactory2() {
EntityManager em = this.entityManagerFactory2.createEntityManager(); EntityManager em = this.entityManagerFactory2.createEntityManager();

View File

@ -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<Person> people = q.getResultList();
assertEquals(1, people.size());
assertEquals(firstName, people.get(0).getFirstName());
}
}

View File

@ -24,4 +24,8 @@
</property> </property>
</bean> </bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
</beans> </beans>

View File

@ -22,5 +22,9 @@
</props> </props>
</property> </property>
</bean> </bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
</beans> </beans>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="entityManagerFactory" name="Person" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean" primary="true">
<property name="annotatedClasses">
<list>
<value>org.springframework.orm.jpa.domain.DriversLicense</value>
<value>org.springframework.orm.jpa.domain.Person</value>
</list>
</property>
<property name="dataSource" ref="dataSource"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="entityManagerFactory"/>
</bean>
<bean id="hibernateStatistics" factory-bean="entityManagerFactory" factory-method="getStatistics"/>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
</beans>

View File

@ -15,6 +15,7 @@
</property> </property>
<property name="jpaPropertyMap"> <property name="jpaPropertyMap">
<props> <props>
<prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate5.SpringSessionContext</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</prop> <prop key="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</prop>
</props> </props>
</property> </property>
@ -23,6 +24,10 @@
</property> </property>
</bean> </bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="sessionFactory" factory-bean="entityManagerFactory" factory-method="getSessionFactory"/> <bean id="sessionFactory" factory-bean="entityManagerFactory" factory-method="getSessionFactory"/>
<bean id="hibernateStatistics" factory-bean="sessionFactory" factory-method="getStatistics"/> <bean id="hibernateStatistics" factory-bean="sessionFactory" factory-method="getStatistics"/>

View File

@ -7,10 +7,6 @@
<property name="entityManagerFactory" ref="entityManagerFactory"/> <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean> </bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/> <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:mem:xdb"/> <property name="url" value="jdbc:hsqldb:mem:xdb"/>
@ -18,14 +14,4 @@
<property name="password" value=""/> <property name="password" value=""/>
</bean> </bean>
<!-- Datasource for using an existing database. make sure you turn off generateDLL otherwise
on multiple runs it will break it.
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:file:target/classes/db/test"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
</bean>
-->
</beans> </beans>

View File

@ -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. 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 This includes web containers such as Tomcat as well as stand-alone applications and
integration tests with sophisticated persistence requirements. 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 <<orm-jpa-hibernate,Native Hibernate setup for JPA interaction>> for details.
==== ====
The `LocalContainerEntityManagerFactoryBean` gives full control over 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. Spring provides dialects for the EclipseLink and Hibernate JPA implementations.
See the next section for details on the `JpaDialect` mechanism. 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 <<orm-jpa-hibernate,Native Hibernate setup for JPA interaction>> for details.
====
[[orm-jpa-dialect]] [[orm-jpa-dialect]]
==== JpaDialect and JpaVendorAdapter ==== 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. 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]] [[oxm]]