Why Hibernate creates new database connection every request? - java

I have Java - Spring Boot - Hibernate - Postgres application. And hibernate creates database connection every request, why? Is there configurable? For example, for one session to last 10 minutes?
My Hibernate configuration:
#Autowired
private Environment environment;
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "monitoring" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.postgresql.Driver");
dataSource.setUrl(environment.getRequiredProperty("spring.datasource.url"));
dataSource.setUsername(environment.getRequiredProperty("spring.datasource.username"));
dataSource.setPassword(environment.getRequiredProperty("spring.datasource.password"));
return dataSource;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", environment.getRequiredProperty("spring.jpa.properties.hibernate.dialect"));
properties.put("hibernate.show_sql", environment.getRequiredProperty("spring.jpa.hibernate.show-sql"));
properties.put("hibernate.format_sql", "false");
properties.put("hibernate.jdbc.lob.non_contextual_creation", "true");
return properties;
}
And every request I'm getting following log:
11:19:13.584 [http-nio-8080-exec-2] DEBUG o.s.j.d.DriverManagerDataSource - Creating new JDBC DriverManager Connection to [jdbc:postgresql://localhost:5432/monit]
Why? How can I change it?

You need to configure connection pools manually since you define a DataSource.
From Spring official documentation :
If you define your own DataSource bean, auto-configuration does not occur.
Starting from spring boot 2, HikariCP is the default Connection Pool embedded with spring boot starter (spring-boot-starter-jdbc and spring-boot-starter-data-jpa).
You can configure the maximum pool size with the following configuration with HikariCP
spring.datasource.hikari.maximum-pool-size= 10

Related

Prevent changing schema in Spring Boot with two DBs

I have two DBs in my Spring Boot app, one configured with appliation.properties:
spring.secondDatasource.url=jdbc:mysql://10.10.10:3306/db1
spring.secondDatasource.username=user
spring.secondDatasource.password=pass
spring.jpa.database-platform = org.hibernate.dialect.MySQL5Dialect
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.jpa.hibernate.ddl-auto = update
and another via DataSource:
#Configuration
public class SecondDbConnectionConfig {
#Bean
public DriverManagerDataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl("jdbc:mysql://20.20.20:3306/db2");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUsername("user");
dataSource.setPassword("pass");
return dataSource;
}
}
When I run the app, both schemas are updated with my domain model.
I want to update a domain model only for app.prop configured DB.
As to DataSource configured DB, I don't want to make any changes, just read.
How to fix this config?
Don't use DriverManagerDataSource for production. It's just suitable for your testing environment. (It does not have connection pool, just create new connection on the fly)
Your datasource uses your JpaProperties config from your spring.jpa.* properties. So you need to override it:
#Bean(name = "your-entity-manager-factory")
public LocalContainerEntityManagerFactoryBean getEntityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("testdatasource") DataSource dataSource, // Here you must annotate your DriverManagerDataSource bean with #Bean("testdatasource")
JpaProperties jpaProperties) {
// Here you clone and modify JpaProperties
Map<String, String> hibernateConfig = jpaProperties.getHibernateProperties(dataSource);
hibernateConfig.remove("hibernate.hbm2ddl.auto");
return builder
.dataSource(dataSource)
.persistenceUnit("testPu")
.properties(hibernateConfig)
.build();
}
You may config additional transaction manager.

Spring Boot Batch With Spring Data Write meta data in different Schema (in memory: HSQL or H2)

