Spring Data JPA + EclipseLink: Configure Spring Boot with EclipseLink JPA
Introduction
Spring uses Hibernate as the default JPA implementation by default. 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 post, I'll show you how to set up a Spring Boot application to use EclipseLink with an in-memory H2 database. However, you can use the same technique with any other database.
The complete source code for this post can be found on 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 project file.
If you are using some other build tool this step won't be necessary, or at least it will be different.
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 previous 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).
@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;
}
}Since 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 Mapwith the JPA properties for the JPA implementation.
You can return an empty Map if you don't require any vendor-specific configuration.
To complete the configuration, we are going to set up some custom Beans that will be picked up by 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.
However, 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:
@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.
These will allow you to fine tune your database connection pool.
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


Comments in "Spring Data JPA + EclipseLink: Configure Spring Boot with EclipseLink JPA"
Thanks for article, but where is "initJpaProperties()" method?
I'm sorry, the code published was from one of my private projects and I forgot to include the method requested.
initJpaProperties()simply initializes aMapwith properties to initialize theEntityManagerFactoryGetting above error in my springboot gradle app.
Please help on this
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: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.
there an unresolvable circular reference?" plus I had to add a Constructor. Help would be appreciated :)
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"}}The sping-examples git has no substantial help.
public class EclipseLinkJpaConfiguration extends JpaBaseConfigurationit demands constructor
please help
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