Spring Data JPA + EclipseLink: Configuring Spring-Boot to use EclipseLink as the JPA provider
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 Map
with 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: Configuring Spring-Boot to use EclipseLink as the JPA provider"
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 aMap
with properties to initialize theEntityManagerFactory
Getting 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:
>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:
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 :)
The sping-examples git has no substantial help.
public class EclipseLinkJpaConfiguration extends JpaBaseConfiguration
it 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