I would like to use a custom connection pool with MyBatis and the following questions have arisen:
What connection pool implementation does MyBatis use?
How can I change the default connection pool by HikariCP or BoneCP?
you can use org.mybatis.spring
#Bean
public SqlSessionFactoryBean mysqlSessionFactoryBean(#Autowired #Qualifier("mysqlDataSource") DataSource source) throws IOException {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setConfigLocation(new ClassPathResource("/mybatis-config.xml"));
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mysqlmapper/**/*Mapper.xml"));
bean.setDataSource(source);
return bean;
}
#Bean
public DataSource mysqlDataSource() {
return DataSourceBuilder.create()
.driverClassName("com.mysql.jdbc.Driver")
.url(mysqlUrl)
.username(mysqlUser)
.password(mysqlPassword)
.type(HikariDataSource.class)
.build();
}
To use hikaricp connection pool, set
sql.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
as sql datasource driver in application.properties
Related
I am New to SpringBoot.
I have a Spring Boot Application and the Database I am using is PostgreSQL, I am using JdbcTemplate and I have 2 datasource connections.
The code works fine,but I observed that in PostgreSQL pgAdmin in the Server Status Dashboard,it shows the connection pool(Say 10 connection) in idle mode.
While earlier I was working with single datasource, I observed the same thing,but I solved it by setting some properties within application.properties file.
For.e.g:
spring.datasource.hikari.minimum-idle=somevalue
spring.datasource.hikari.idle-timeout=somevalue
How do I achieve the same with multiple Datasources.
properties file
spring.datasource.jdbcUrl=jdbc:postgresql://localhost:5432/stsdemo
spring.datasource.username=postgres
spring.datasource.password=********
spring.datasource.driver-class-name=org.postgresql.Driver
spring.seconddatasource.jdbcUrl=jdbc:postgresql://localhost:5432/postgres
spring.seconddatasource.username=postgres
spring.seconddatasource.password=********
spring.seconddatasource.driver-class-name=org.postgresql.Driver
DbConfig
#Configuration
public class DbConfig {
#Bean(name="db1")
#Primary
#ConfigurationProperties(prefix="spring.datasource")
public DataSource firstDatasource()
{
return DataSourceBuilder.create().build();
}
#Bean(name = "jdbcTemplate1")
public JdbcTemplate jdbcTemplate1(#Qualifier("db1") DataSource ds) {
return new JdbcTemplate(ds);
}
#Bean(name="db2")
#ConfigurationProperties(prefix="spring.seconddatasource")
public DataSource secondDatasource()
{
return DataSourceBuilder.create().build();
}
#Bean(name="jdbcTemplate2")
public JdbcTemplate jdbcTemplate2(#Qualifier("db2") DataSource ds)
{
return new JdbcTemplate(ds);
}
}
That's to be expected.
"closing" a connection (i.e. calling close()) that is obtained from am pool will only return it to the pool. The pool will not immediately close the physical connection to the database to avoid costly reconnects (which is the whole point of using a connection pool)
An "idle" connection is also no real problem in Postgres.
If you have connections that stay "idle in transaction" for a long time - that would be a problem.
I'm working on a big project written with java8 and SringBoot 2.2.6. The project uses Envers and, the girl builds the architecture say to me that she doesn't manage to put in the application.properties the Envers configuration. Than she do as follows:
#Configuration
public class JPAConfig {
#Autowired
private DataSource dataSource;
#Bean(name="entityManagerFactory")
public LocalSessionFactoryBean sessionFactory() throws IOException {
LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();
factoryBean.setHibernateProperties(getHibernateProperties());
factoryBean.setDataSource(dataSource);
factoryBean.setPackagesToScan("it.xxxx.xxxxx.xxxxx.common.model");
return factoryBean;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
private Properties getHibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", PostgreSQL82Dialect.class.getName());
properties.put("hibernate.default_schema", "test");
properties.put("hibernate.listeners.envers.autoRegister", true);
properties.put("org.hibernate.envers.revision_field_name", "rev");
properties.put("org.hibernate.envers.revision_type_field_name", "rev_type");
properties.put("org.hibernate.envers.audit_table_prefix", "aud_");
properties.put("org.hibernate.envers.store_data_at_delete", true);
properties.put("org.hibernate.envers.audit_table_suffix", "");
return properties;
}
}
Problem is that without dataSource class name I can't start my #SpringBootTest classes and I don't know how to add it in a scenario like this (without change the configuration I mean).
I also tries to add this row inside the application.properties:
spring.profiles.active=#spring.profile#
spring.datasource.driver-class-name=org.postgresql.Driver
#JPA
spring.datasource.jndi-name=jdbc/test
But doesn't work at all..
If I run the App with JUnit I obtain this error:
org.springframework.jdbc.datasource.lookup.DataSourceLookupFailureException: Failed to look up JNDI DataSource with name 'jdbc/test'; nested exception is javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
Can you help me??
Thanks a lot
You need to register your Datasource as JNDI resource in the spring-boot embedded tomcat.
You can add it as test scope configuration.
This answer shows how to register a JNDI resource: https://stackoverflow.com/a/26005740/5230585
I have a simple jax-rs REST-service that is deployed as a WAR on a wildfly server and uses a JNDI lookup for a datasource configured in the standalone.xml. For this the path is read from a datasource.properties file. The service then performas database actions through this datasource.
Now I want to use this REST-service in a SpringBoot application which is deployed to an embedded tomcat. My implementation uses RESTEasy and the service can easily be integrated with the resteasy-spring-boot-starter. But the JNDI lookup doesn't work, because of course the datasource is now not configured in a standalone.xml, but in the application.properties file. It is a completely different datasource.
I'm looking for a solution to set the datasource without having to "hard code" it. This is how the connection is retrieved currently in the WAR for the wildfly:
private Connection getConnection() {
Connection connection = null;
try (InputStream config = OutboxRestServiceJbossImpl.class.getClassLoader().getResourceAsStream("application.properties")) {
Properties properties = new Properties();
properties.load(config);
DataSource ds = (DataSource) new InitialContext().lookup(properties.getProperty("datasource"));
connection = ds.getConnection();
} catch (Exception e) {
}
return connection;
}
Currently I solved this by having a core module which actually performs the logic and 2 implementations with jax-rs for wildfly and SpringMVC in SpringBoot. They invoke the methods of an instance of the core module and the the connection is handed over to these methods. This looks like this for wildfly:
public String getHelloWorld() {
RestServiceCoreImpl rsc = new RestServiceCoreImpl();
try (Connection connection = getConnection()) {
String helloWorld = rsc.getHelloWorld(connection);
} catch (Exception e) {
}
return helloWorld;
}
public String getHelloWorld(Connection connection){
//database stuff, eg. connection.execute(SQL);
}
And like this in SpringBoot:
#Autowired
RestServiceCoreImpl rsc;
#Autowired
DataSource restServiceDataSource;
#Override
public String getHelloWorld() {
try (Connection connection = restServiceDataSource.getConnection()){
return rsc.getHelloWorld(connection);
} catch (SQLException e) {
}
return null;
}
Is there any way to solve this datasource issue? I need the SpringMVC solution to be replaced with the jax-rs solution within SpringBoot.
Okay, I was able to solve this myself. Here is my solution:
I enabled the naming in the embedded tomcat server as follows:
#Bean
public TomcatServletWebServerFactory tomcatFactory() {
return new TomcatServletWebServerFactory() {
#Override
protected TomcatWebServer getTomcatWebServer(org.apache.catalina.startup.Tomcat tomcat) {
tomcat.enableNaming();
return super.getTomcatWebServer(tomcat);
}
Then I was able to add the JNDI ressource in the server context. Now a JNDI lookup is possible.
In spring boot if we want to connect to mongodb, we can create a configuration file for mongodb or writing datasource in application.properties
I am following the second way
For me, I am gettint this error
"Timeout while receiving message; nested exception is com.mongodb.MongoSocketReadTimeoutException: Timeout while receiving message
.
spring.data.mongodb.uri = mongodb://mongodb0.example.com:27017/admin
I am gettint this error If I am not using my app for 6/7 hours and after that If I try to hit any controller to retrieve data from Mongodb. After 1/2 try I am able to get
Question - Is it the normal behavior of mongodb?
So, in my case it is closing the socket after some particular hours
I read some blogs where it was written you can give socket-keep-alive, so the connection pool will not close
In spring boot mongodb connection, we can pass options in uri like
spring.data.mongodb.uri = mongodb://mongodb0.example.com:27017/admin/?replicaSet=test&connectTimeoutMS=300000
So, I want to give socket-keep-alive options for spring.data.mongodb.uri like replicaset here.
I searched the official site, but can't able to find any
You can achieve this by providing a MongoClientOptions bean. Spring Data's MongoAutoConfiguration will pick this MongoClientOptions bean up and use it further on:
#Bean
public MongoClientOptions mongoClientOptions() {
return MongoClientOptions.builder()
.socketKeepAlive(true)
.build();
}
Also note that the socket-keep-alive option is deprecated (and defaulted to true) since mongo-driver version 3.5 (used by spring-data since version 2.0.0 of spring-data-mongodb)
You can achieve to pass this option using MongoClientOptionsFactoryBean.
public MongoClientOptions mongoClientOptions() {
try {
final MongoClientOptionsFactoryBean bean = new MongoClientOptionsFactoryBean();
bean.setSocketKeepAlive(true);
bean.afterPropertiesSet();
return bean.getObject();
} catch (final Exception e) {
throw new BeanCreationException(e.getMessage(), e);
}
}
Here an example of this configuration by extending AbstractMongoConfiguration:
#Configuration
public class DataportalApplicationConfig extends AbstractMongoConfiguration {
//#Value: inject property values into components
#Value("${spring.data.mongodb.uri}")
private String uri;
#Value("${spring.data.mongodb.database}")
private String database;
/**
* Configure the MongoClient with the uri
*
* #return MongoClient.class
*/
#Override
public MongoClient mongoClient() {
return new MongoClient(new MongoClientURI(uri,mongoClientOptions().builder()));
}
There are security reasons that I cannot add my MBeans to the existing JBoss 7 platform MBeanServer. So I create my own mBeanServer and JMXConnectorServer with a customAuthenticator.
Here are my Spring Bean definition for new MBeanServer and JMXConnectorServer. This code works when I run my application in Jetty. I was able to connect via URL service:jmx:rmi://localhost/jndi/rmi://localhost:17999/sample in jconsole and it only shows the custom MBeans which is what I expect.
But the same code does not work in JBoss 7. When I deploy to JBoss and try to connect with the same JMX URL, it gives a dialog with this error: "The connection to myuser#service:jmx:rmi://localhost/jndi/rmi://localhost:17999/trm did not succeed. Would you like to try again?"
I put a break point in my customAuthenticator and JBoss doesn't stop at my break point when I attempt to connect JMX. It seems my JMXConnectorServer is not being used by JBoss. Can anyone help? Note that I cannot change the existing JBoss MBeanServer or JMX Connector Server configuration because they are used for other purpose.
Thanks in advance.
#Bean
public Object rmiRegistry() throws Exception {
RmiRegistryFactoryBean factory = new RmiRegistryFactoryBean();
factory.setPort(17999);
factory.afterPropertiesSet();
return factory.getObject();
}
#Bean
#DependsOn("rmiRegistry")
public MBeanServer mBeanServer() {
MBeanServerFactoryBean factory = new MBeanServerFactoryBean();
factory.afterPropertiesSet();
return factory.getObject();
}
#Bean
#DependsOn("rmiRegistry")
public JMXConnectorServer jmxConnectorServer() throws IOException, JMException {
ConnectorServerFactoryBean factory = new ConnectorServerFactoryBean();
factory.setServer(mBeanServer());
factory.setServiceUrl("service:jmx:rmi://localhost/jndi/rmi://localhost:17999/sample");
factory.setRegistrationPolicy(RegistrationPolicy.FAIL_ON_EXISTING);
Map<String, Object> props = new HashMap<>();
props.put(JMXConnectorServer.AUTHENTICATOR, customAuthenticator);
factory.setEnvironmentMap(props);
factory.afterPropertiesSet();
return factory.getObject();
}
#Bean
#DependsOn("rmiRegistry")
public AnnotationMBeanExporter annotationMBeanExporter() {
AnnotationMBeanExporter result = null;
result = new AnnotationMBeanExporter();
result.setServer(mBeanServer());
return result;
}
I suspect the JBoss environment is influencing how the JMX Connector server is configured. I would try taking the extra step of specifying the service listening port (e.g. 17998) rather than leaving it as an ephemeral by using this JMXServiceURL:
service:jmx:rmi://localhost:17998/jndi/rmi://localhost:17999/sample