I am writring a batch using the following thecnologies:
Spring Boot to run the application : V1.5.3.RELEASE
Spring Batch with Spring Batch Config: spring-batch-infrastructure V3.0.7.RELEASE
Spring Data for my generic DAO to the business database: >spring-data-jpa V1.11.3.RELEASE
My datasource to oracle datbase is HikariDataSource :
#Qualifier("dataSource")
#Bean(destroyMethod = "close")
#Primary
public HikariDataSource dataSource() throws SQLException {
return buildDataSource();
}
public HikariDataSource buildDataSource() throws SQLException {
HikariDataSource ds = new HikariDataSource();
ds.setMaximumPoolSize(poolSize);
ds.setDriverClassName(driverClassName);
ds.setJdbcUrl(jdbcUrl);
ds.setUsername(userName);
ds.setPassword(password);
ds.setConnectionTestQuery("SELECT 1 from DUAL");
ds.addDataSourceProperty("hibernate.show_sql", showSQL);
ds.addDataSourceProperty("hibernate.use_sql_comments", useSQLComment);
ds.addDataSourceProperty("hibernate.format_sql", formatSQL);
ds.addDataSourceProperty("hibernate.ddl-auto", "none");
return ds;
}
I want to write my meta data in another database (in memory HSQL or H2 for example) but i can't find a way because the context is writing the meta data in the same database.
The only way is to define a TransactionManager and an EntityManager and enable them to my DAO :
#Bean
PlatformTransactionManager businessTransactionManager() throws SQLException {
return new JpaTransactionManager(businessEntityManagerFactory().getObject());
}
#Bean
LocalContainerEntityManagerFactoryBean businessEntityManagerFactory() throws SQLException {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource());
factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
factoryBean.setPackagesToScan("package.of.business.model", "package.of.business.data");
return factoryBean;
}
and in my batch configuration i add :
#EnableJpaRepositories(entityManagerFactoryRef = "businessEntityManagerFactory",
transactionManagerRef = "businessTransactionManager", basePackages = {"package.of.business.model",
"package.of.business.data"})
This way it works after i define the spring default dataSource in my app.properties:
spring.datasource.url=jdbc:h2:mem:test
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=update
What i really want to do is the exact opposite of this, i want that the default database is the business one and i want to override the datasource that writes the meta data but i can't find a way. I even tried to make a custom BatchConfigurer:
CustomBatchConfigurer extends DefaultBatchConfigurer
It works only for my meta data after i disable the initialization of my spring data for the default datasource but it doesn't write anything in my oracle business database :
batch.data.source.init=false
spring.batch.initializer.enabled=false
spring.batch.initialize.enabled=false
spring.datasource.initialize=false
spring.datasource.continue-on-error=true
Does any one have any idea how this could be done?
You'll need to create a custom implementation of the BatchConfigurer (typically by extending DefaultbatchConfigurer. That will allow you to configure the batch DataSource explicitly.
You can read more about the BatchConfigurer in the documentation here: http://docs.spring.io/spring-batch/apidocs/org/springframework/batch/core/configuration/annotation/BatchConfigurer.html

How to generate db schema from entities using annotation configuration?

I have bunch of java POJO classes. I previously generated schema with XML configuration but now I am using annotated and it doesn't work.
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, Environment env) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactoryBean.setPackagesToScan("com.library.entities");
entityManagerFactoryBean.setJpaProperties(hibernateProperties());
return entityManagerFactoryBean;
}
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost/library");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
final Properties hibernateProperties() {
final Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("spring.jpa.database", "MYSQL");
hibernateProperties.setProperty("spring.jpa.show-sql", "true");
hibernateProperties.setProperty("spring.jpa.hibernate.ddl", "create");
return hibernateProperties;
}
My properties are set up in entityManagerFactory. The logger info that can be helpful:
LocalContainerEntityManagerFactoryBean:462 - Closing JPA EntityManagerFactory for persistence unit 'default'
I don't have hibernate.properties file. Is this file necessary in Annotated configuration?
INFO Environment:239 - HHH000206: hibernate.properties not found
Your keys for properties are wrong.
Keys like spring.jpa.* are for Spring Boot when you are defining configs in application.properties file.
Take a look at the AvailableSettings interface to see available and valid keys.
So the keys must be:
hibernateProperties.setProperty("hibernate.show_sql", "true");
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", "create");
OR
hibernateProperties.setProperty(AvailableSettings.SHOW_SQL, "true");
hibernateProperties.setProperty(AvailableSettings.HBM2DDL_AUTO, "create");

Connect to mySQL via GlassFish connection pool

