Disable/Enable eclipselink connection pool - spring-boot

I am writing a Spring Boot application and configured Eclipselink as a JPA provider. I would like to disable Eclipselink external and internal connection pools and use tomcat to provide connection to Eclipselink.
Could you suggest is this approach is good to go? If I have to give pooling to Eclipselink control, I wouldn't be able to use spring orm and jpa features. Several things happens behind the scenes such as EntityManagerFactory and EntityManager I have to manage in application.
My questions are:
Which one is better/smooth approach with Spring Boot + Eclipselink for connection pooling.
How to disable/enable eclipselink internal or external connection pools. Looking at the documentation I couldn't get clear idea.
Thank you for your help!
Below is my eclipselink configuration:
public class EclipseLinkJpaConfig extends JpaBaseConfiguration {
#Override
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
final EclipseLinkJpaVendorAdapter adapter = new EclipseLinkJpaVendorAdapter();
return adapter;
}
#Override
protected Map<String, Object> getVendorProperties() {
final HashMap<String, Object> map = new HashMap<String, Object>();
map.put(PersistenceUnitProperties.WEAVING, "static");
map.put(PersistenceUnitProperties.DEPLOY_ON_STARTUP, "true");
map.put(PersistenceUnitProperties.TARGET_SERVER, "config.ConnectionPlatform");
return map;
}
}
Data source config in application.properties below:
spring.datasource.initial-size=5
spring.datasource.max-wait=30000
spring.datasource.max-active=10
spring.datasource.max-idle=10
spring.datasource.min-idle=5
spring.datasource.test-on-borrow=true

Related

Spring boot Kafka metrics configuration

We have a spring boot microservice using spring-kafka. There are a couple of Kafka listeners and producers and the Kafka related metrics are successfully presented at the actuator endpoint. But when adding another maven dependency with custom metrics, the Kafka related metrics provided by spring boot are removed. I suspect it's related to introducing new MeterRegistry bean, but overriding it with the same bean definition as in NoOpMeterRegistryConfiguration in spring-boot-actuator-autoconfigure module of spring-boot didn't help. I've also tried debugging KafkaMetricsAutoConfiguration, and it looks like all the beans are successfully instantiated. Can somebody with more experience with spring-boot-actuator-autoconfigure help me to understand the default configurations for Kafka related metrics, please?
The spring configuration in the new maven dependency is pretty simple, just ProducerFactory and MeterRegistry beans are defined
#Bean
#Primary
public ProducerFactory<Object, Object> producerFactory(MessageCounter messageCounter) {
Map<String, Object> configs = this.kafkaProperties.buildProducerProperties();
configs.put("interceptor.classes", Collections.singletonList(RecordSuccessfullySentInterceptor.class));
configs.put("message.counter.bean", messageCounter);
DefaultKafkaProducerFactory<Object, Object> producerFactory = new DefaultKafkaProducerFactory(configs);
producerFactory.setTransactionIdPrefix(this.transactionId);
return producerFactory;
}
#Bean
public MeterRegistry meterRegistry() {
return new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
}
#Bean({"retryProducerFactory"})
public ProducerFactory<Object, Object> retryProducerFactory(MessageCounter messageCounter) {
Map<String, Object> configs = this.kafkaProperties.buildProducerProperties();
configs.put("interceptor.classes", Collections.singletonList(RecordSuccessfullySentInterceptor.class));
configs.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
configs.put("message.counter.bean", messageCounter);
DefaultKafkaProducerFactory<Object, Object> producerFactory = new DefaultKafkaProducerFactory(configs);
producerFactory.setTransactionIdPrefix(this.transactionId);
return producerFactory;
}
It may be possible some transient dependency is messing up spring boot autoconfiguration for Kafka related metrics though.

Java JobRunr when using Spring Boot Redis Starter

How do I create and use the Redis connection that spring-boot-starter-data-redis creates? It doesn't seem like there is a Bean for RedisClient created by the default auto configuration so I'm not sure of the best way to do this.
The documentation does state that in this case you need to create the StorageProvider yourself which is fine, but can you reuse what Spring Boot has already created. I believe this would need to be a pooled connection which you would also need to enable through Spring Boot.
RedisTemplate offers a high-level abstraction for Redis interactions:
https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/#redis:template
Redis autoconfiguration :
#AutoConfiguration
#ConditionalOnClass({RedisOperations.class})
#EnableConfigurationProperties({RedisProperties.class})
#Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
public RedisAutoConfiguration() {
}
#Bean
#ConditionalOnMissingBean(
name = {"redisTemplate"}
)
#ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
#Bean
#ConditionalOnMissingBean
#ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
return new StringRedisTemplate(redisConnectionFactory);
}
}
Here you can find the corresponding configuration properties(including connection pool default configuration).
Simple implementation example :
https://www.baeldung.com/spring-data-redis-tutorial

