Spring Data JPA
Post updated on 2020-06-28.
By default Spring uses Hibernate as the default JPA vendor. Although Hibernate is a good choice, some of us may prefer to use EclipseLink as it was supposed to be the reference implementation for the Java Persistence JSR.
In this tutorial we will setup a Spring Boot application to use EclipseLink with an in-memory H2 database, although it can be used with any other database. You can check the source code in GitHub.
Fixing dependencies
In order to use EclipseLink, it’s recommended to remove Hibernate’s entity manager from the classpath to avoid collision problems. This is as simple as adding some exclusions to your application’s Gradle script or Maven’s pom.xml
file. If you are using some other build tool this step won’t be necessary, or at least it will be different.
1 2 3 4 5 6 7 8 9 10 |
dependencies { implementation ('org.eclipse.persistence:eclipselink:2.7.7') implementation ('org.springframework.boot:spring-boot-starter-web') implementation('org.springframework.boot:spring-boot-starter-actuator') implementation('org.springframework.boot:spring-boot-starter-data-jpa'){ exclude group: 'org.hibernate', module: 'hibernate-core' exclude group: 'org.hibernate.common', module: 'common-annotations' } //...Your project dependencies... } |
In the above code block we are adding the exclusion to the build.gradle script of our example project.
Spring configuration
In order to change the JPA vendor, we are going to extend JpaBaseConfiguration
class and annotate it as a Spring @Configuration
class (CustomJpaConfiguration
).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
@Configuration @EnableTransactionManagement @EnableJpaRepositories("com.marcnuri.demo.springeclipselink.repository") public class CustomJpaConfiguration extends JpaBaseConfiguration { protected CustomJpaConfiguration(DataSource dataSource, JpaProperties properties, ObjectProvider<JtaTransactionManager> jtaTransactionManager) { super(dataSource, properties, jtaTransactionManager); } @Override protected AbstractJpaVendorAdapter createJpaVendorAdapter() { return new EclipseLinkJpaVendorAdapter(); } @Override protected Map<String, Object> getVendorProperties() { final Map<String, Object> ret = new HashMap<>(); ret.put(PersistenceUnitProperties.BATCH_WRITING, BatchWriting.JDBC); return ret; } @Bean("entityManagerFactory") public LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactory( EntityManagerFactoryBuilder builder, DataSource dataSource) { return builder .dataSource(dataSource) .packages("com.marcnuri.demo.springeclipselink.repository") .persistenceUnit("YourPersistenceUnitName") .properties(initJpaProperties()).build(); } @Bean public static DataSource dataSource() { final DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("org.h2.Driver"); dataSource.setUrl("jdbc:h2:mem:marcnuridemo;DB_CLOSE_DELAY=-1"); dataSource.setUsername("sa"); dataSource.setPassword("password"); return dataSource; } @Bean public static PlatformTransactionManager transactionManager(EntityManagerFactory emf) { final JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(emf); return transactionManager; } @Bean @Primary public static JpaProperties properties() { final JpaProperties jpaProperties = new JpaProperties(); jpaProperties.setShowSql(true); jpaProperties.setDatabasePlatform("org.eclipse.persistence.platform.database.H2Platform"); return jpaProperties; } private static Map<String, ?> initJpaProperties() { final Map<String, Object> ret = new HashMap<>(); // Add any JpaProperty you are interested in and is supported by your Database and JPA implementation ret.put(PersistenceUnitProperties.BATCH_WRITING, BatchWriting.JDBC); ret.put(PersistenceUnitProperties.LOGGING_LEVEL, SessionLog.FINEST_LABEL); ret.put(PersistenceUnitProperties.WEAVING, "false"); ret.put(PersistenceUnitProperties.DDL_GENERATION, PersistenceUnitProperties.CREATE_ONLY); ret.put(PersistenceUnitProperties.DDL_GENERATION_MODE, PersistenceUnitProperties.DDL_DATABASE_GENERATION); return ret; } } |
As we are extending JpaBaseConfiguration, we must implement the createJpaVendorAdapter()
and getVendorProperties()
methods. The first one must return a subclass of AbstractJpaVendorAdapter
, in this case it’s really easy because Spring provides an out of the box adapter for EclipseLink (EclipseLinkJpaVendorAdapter
). The second method (getVendorProperties()) must return a Map
with the JPA properties for the JPA implementation. You can return an empty Map
if you don’t need any vendor specific configuration.
To complete the configuration, we are going to set up some custom Beans that will be picked up in the appropriate Autowired fields used by Spring.
The first Bean entityManagerFactory
is used to build an EntityManagerFactory
using the supplied builder and specifying our custom parameters. In this case we specify the default PersistenceUnit
name (we may have more than one), the dataSource, the package names where our Entity annotated classes are and some extra JPA properties.
We must also define a transactionManager
bean. In this case we are going to use Spring’s JpaTransactionManager.
Next we define a DataSource for the Persistence provider. In this case we are using the simple DriverManagerDataSource, although in a production environment it’s recommended to use a connection pooled provider. Here we specify the details for our connection. These parameters will override those specified in the application properties file.
Connection Pooling
If we are using Spring-Boot with embedded Apache Tomcat server, we can use Tomcat’s container DataSource which has connection pooling capabilities:
1 2 3 4 5 6 7 8 9 10 11 |
@Bean public DataSource dataSource() { //In classpath from spring-boot-starter-web final Properties pool = new Properties(); pool.put("driverClassName", "org.postgresql.Driver"); pool.put("url", "jdbc:postgresql://example.com:5432/DatabaseName"); pool.put("username", "user"); pool.put("password", "password"); pool.put("minIdle", 1); return new org.apache.tomcat.jdbc.pool.DataSourceFactory().createDataSource(pool); } |
In this case, instead of returning Spring’s DriverManagerDataSource
, we are going to use Tomcat’s DataSourceFactory to generate a DataSource with the specified connection pooling parameters. You can find a list of available properties for Tomcat’s DataSource here, in order to fine tune your database connections.
Finally, when you run your application, Spring-Boot will pick up your configuration and create the EntityManagerFactory using EclipseLink instead of Hibernate.
The full source code for this post can be found on GitHub.
Hello,
Thanks for article, but where is “initJpaProperties()” metho?
I am also interested in the initJpaProperties method. 🙁
Hello
I’m sorry, the code published was from one of my private projects and I forgot to include the method requested.
initJpaProperties()
simply initializes aMap
with properties to initialize theEntityManagerFactory
The web application [ROOT] appears to have started a thread named [Abandoned connection cleanup thread] but has failed to stop it
Getting above error in my springboot gradle app.
Please help on this
Nice article!
But, There requires a constructor since the super class has no constructor without parameters.
And when I add constructor as follows:
public CustomJpaConfiguration(DataSource dataSource, JpaProperties jpaProperties, ObjectProvider jtaTransactionManager, ObjectProvider transactionManagerCustomizers) {
super(dataSource, jpaProperties, jtaTransactionManager, transactionManagerCustomizers);
}
I’ve encountered a circular reference error it starts for configuration:
Exception encountered during context initialization – cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘customJpaConfiguration’ defined in file [/Users/uengine/Downloads/msavuejs/target/classes/com/moornmo/ltms/CustomJpaConfiguration.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘customJpaConfiguration’: Requested bean is currently in creation: Is there an unresolvable circular reference?
How many roads to somewhere…
My hope was – hey a solution for my problem. But in deatils a complete other road.
My way:
dependencies {
compile(“org.eclipse.persistence:javax.persistence:$jpaApiVersion”)
compile(“org.eclipse.persistence:org.eclipse.persistence.jpa:$jpaEclipseVersion”)
runtime(‘org.postgresql:postgresql’)
…
}
and the connection setting are a part of the application.properties. My only problem – until now – is: outside the IDE the persistence.xml is not found in the final jar. It is inside, but on a different place – do you have an idea why? The build was made with gradle 3.4.1.
I get an Exception “Error creating bean with name ‘customJpaConfiguration’: Requested bean is currently in creation: Is
there an unresolvable circular reference?” plus I had to add a Constructor. Help would be appreciated 🙂
Is there any example for Spring f/w without Spring boot along with EclipseLink. am trying to configure Spring framework 4.3.6 with EclipseLink. I am trying to run the application on Jboss. However when deployed on Jboss it tries to load Hibernate instead of eclipse link and throws below error each time.
17:33:47,036 ERROR [org.jboss.msc.service.fail] (ServerService Thread Pool — 51) MSC000001: Failed to start service jboss.persistenceunit.”HelloWeb.war#Eclipselink_JPA”: org.jboss.msc.service.StartException in service jboss.persistenceunit.”HelloWeb.war#Eclipselink_JPA”: javax.persistence.PersistenceException: [PersistenceUnit: Eclipselink_JPA] Unable to build EntityManagerFactory
at org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1.run(PersistenceUnitServiceImpl.java:103)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) [rt.jar:1.8.0_102]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) [rt.jar:1.8.0_102]
at java.lang.Thread.run(Unknown Source) [rt.jar:1.8.0_102]
at org.jboss.threads.JBossThread.run(JBossThread.java:122) [jboss-threads-2.1.2.Final-redhat-1.jar:2.1.2.Final-redhat-1]
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: Eclipselink_JPA] Unable to build EntityManagerFactory
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:925)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:900)
at org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:76)
at org.jboss.as.jpa.service.PersistenceUnitServiceImpl.createContainerEntityManagerFactory(PersistenceUnitServiceImpl.java:200)
at org.jboss.as.jpa.service.PersistenceUnitServiceImpl.access$600(PersistenceUnitServiceImpl.java:57)
at org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1.run(PersistenceUnitServiceImpl.java:99)
… 4 more
Caused by: org.hibernate.HibernateException: Connection cannot be null when ‘hibernate.dialect’ not set
at org.hibernate.service.jdbc.dialect.internal.DialectFactoryImpl.determineDialect(DialectFactoryImpl.java:98)
at org.hibernate.service.jdbc.dialect.internal.DialectFactoryImpl.buildDialect(DialectFactoryImpl.java:68)
at org.hibernate.engine.jdbc.internal.JdbcServicesImpl.configure(JdbcServicesImpl.java:174)
at org.hibernate.service.internal.StandardServiceRegistryImpl.configureService(StandardServiceRegistryImpl.java:85)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:184)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:156)
at org.hibernate.cfg.Configuration.buildTypeRegistrations(Configuration.java:1827)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1785)
at org.hibernate.ejb.EntityManagerFactoryImpl.(EntityManagerFactoryImpl.java:96)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:915)
… 9 more
17:33:47,056 ERROR [org.jboss.as.controller.management-operation] (Controller Boot Thread) JBAS014612: Operation (“deploy”) failed – address: ([(“deployment” => “HelloWeb.war”)]) – failure description: {“JBAS014671: Failed services” => {“jboss.persistenceunit.\”HelloWeb.war#Eclipselink_JPA\”” => “org.jboss.msc.service.StartException in service jboss.persistenceunit.\”HelloWeb.war#Eclipselink_JPA\”: javax.persistence.PersistenceException: [PersistenceUnit: Eclipselink_JPA] Unable to build EntityManagerFactory
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: Eclipselink_JPA] Unable to build EntityManagerFactory
Caused by: org.hibernate.HibernateException: Connection cannot be null when ‘hibernate.dialect’ not set”}}
Can u provide the full source code for the steps include the pom.xml
Can you also mention the list of properties that go into the bean xml or the application.properties for configuring eclipselink? Too many combinations to even think creatively and guess what they could be. If you had a reference to a site that mentions this, that’d be great too.
The sping-examples git has no substantial help.
Hey i am having a problem with the extension:
public class EclipseLinkJpaConfiguration extends JpaBaseConfiguration
Error:(22, 8) java: constructor JpaBaseConfiguration in class org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration cannot be applied to given types;
required: javax.sql.DataSource,org.springframework.boot.autoconfigure.orm.jpa.JpaProperties,org.springframework.beans.factory.ObjectProvider
found: no arguments
reason: actual and formal argument lists differ in length
it demands constructor
please help
The post has been updated to be Spring Boot compatible, many of the problems in the comments are due to the post being really outdated.
I’ve also implemented a complete working project so that you can play around with the suggestions in the post:
https://github.com/marcnuri-demo/spring-boot-eclipselink