Spring Data JPA + EclipseLink: Configurando Spring-Boot para usar EclipseLink como proveedor de JPA
IntroducciĆ³n
Spring emplea Hibernate como proveedor de JPA por defecto. Aunque Hibernate es una buena elecciĆ³n, algunos preferimos emplear EclipseLink debido a que en un principio se suponĆa que era la implementaciĆ³n de referencia para el JSR de Persistencia en Java.
En este artĆculo, mostrarĆ© como configurar una aplicaciĆ³n Spring Boot para usar EclipseLink con una base de datos H2 en memoria. No obstante, puedes emplear la misma tĆ©cnica con cualquier otra base de datos.
El cĆ³digo fuente completo de este artĆculo puede encontrarse en GitHub.
Arreglando dependencias
Para poder emplear EclipseLink, es recomendable quitar el EntityManager de Hibernate del classpath para evitarnos colisiones.
Para ello simplemente tenemos que aƱadir una exclusiĆ³n en nuestro script de Gradle o en el fichero pom.xml
de Maven.
Si estamos empleando alguna otra herramienta de compilaciĆ³n, no serĆ” necesario este paso (o serĆ” distinto).
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...
}
En el fragmento de cĆ³digo anterior, estamos aƱadiendo la exclusiĆ³n al script build.gradle de nuestro proyecto de ejemplo.
Configurando Spring
Para poder cambiar el JPA vendor, vamos a extender JpaBaseConfiguration
y anotarlo como una clase @Configuration
(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;
}
}
Al extender JpaBaseConfiguration
,
debemos de implementar los mƩtodos createJpaVendorAdapter()
y getVendorProperties()
.
El primero debe de devolver una implementaciĆ³n de AbstractJpaVendorAdapter
,
en este caso es realmente sencillo porque Spring ya dispone de un adaptador preconfigurado para EclipseLink (EclipseLinkJpaVendorAdapter
).
El segundo mƩtodo, getVendorProperties()
, debe devolver un Map
con las propiedades JPA para la implementaciĆ³n JPA que elijamos.
Podemos devolver un Map
vacĆo si no queremos customizar nada.
Para completar la configuraciĆ³n vamos a configurar varios Beans que Spring emplearĆ” cuando sea necesario.
El primer Bean, entityManagerFactory
, se usa para construir un EntityManagerFactory
empleando el constructor facilitado y especificando nuestros parƔmetros.
En este caso, le estamos facilitando el nombre de nuestro PersistenceUnit
, el dataSource y el nombre de los paquetes donde se encuentran las Entidades y alguna propiedad JPA mƔs.
TambiƩn definimos un Bean como transactionManager
.
En este caso empleamos el gestor de transacciones facilitado por Spring, JpaTransactionManager
.
Por Ćŗltimo, definimos un DataSource para nuestro proveedor de persistencia.
En este caso empleamos el simple DriverManagerDataSource
.
Sin embargo, en un entorno de producciĆ³n es recomendable usar un datasource que pueda ofrecer un pool de conexiones.
Todos estos parĆ”metros serĆ”n usados con prioridad respecto a los que se hayan podido definir en el fichero de propiedades de la aplicaciĆ³n.
Connection Pooling / Pool de conexiones a la base de datos
Si estamos empleando Spring-Boot con el servidor Apache Tomcat embebido, podemos usar el DataSource que emplea Tomcat en su contendedor, que dispone de capacidad para hacer un pool de conexiones a la base de datos.
@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);
}
En este caso, en lugar de devolver DriverManagerDataSource
, vamos a usar el DataSourceFactory
de Tomcat para generar un DataSource con los parƔmetros especificados.
En esta pƔgina puedes encontrar un listado con todos los parƔmetros aceptados para poner a punto correctamente las conexiones a la base de datos.
Finalmente, cuando ejecutemos nuestra aplicaciĆ³n, Spring-Boot cogerĆ” nuestra configuraciĆ³n y crearĆ” un EntityManagerFactory
usando EclipseLink en lugar de Hibernate.
El cĆ³digo fuente completo de este artĆculo puede encontrarse en GitHub