Multiple R2DBC datasource with Spring boot

I want to migrate my app to WebFlux, but the tricky part that I have bean which connects to 6 data sources by such mechanism
public class MultiRoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return //code which sets context for chosen db;
}
}
Then I'm creating 6 data sources which is then managed by multiRoutingDataSource
#Bean(name = "multiRoutingDataSource")
public DataSource multiRoutingDataSource() {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(ident, MyDataSourceBean());
MultiRoutingDataSource multiRoutingDataSource = new MultiRoutingDataSource();
multiRoutingDataSource.setTargetDataSources(targetDataSources);
return multiRoutingDataSource;
}
and this data sources could be changed in runtime. This multiRouting then set into entity manager.
Is there something similar with WebFlux?
I found
public class MultiRoutingDataSource extends AbstractRoutingConnectionFactory {
#Override
protected Mono<Object> determineCurrentLookupKey() {
return null;
}
But how to create beans with connections and switch them in runtime like I'm doing in Spring MVC?
If you want multi R2dbc connectionfactories at the same application, check my example multi-r2dbc-connectionfactories.
For multi-tenancy support, check multi-tenancy-r2dbc.

Setting up in-memory H2 database without Spring Boot

I am working in a spring 5 (Not Sprig Boot) project. I need to test my application with in-memory H2 database. I am using Spring with Java Config on maven build tool. Is there any way I can configure in-memory H2 DB?
Usually I use this in my #Config class:
#Bean
public DataSource h2TestDataSource(){
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build();
}
So I use Spring Embedded DB in my spring projects (I don't use spring boot)
I hope it's useful.
You can add the DataSource bean using the EmbeddedDatabaseBuilder as follows:
#Bean
public DataSource dataSource(
#Value("${datasource.dbname}") String dbname,
#Value("${datasource.script}") String script) {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.setName(dbname)
.addScript(script)
.build();
}
application.properties
datasource.dbname=users
datasource.script=classpath:resources/users.sql
Also you can register h2-console servlet in the application configuration class as follows:
#Configuration
public class WebAppConfig implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) {
. . .
servletContext
.addServlet("H2Console", WebServlet.class)
.addMapping("/console/*");
. . .
}
}
Then you can open http://localhost:8080/console and connect to the jdbc:h2:mem:users database as follows:
See also How to enable h2-console in spring-webmvc without spring-boot?

Can't set JPA naming strategy after configuring multiple data sources (Spring 1.4.1 / Hibernate 5.x)

