Configuring Hibernate JPA with Spring – tutorial
After going through this tutorial you’ll know how to configure Hibernate JPA with Spring. You’ll learn how to set it up using persistence.xml, as well as fully xml-less.
Please remember, that aside from implementing JPA, Hibernate has it’s own persistence API, bringing a little more functionality on top of the official Java standard. Some of it will be covered in upcoming articles.
Traditionally, full source code can be found on allAroundJava github.
Setting up the dependencies
Running Hibernate with Spring requires a much more extensive set of dependencies.
It’s all the same configuration
One thing to realize is that configuring Hibernate with Spring is very similar to configuring Hibernate standalone. Before going through this article, I definitely recommend going through Java SE configuration in previous post.
Just as with standalone Hibernate, we need to configure our data source and a set of persistent classes. We need an EntityManagerFactory and a way to access EntityManager inside Dao (repository) objects. Spring is smart enough to do some of the things for us if we choose. Lets see how it works.
It’s all about EntityManagers
When configuring Hibernate with Spring, you need to choose how you’d like to create and manage your EntityManagers. There’s two options you can choose from:
- Manage EntityManagers yourself.
- Let Java EE container manage them for you.
Before we move to explaining the difference, lets understand a thing or two about Persistence Context.
Persistence Context is always associated with an EntityManager, for which it looks after all entities in persistent state. It stores information about any modifications to persistent objects which need to be populated to a database. Whenever you fetch an entity from the database, it’s stored in the Persistence Context. When fetching the same entity again, the EntityManager, rather than launching a SELECT query, reaches out to Persistence Context to see if this entity is already there. This way the Persistence Context acts as Hibernate’s first level cache. Whenever a persistent entity is modified, it’s marked dirty in Persistence Context. Then once Persistence Context is flushed, UPDATE statements are sent to the database for all the dirty entities.
Container managed EntityManagers
When using a Java EE container, you typically define a JTA transaction manager to handle transactions. JTA transactions often span several application components. When several components act within the same JTA transaction, they typically need access to the same Persistence Context. When that happens, Java EE container is able to distribute EntityManagers, so that the components acting within same JTA transaction receive EntityManagers with the same Persistence Contexts.
Application managed EntityManagers
When there’s no need to share Persistence Contexts across components, application is free to handle EntityManager creation or removal itself. In this approach, each EntityManager will use its own, isolated Persistence Context.
This is exactly what we did in the previous article where we called createEntityManager() every time we wanted to persist or retrieve an entity from the database.
Configuring Hibernate with application managed EntityManagers
This approach will rely heavily on persistence.xml file. There’s really no need to inject an external datasource or EntityManagerFactory in case of standalone java application, therefore most of the configuration details are placed in well known Hibernate configuration file. The persistence unit, set of persistent classes and a datasource are configured here in exactly same way as with standalone Hibernate application.
Setting up persistence.xml
This persistence.xml defines a single persistence unit called hibernateSpring, with a single Car entity. I’m using an in memory database called H2.
Because there’s a lot of configuration already present in persistence.xml, we don’t need too much Spring configuration. All we need to do is to configure an application managed variant of EntityManagerFactory. In this case it’s done with Spring’s LocalEntityManagerFactoryBean.
Implementing Spring configuration
@Configuration
@ComponentScan(basePackages = "com.allaroundjava.dao")
@EnableTransactionManagement
public class AppConfig {
@Bean
public LocalEntityManagerFactoryBean entityManagerFactory() {
LocalEntityManagerFactoryBean localEmfBean =
new LocalEntityManagerFactoryBean();
localEmfBean.setPersistenceUnitName("hibernateSpring");
return localEmfBean;
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
}
The only thing that LocalEntityManagerFactoryBean needs is name of the persitence unit. It’s aware that connection information and a set of persistent classes come from the persistence.xml file in its default location.
In order for Spring to manage transactions for us, we’re defining a JPA variant of PlatformTransactionManager and annotating the configuration bean with @EnableTransactionManagement.
Configuring Hibernate using container managed EntityManager approach
This approach will let us configure several beans as parameters to EntityManagerFactory. These are DataSource, which may be coming from JNDI and a JpaVendorAdaper along with additional properties.
@Configuration
@EnableTransactionManagement
@ComponentScan(basePackages = {"com.allaroundjava.container.dao"})
public class AppConfig {
@Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean emf =
new LocalContainerEntityManagerFactoryBean();
emf.setPackagesToScan("com.allaroundjava.model");
emf.setDataSource(createDataSource());
emf.setJpaVendorAdapter(createJpaVendorAdapter());
emf.setJpaProperties(createHibernateProperties());
emf.afterPropertiesSet();
return emf;
}
private DataSource createDataSource() {
EmbeddedDatabaseBuilder builder =
new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.H2).build();
}
private JpaVendorAdapter createJpaVendorAdapter() {
return new HibernateJpaVendorAdapter();
}
private Properties createHibernateProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.hbm2ddl.auto", "create-drop");
properties.setProperty(
"hibernate.dialect", "org.hibernate.dialect.H2Dialect");
return properties;
}
@Bean
PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
}
There’s significantly more configuration with this approach, however we’ve completely eliminated the need for persistence.xml and manual maintenance required when editing a set of persistent classes.
Factory’s .setPachagesToScan() method allows us to indicate which packages in our application contain persistent classes annotated with @Entity. This way we don’t need to explicitly add them to a persistence unit definition each time a new entity class is added.
One word of explaination here. We’re configuring Hibernate just as if application was running in Java EE Conainer, however it’s not running in any container at all. How is that possible?
Spring allows us to inject an external DataSource just as if it was coming from Java EE container. It can also let us inject EntityManagerFactory from JNDI if we wish. Because this application is not running in a container, I’m creating a datasource bean directly in Spring configuration.With such setup, Spring will act as a container, distributing EntityManagers across requesting classes, attaching them to transactions.
Creating a data access class
Next up we’ll create a class handling all the persistence related activities. This is a common code for both application and container managed approaches. Configuring Hibernate without Spring, we had to ensure EntityManagers is retrieved from EntityManagerFactory each time we wanted to perform database related operations. Now Spring will wire EntityManager for us. All we need is a couple of annotations.
@Repository
@Transactional
public class ContainerManagedCarDaoImpl implements CarDao {
@PersistenceContext
private EntityManager entityManager;
public Optional getById(Long id) {
Car car = entityManager.find(Car.class, id);
return Optional.ofNullable(car);
}
public Long persist(Car c) {
entityManager.persist(c);
return c.getId();
}
}
The @Repository annotation serves two purposes. First, it marks the class as a Spring bean and makes it discoverable by component scanning.
Second, it tells PersistenceExceptionTranslationPostProcessor to catch any platform specific exceptions thrown by this class and rethrow them as Spring unchecked exceptions.
In order for it to work, PersistenceExceptionTranslationPostProcessor needs to be configured in Spring context. If your Spring configuration bean is annotated with any of the two @ComponentScan or @AnnotationConfig, then the exception post processor is already registered for you.
@Transactional annotation ensures that each method in the repository class is launched within a transaction. Thanks to that, class logic is not polluted with code related to transaction management.
@PersistenceContext annotation wires an EntityManager directly to our Dao class. Well, it does not inject EntityManager directly, it rather injects a proxy class. Whenever any EntityManager method is called, proxy will choose between:
- Calling it on an existing EntityManager associated with current transaction,
- Creating a new EntityManager and calling the method on it.
Thanks to Spring, the Dao class can purely deal with persistence related activities. All the coode related to EntityManager creation and handling transactions is taken care of by the framework.
Will it work?
The usage of Spring-configured repository class is exactly the same as in Spring-less case.
Test class is running with SpringJUnit4ClassRunner, which will ensure Spring context is loaded and proper CarDao implementation is wired in our test.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class ContainerManagedCarDaoImplTest {
@Autowired
private CarDao carDao;
@Test
public void whenPersist_thenNoExceptionIsThrown() {
Car car = new Car();
car.setMake("Mini");
car.setModel("Cooper S");
car.setManufacturedAt(LocalDate.now());
carDao.persist(car);
}
@Test
public void whenEntityIsPersisted_thenItsRetrievedFromDb() {
Car car = new Car();
car.setMake("Subaru");
car.setModel("Impreza");
car.setManufacturedAt(LocalDate.now());
carDao.persist(car);
Optional carFetched = carDao.getById(car.getId());
Assert.assertTrue(carFetched.isPresent());
}
@Test
public void whenRecordNotExists_thenGetByIdReturnsEmptyOptional() {
Optional carFetched = carDao.getById(789L);
Assert.assertFalse(carFetched.isPresent());
}
}
Here’s some more useful tips and tricks
- Find out what Hibernate framework is and why we use it
- Setting up a complete Spring and Hibernate application example.
Excellent article Adam! Useful, especially part about understanding persistence context with entity managers.
It would be nice to add more explanation of persistence unit vs data source.
Small technical suggestion: blocks of code could be formatted together with number of lines. Then you and comment writers can reference to specific line of published code. Maybe take a look on https://prismjs.com/plugins/line-numbers/