I'm having this error while trying to connect to mySQL database:
No object bound to name java:comp/env/jdbc/mySql
This is my configuration class:
#Configuration
#EnableTransactionManagement
#ComponentScan({ "org.onmyown.config" })
#PropertySource(value = { "classpath:application.properties" })
public class HibernateConfiguration {
#Autowired
private Environment environment;
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "org.onmyown" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public DataSource dataSource() {
DataSource dataSource = null;
try {
Context initialContext = new InitialContext();
Context environmentContext = (Context)initialContext.lookup("java:comp/env");
dataSource = (DataSource) environmentContext.lookup("jdbc/mySql");
} catch (NamingException e) {
e.printStackTrace();
}
return dataSource;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
properties.put("hibernate.hbm2ddl.auto", environment.getRequiredProperty("hibernate.hbm2ddl.auto"));
return properties;
}
#Bean
#Autowired
public HibernateTransactionManager transactionManager(SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
}
I can ping from GlassFish just fine. I put all the properties I need (I think). My friend did this configuration with Derby database and it works. Is there any difference?
#edit
I 'fixed' something. I added web.xml, everything is here:
https://github.com/afterlook/SpringMVC
The problem now is that the application doesn't really care that i put mySql as a data source. It searches for DerbyPool which is one of the defaults in GlassFish. Any idea why?
The problem you posted originaly tries to use a datasource that is looked up using JNDI with the key comp/env/jdbc/mySql and this is not configured.
If you want to use a connection pool of Glasfish, you have to configure Glasfish so it creates that pool and publishes it as a JNDI component with the key comp/env/jdbc/mySql
mysql glasfish shows how this is done.
With your fix you commented the JNDI code out, instead you set the connection url from a property.
db.url=jdbc:mysql://localhost:3306/app
without additional configuration, this seems to point to the Glasfish default connection pool using the derby db.
If you want to use the Glasfish connection pool :
Configure Glasfish to crate that pool using mysql as described in the linked document.
You will configure url,user,password for the Glsfish connection pool, so I think you dont have to provide it in the spring config again. Take the datasource as you get it from the JNDI lookup.
Revert your fix that commented out the JNDI lookup.
Your code should look like this :
#Bean
public DataSource dataSource() {
DataSource dataSource = null;
try {
Context initialContext = new InitialContext();
Context environmentContext = (Context)initialContext.lookup("java:comp/env");
dataSource = (DataSource) environmentContext.lookup("jdbc/mySql");
} catch (NamingException e) {
e.printStackTrace();
}*/
return dataSource;
}

Set up JMX support using LocalSessionFactoryBean

There is java configuration bean with Hibernate JMX Statistics Service and LocalSessionFactoryBean configurations. I don't see any possibility to get SessionFactory through already instantiated LocalSessionFactoryBean. The goal is to enable JMX support so JConsole would be able to access Hibernates statistics. If I create new SessionFactory it will be duplicate. How to proceed with this configuration?
#Bean
public LocalSessionFactoryBean sessionFactory(){
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource());
sessionFactoryBean.setPackagesToScan(env.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));
sessionFactoryBean.setHibernateProperties(hibProperties());
// JMX statistics
SessionFactory sf = ...; // ???
StatisticsService statsMBean = new StatisticsService();
statsMBean.setSessionFactory(sessionFactoryBean.);
statsMBean.setStatisticsEnabled(true);
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
mBeanServer.registerMBean(statsMBean, new ObjectName("Hibernate:application=Statistics"));
return sessionFactoryBean;
}
JConsole
Write a new #Bean method to expose the StatisticsService
#Autowired
#Bean
public StatisticsService service(SessionFactory sessionFactory) {
StatisticsService statsMBean = new StatisticsService();
statsMBean.setSessionFactory(sessionFactory);
statsMBean.setStatisticsEnabled(true);
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
mBeanServer.registerMBean(statsMBean, new ObjectName("Hibernate:application=Statistics"));
return statsMBean;
}
Alternatively, you can invoke afterPropertiesSet and getObject on sessionFactoryBean to get the SessionFactory instance. Note that you will have to check if getObject returns the same object on future invocations. You don't want it to return one instance for use with your MBean and another instance for the rest of your app.

Categories

Resources