I am using Spring Boot 1.4.1 which uses Hibernate 5.0.11. Initially I configured a data source using application.properties like this:
spring.datasource.uncle.url=jdbc:jtds:sqlserver://hostname:port/db
spring.datasource.uncle.username=user
spring.datasource.uncle.password=password
spring.datasource.uncle.dialect=org.hibernate.dialect.SQLServer2012Dialect
spring.datasource.uncle.driverClassName=net.sourceforge.jtds.jdbc.Driver
I configured it with "uncle" because that will be the name of one of multiple data sources that I'll configure. I configured this data source like this, according to the Spring docs:
#Bean
#Primary
#ConfigurationProperties(prefix = "spring.datasource.uncle")
public DataSource uncleDataSource() {
return DataSourceBuilder.create().build();
}
At this point everything worked fine.
I created an #Entity class without any #Column annotations and let Hibernate figure out the column names, for example if I have a Java property named idBank, Hibernate will automatically assume the column name is id_bank. This is used when generating ddl, running SQL statements, etc. I want to utilize this feature because I'm going to have a lot of entity classes and don't want to have to create and maintain all of the #Column annotations. At this point, this worked fine.
I then added another data source like this:
spring.datasource.aunt.url=jdbc:sybase:Tds:host2:port/db2
spring.datasource.aunt.username=user2
spring.datasource.aunt.password=password2
spring.datasource.aunt.dialect=org.hibernate.dialect.SybaseDialect
spring.datasource.aunt.driverClassName=com.sybase.jdbc4.jdbc.SybDriver
... and also this, following the Spring docs for setting up multiple data sources. Apparently once you define a 2nd data source, it can't configure the default beans and you have to define your own EntityManager and TransactionManager. So in addition to the data source configured above, I added these configurations:
#Bean
#Primary
PlatformTransactionManager uncleTransactionManager(#Qualifier("uncleEntityManagerFactory") final EntityManagerFactory factory) {
return new JpaTransactionManager(factory);
}
#Bean
#Primary
LocalContainerEntityManagerFactoryBean uncleEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(uncleDataSource())
.packages(Uncle.class)
.persistenceUnit("uncle")
.build();
}
#Bean
#ConfigurationProperties(prefix = "spring.datasource.aunt")
public DataSource auntDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
PlatformTransactionManager auntTransactionManager(#Qualifier("auntEntityManagerFactory") final EntityManagerFactory factory) {
return new JpaTransactionManager(factory);
}
#Bean
LocalContainerEntityManagerFactoryBean auntEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(auntDataSource())
.packages(Aunt.class)
.persistenceUnit("aunt")
.build();
}
This works in terms of connecting to the database and trying to fetch data.
HOWEVER (and here's the problem, thanks for reading this far). After these configurations I have lost the implied naming strategy that translates Java column names to snake case names, so now if I have a Java property idBank it incorrectly uses column name idBank instead of id_bank. I would really like to get that functionality back.
There is a JPA property for this spring.jpa.hibernate.naming-strategy, and there are various naming strategy classes in Spring and Hibernate such as org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy. So I tried setting it like this:
spring.jpa.hibernate.naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy
But it did not work. I tried some variations such as:
spring.datasource.uncle.naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy
and
spring.datasource.uncle.hibernate.naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy
but this did not have any effect.
Then I read that in Hibernate 5, the naming strategy was broken up into two parts, "physical" and "implicit" and there are different settings for each. So I tried this, with a few variations:
spring.jpa.hibernate.naming.physical-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
and
spring.jpa.hibernate.naming.implicit-strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
and
spring.datasource.uncle.naming.physical-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
and
spring.datasource.uncle.hibernate.naming.implicit-strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
But none of these worked.
It seems like there should be a way for me to set this configuration in the beans directly, such as on the SessionFactory, but I could not find that API. The documentation around this seems to have some gaps.
I'd really like to avoid setting up a persistence.xml also, which I have not needed up to this point.
So here is where I'm stuck and I'm hoping someone can help out. Really what I would like is a way to debug these property settings, I turned on trace logging in both org.springframework and org.hibernate but there was nothing useful there. I tried stepping through the code when these beans were configured but couldn't find the place where this happens. If anyone has that info and could share it I'd be really grateful.
I had the same problem and fixed it with the following code (adapted to the code in the question - for a single entity manager):
protected Map<String, Object> jpaProperties() {
Map<String, Object> props = new HashMap<>();
props.put("hibernate.physical_naming_strategy", SpringPhysicalNamingStrategy.class.getName());
props.put("hibernate.implicit_naming_strategy", SpringImplicitNamingStrategy.class.getName());
return props;
}
#Primary
#Bean(name = "defaultEntityManager")
public LocalContainerEntityManagerFactoryBean defaultEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(auntDataSource())
.packages(Aunt.class)
.persistenceUnit("aunt")
.properties(jpaProperties())
.build();
}
The same as #ewert answer can be gained using properties:
# this works
spring.jpa.properties.hibernate.implicit_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
spring.jpa.properties.hibernate.physical_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
# but that doesn't work
spring.jpa.hibernate.naming.physical-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
spring.jpa.hibernate.naming.implicit-strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
I think I can explain why the default behaviour disappears, as per your latest question.
As of Spring Boot 2.4.2 the deafult configuration kicks in in this method of JpaBaseConfiguration:
#Bean
#Primary
#ConditionalOnMissingBean({ LocalContainerEntityManagerFactoryBean.class, EntityManagerFactory.class })
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder factoryBuilder) {
Map<String, Object> vendorProperties = getVendorProperties();
customizeVendorProperties(vendorProperties);
return factoryBuilder.dataSource(this.dataSource).packages(getPackagesToScan()).properties(vendorProperties)
.mappingResources(getMappingResources()).jta(isJta()).build();
}
it happens within the customizeVendorProperties method call.
By creating your own LocalContainerEntityManagerFactoryBean bean (two of them actually) this customization is not performed anymore.
If you are using SessionFactory you should use next lines to set naming strategies.
sessionFactory.setImplicitNamingStrategy(SpringImplicitNamingStrategy.INSTANCE);
sessionFactory.setPhysicalNamingStrategy(new SpringPhysicalNamingStrategy());
The only way I get this running properly with Spring-Boot 2+ was setting the following manually:
#Bean(name = "myEmf")
public LocalContainerEntityManagerFactoryBean sapEntityManagerFactory(
EntityManagerFactoryBuilder builder, #Qualifier("myDataSource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("my.custom.package")
.persistenceUnit("myPu")
.properties(getProperties())
.build();
}
public Map<String, String> getProperties() {
val props = new HashMap<String, String>();
if (isTest()) {
props.put("hibernate.hbm2ddl.auto", "create");
} else {
props.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQL95Dialect");
}
return props;
}

Resources