ORA-02396: exceeded maximum idle when several DS and Hikari used - spring-boot

We have several systems, using same core libraries and same Oracle DB. But just one system get some error every day, below is a stacktrace.
The error is: ORA-04042: procedure, function, package, or package body does not exist. The difference between this system and others is, that this system uses several datasources, below you can see the Hikari config, part of build.gradle and stacktrace. All other system uses one datasource.
This is Oracle version info:
BANNER CON_ID
-------------------------------------------------------------------------------- ----------
Oracle Database 12c Release 12.1.0.1.0 - 64bit Production 0
PL/SQL Release 12.1.0.1.0 - Production 0
CORE 12.1.0.1.0 Production 0
TNS for 64-bit Windows: Version 12.1.0.1.0 - Production 0
NLSRTL Version 12.1.0.1.0 - Production 0
This is a code used for config one of the datasources. Second one is prodused in same way:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "sourceEntityManagerFactory",
transactionManagerRef = "sourceTransactionManager",
basePackages = { "com.maxi.jpa.source" }
)
public class SourceConfig
{
#Bean(name = "sourceDS")
#Primary
#ConfigurationProperties("spring.datasource.ds-src")
public DataSource dataSource(){
return DataSourceBuilder.create().build();
}
#Bean(name = "sourceEntityManagerFactory")
#DependsOn("sourceDS")
#Primary
public LocalContainerEntityManagerFactoryBean sourceEMF(
EntityManagerFactoryBuilder builder,
#Qualifier("sourceDS")
DataSource ds
) {
return builder
.dataSource(ds)
.packages("com.maxi.jpa.model")
.persistenceUnit("source")
.build();
}
#Bean(name = "sourceEntityManager")
#Primary
public EntityManager sourceEM(
#Qualifier("sourceEntityManagerFactory")
EntityManagerFactory factory
) {
return factory.createEntityManager();
}
#Bean(name = "sourceTransactionManager")
#Primary
public PlatformTransactionManager transactionManager(
#Qualifier("sourceEntityManagerFactory") EntityManagerFactory
entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
}
Here is the code where the exception is:
private List<String> descTable(String owner, String tableName) {
String query =
"select column_name " +
"from all_tab_columns " +
"where upper(owner) = '%s' " +
"and upper(table_name) = '%s'";
query = String.format(query, owner, tableName);
#SuppressWarnings("unchecked")
List<String> result = (List<String>)sourceEntityManager
.createNativeQuery(query)
.getResultList();
return result;
}
This way, I inject sourceEntityManager:
#Service
public class DMLService
{
private EntityManager sourceEntityManager;
private EntityManager destEntityManager;
public DMLService(
#Qualifier("sourceEntityManager")
EntityManager sourceEntityManager,
#Qualifier("destEntityManager")
EntityManager destEntityManager
) {
this.sourceEntityManager = sourceEntityManager;
this.destEntityManager = destEntityManager;
}
....
}
application.yaml
spring:
datasource:
ds-src:
driverClassName: oracle.jdbc.OracleDriver
jdbcUrl: jdbc:oracle:thin:#${DB4VAL_SRC_DB}
username: ${DB4VAL_SRC_USERNAME}
password: ${DB4VAL_SRC_PASSWORD}
poolName: Db4ValidateSource
connectionTestQuery: SELECT 1 FROM DUAL
ds-dest:
driverClassName: oracle.jdbc.OracleDriver
jdbcUrl: jdbc:oracle:thin:#${DB4VAL_DEST_DB}
username: ${DB4VAL_DEST_USERNAME}
password: ${DB4VAL_DEST_PASSWORD}
poolName: Db4ValidateDestination
connectionTestQuery: SELECT 1 FROM DUAL
jpa:
database-platform: org.hibernate.dialect.Oracle12cDialect
application-default.yaml
spring:
datasource:
ds-src:
minimumIdle: 1
maximumPoolSize: 5
idleTimeout: 20000
maxLifetime: 60000
keepaliveTime: 10000
connectionTimeout: 30000
ds-dest:
minimumIdle: 1
maximumPoolSize: 5
idleTimeout: 20000
maxLifetime: 60000
keepaliveTime: 10000
connectionTimeout: 30000
build.gradle
dependencies {
implementation 'org.apache.commons:commons-lang3'
implementation 'commons-net:commons-net:3.6'
implementation 'commons-io:commons-io:2.6'
implementation 'com.zaxxer:HikariCP:4.0.2'
implementation 'org.springframework:spring-context-support'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
...
}
stacktrace
at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:628)
at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:557)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:730)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:291)
at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:492)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:148)
at oracle.jdbc.driver.T4CPreparedStatement.executeForDescribe(T4CPreparedStatement.java:928)
at oracle.jdbc.driver.OracleStatement.prepareDefineBufferAndExecute(OracleStatement.java:1158)
at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1093)
at oracle.jdbc.driver.OracleStatement.executeSQLSelect(OracleStatement.java:1402)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1285)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3735)
at oracle.jdbc.driver.OraclePreparedStatement.executeQuery(OraclePreparedStatement.java:3847)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeQuery(OraclePreparedStatementWrapper.java:1098)
at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52)
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeQuery(HikariProxyPreparedStatement.java)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:57)
at org.hibernate.loader.Loader.getResultSet(Loader.java:2297)
at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2050)
at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2012)
at org.hibernate.loader.Loader.doQuery(Loader.java:948)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:349)
at org.hibernate.loader.Loader.doList(Loader.java:2843)
at org.hibernate.loader.Loader.doList(Loader.java:2825)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2657)
at org.hibernate.loader.Loader.list(Loader.java:2652)
at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:338)
at org.hibernate.internal.SessionImpl.listCustomQuery(SessionImpl.java:2141)
at org.hibernate.internal.AbstractSharedSessionContract.list(AbstractSharedSessionContract.java:1169)
at org.hibernate.query.internal.NativeQueryImpl.doList(NativeQueryImpl.java:176)
at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1604)
at org.hibernate.query.Query.getResultList(Query.java:165)
at com.maxi.services.DMLService.descTable(DMLService.java:77)
at com.maxi.services.DMLService.getTableData(DMLService.java:191)
at com.maxi.services.DMLService.processRecord(DMLService.java:203)
at com.maxi.services.ValidateService.processRecord(ValidateService.java:263)
at com.maxi.services.ValidateService.runETL(ValidateService.java:362)
at com.maxi.services.Handler.runProcess(Handler.java:54)
at com.maxi.services.Handler.lambda$handleRequest$0(Handler.java:35)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
As you can see, the system catches all the configuration

So, the problem was, that EntityManager retrievs connection from pool, and doesn't returns it, till it will be closed. But we don't able to close it manually, since it is a bean, that we possible will not able to reopen.
So the solution is not to create the EM manuualy, but fetch it from framework, using #PersistenceContext(unitName = "unit_name").
It is necessary to remove this code (for both EMs):
#Bean(name = "sourceEntityManager")
#Primary
public EntityManager sourceEM(
#Qualifier("sourceEntityManagerFactory")
EntityManagerFactory factory
) {
return factory.createEntityManager();
}
#Bean(name = "sourceTransactionManager")
#Primary
public PlatformTransactionManager transactionManager(
#Qualifier("sourceEntityManagerFactory") EntityManagerFactory
entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
And to Autowire the beans in following way:
#PersistenceContext(unitName = "source")
private EntityManager sourceEntityManager;
#PersistenceContext(unitName = "destination")
private EntityManager destEntityManager;
Don't forget to rewrite code for use #Transactions (jpa or spring). Put the annotation over public method or whole class.

Related

jooq DataSourceConnectionProvider with TransactionAwareDataSourceProxy is not taking part in spring transactions

I have the following spring data source setup:
datasource:
name: postgres-datasource
url: ${POSTGRES_URL:jdbc:postgresql://localhost:5432/mydb}?reWriteBatchedInserts=true&prepareThreshold=0
username: ${POSTGRES_USER:mydb}
password: ${POSTGRES_PASS:12345}
driver-class: org.postgresql.Driver
hikari:
minimumIdle: 2
maximum-pool-size: 30
max-lifetime: 500000
idleTimeout: 120000
auto-commit: false
data-source-properties:
cachePrepStmts: true
useServerPrepStmts: true
prepStmtCacheSize: 500
jpa:
database-platform: org.hibernate.dialect.PostgreSQLDialect
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
# generate_statistics: true
order_inserts: true
order_updates: true
jdbc:
lob:
non_contextual_creation: true
batch_size: 50
Notice that auto-commit is false.
Since, I need to use both jooq and JPA and also have multiple schemas in my db, I have configured the following DataSourceConnectionProvider
public class SchemaSettingDataSourceConnectionProvider extends DataSourceConnectionProvider {
public SchemaSettingDataSourceConnectionProvider(TransactionAwareDataSourceProxy dataSource) {
super(dataSource);
}
public Connection acquire() {
try {
String tenant = TenantContext.getTenantId();
log.debug("Setting schema to {}", tenant);
Connection connection = dataSource().getConnection();
Statement statement = connection.createStatement();
statement.executeUpdate("SET SCHEMA '" + tenant + "'");
statement.close();
return connection;
} catch (SQLException var2) {
throw new DataAccessException("Error getting connection from data source " + dataSource(), var2);
}
}
I have the #EnableTransactionManagement on the spring boot config. With this setup in place, the connection is not committing after transaction is over.
#Transactional(propagation = Propagation.REQUIRES_NEW)
public FlowRecord insert(FlowName name, String createdBy) {
return dslContext.insertInto(FLOW, FLOW.NAME, FLOW.STATUS)
.values(name.name(), FlowStatus.CREATED.name())
.returning(FLOW.ID)
.fetch()
.get(0);
}
This does not commit. So, I tried adding the following code to my SchemaSettingDataSourceConnectionProvider class
#Override
public void release(Connection connection) {
connection.commit();
super.release(connection);
}
However, now the issue is that even when a transaction should get rolled back, for e.g due to a runtime exception, it still commits all the time.
Is there some configuration I am missing
UPDATE
Following the answer below, I provided a DataSourceTransactionManager bean and it worked for JOOQ.
public DataSourceTransactionManager jooqTransactionManager(DataSource dataSource) {
// DSTM is a PlatformTransactionManager
return new DataSourceTransactionManager(dataSource);
}
However, now my regular JPA calls are all failing with
Caused by: javax.persistence.TransactionRequiredException: Executing an update/delete query
at org.hibernate.internal.AbstractSharedSessionContract.checkTransactionNeededForUpdateOperation(AbstractSharedSessionContract.java:445)
at org.hibernate.query.internal.AbstractProducedQuery.executeUpdate(AbstractProducedQuery.java:1692)
So, I provided a JpaTransactionManager bean. Now this causes JOOQ Auto Configuration to throw multiple DataSourceTransactionManager beans present exception. After much trial and error, the one that worked for me was this:
private final TransactionAwareDataSourceProxy dataSource;
public DslConfig(DataSource dataSource) {
// A transaction aware datasource is needed, otherwise the spring #Transactional is ignored and updates do not work.
this.dataSource = new TransactionAwareDataSourceProxy(dataSource);
}
#Bean("transactionManager")
public PlatformTransactionManager transactionManager() {
// Needed for jpa transactions to work
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
Notice that I am using a JpaTransactionManager but setting the datasource to be TransactionAwareDataSourceProxy. Further testing required, but looks like both JPA and JOOQ transactions are working now.
One thing to watch out for is to make sure you're using the correct #Transactional annotation. In my project there were two: one from a Jakarta package and one from a Spring package - make sure you're using the Spring annotation.
I don't know if your Spring config is correct.
We use Java config so it's hard to compare.
One obvious difference is that we have an explicit TransactionManager defined, is that something you maybe need to do?

Spring boot Hibernate error "Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set" when working with multiple data sources

I am building a Springboot application that needs to talk to 2 different databases (DB2 and Oracle).
Spring-boot-starter-parent version 2.6.6
hibernate version 5.6.7.Final
I first added DB2 support and that was working fine until I added oracle related settings in application.properties file below.
Adding oracle related settings causes following error:
org.hibernate.HibernateException: Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set
Below are the databases and hibernate related settings from my application.properties:
# ==============================
# = DB2
db2.datasource.jdbc-url=jdbc:db2://SERVER1:PORT/DATABASE-1:currentSchema=SCHEMA;
db2.datasource.username=USER1
db2.datasource.password=PASSWORD1
db2.datasource.driver-class-name=com.ibm.db2.jcc.DB2Driver
db2.jpa.properties.hibernate.dialect=org.hibernate.dialect.DB2390Dialect
# ==============================
# = ORACLE
oracle.datasource.jdbc-url=jdbc:oracle:thin:#SERVER2:PORT/DATABASE-2
oracle.datasource.username=USER2
oracle.datasource.password=PASSWORD2
oracle.jpa.properties.hibernate.dialect=org.hibernate.dialect.Oracle10gDialect
# ==============================
# = JPA / HIBERNATE
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
My pom.xml contains dependencies for oracle and DB2 amongs other required dependencies:
...
<dependency>
<groupId>com.ibm.db2</groupId>
<artifactId>jcc</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<scope>runtime</scope>
</dependency>
...
I have placed my entities, repositories, and data source configurations into different packages as I read is required in this article https://www.javadevjournal.com/spring-boot/multiple-data-sources-with-spring-boot/. My package structure looks like:
project
- dataconfig
- db2
- config
- entity
- repository
- oracle
- config
- entity
- repository
I also added some entities and repositories and my configuration classes.
Here is my DB2Configuration class:
package project.dataconfig.db2.config;
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "db2EntityManagerFactory",
transactionManagerRef = "db2TransactionManager",
basePackages = {
"project.dataconfig.db2.repository"
}
)
public class Db2Configuration {
#Primary
#Bean(name = "db2DataSource")
#ConfigurationProperties(prefix = "db2.datasource")
public DataSource db2DataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "db2EntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder
, #Qualifier("db2DataSource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("project.dataconfig.db2.entity")
.persistenceUnit("db2persistanceunit")
.build();
}
#Primary
#Bean(name = "db2TransactionManager")
public PlatformTransactionManager db2TransactionManager(
#Qualifier("db2EntityManagerFactory") EntityManagerFactory db2EntityManagerFactory) {
return new JpaTransactionManager(db2EntityManagerFactory);
}
}
Here is my OracleConfiguration class:
package project.dataconfig.oracle.config;
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "oracleEntityManagerFactory",
transactionManagerRef = "oracleTransactionManager",
basePackages = {
"project.dataconfig.oracle.repository"
}
)
public class OracleConfiguration {
#Bean(name = "oracleDataSource")
#ConfigurationProperties(prefix = "oracle.datasource")
public DataSource oracleDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "oracleEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder
, #Qualifier("oracleDataSource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("project.dataconfig.oracle.entity")
.persistenceUnit("oraclepersistanceunit")
.build();
}
#Bean(name = "oracleTransactionManager")
public PlatformTransactionManager oracleTransactionManager(
#Qualifier("oracleEntityManagerFactory") EntityManagerFactory oracleEntityManagerFactory) {
return new JpaTransactionManager(oracleEntityManagerFactory);
}
}
Before I added oracle related settings in application.properties file, my application worked just find with only DB2 settings as described above.
Once I added oracle related settings and configuration, I started getting this error:
org.hibernate.HibernateException: Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set
Based on the error, I think the problem is caused by my settings for Hibernate dialect in application.properties file. I think it is caused by one of the two settings
...
db2.jpa.properties.hibernate.dialect=org.hibernate.dialect.DB2390Dialect
...
oracle.jpa.properties.hibernate.dialect=org.hibernate.dialect.Oracle10gDialect
How do I resolve this issue?
I figure it out.
Modify method entityManagerFactory for both Db2Configuration and OracleConfiguration to supply them with the information about hibernate dialect:
for DB2
#Primary
#Bean(name = "db2EntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder
, #Qualifier("db2DataSource") DataSource dataSource) {
final HashMap<String, Object> hibernateProperties = new HashMap<String, Object>();
hibernateProperties.put("hibernate.dialect", "org.hibernate.dialect.DB2390Dialect");
return builder
.dataSource(dataSource)
.packages("project.dataconfig.db2.entity")
.properties(hibernateProperties)
.persistenceUnit("db2persistanceunit")
.build();
}
for Oracle
#Bean(name = "oracleEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder
, #Qualifier("oracleDataSource") DataSource dataSource) {
final HashMap<String, Object> hibernateProperties = new HashMap<String, Object>();
hibernateProperties.put("hibernate.dialect", "org.hibernate.dialect.Oracle10gDialect");
return builder
.dataSource(dataSource)
.packages("project.dataconfig.oracle.entity")
.properties(hibernateProperties)
.persistenceUnit("oraclepersistanceunit")
.build();
}
After this, my console shows when running app, indicating all is good:
HHH000400: Using dialect: org.hibernate.dialect.DB2390Dialect
Initialized JPA EntityManagerFactory for persistence unit 'db2persistanceunit'
HHH000204: Processing PersistenceUnitInfo [name: oraclepersistanceunit]
HHH000400: Using dialect: org.hibernate.dialect.Oracle10gDialect
Initialized JPA EntityManagerFactory for persistence unit 'oraclepersistanceunit'
My actuator /health endpoint also sees both databases as up and running.

Micronaut - Configuring Oracle UCP Multiple Data Sources And jdbcOperations

I am developing my first micronaut application and i'm having a problem configuring oracle multiple datasources with ucp.
I'm following the oficial tutorial (https://micronaut-projects.github.io/micronaut-sql/latest/guide/) by when I try to perform a select, I get a error:
io.micronaut.transaction.jdbc.exceptions.CannotGetJdbcConnectionException: No current JDBC Connection found. Consider wrapping this call in transactional boundaries.
I check the DataSourceFactory and the PoolDataSource is set correctly
What am I missing?
Thanks!
micronaut:
application:
name: myapp
datasources:
first:
url: url
connectionFactoryClassName: oracle.jdbc.pool.OracleDataSource
username: user
password: password
minPoolSize: 1
maxPoolSize: 10
second:
url: url
connectionFactoryClassName: oracle.jdbc.pool.OracleDataSource
username: user
password: password
minPoolSize: 1
maxPoolSize: 10
#JdbcRepository(dialect = Dialect.ORACLE)
public abstract class MyRepository {
#Inject
#Named("first")
protected final DataSource dataSource;
protected final JdbcOperations jdbcOperations;
public MyRepository(JdbcOperations jdbcOperations, DataSource dataSource) {
this.dataSource = dataSource;
this.jdbcOperations = jdbcOperations;
}
}
#Singleton
public class MyDao extends MyRepository {
public MyDao(JdbcOperations jdbcOperations, DataSource dataSource) {
super(jdbcOperations, dataSource);
}
#Transactional
public Long find() {
String sql = "select * from table where id = 1";
return this.jdbcOperations.prepareStatement(sql, st -> {
return 1L;
});
}
The first datasource needs to be called 'default'.
Also when using a transaction with the non-default datasource, you need to use #TransactionAdvice with the datasource name, e.g. #TransactionalAdvice("Second").

hibernate session initialization very slow in production with many entities definition

I have a Spring Boot app with 296 persisted objects annotated by #Entity using Hibernate framework for persitence implementation. In developpement using eclipse on a Core i7-6700 (4x 3.4Ghz - 4Ghz Turbo, SSD), the SessionFactory is initialized in almost 6 seconds.
My production server is a virtualized windows server on a Xeon E5-2620v2 (6x 2,1GHz - 2.6Ghz, HDD (+ SSD cache)). I have no control about allocated resources by virtualization.
When I run my app on this server, it take about 25 seconds to instantiate the SessionFactory.
No speed problem when app is fully initialized: the execution speed of all webservices/databases queries is normal.
Naively I think hibernate create SQL Queries of basical CRUD operations when SessionFactory is instantiated.
I tried adding #DynamicInsert and #DynamicUpdate on all #Entity classes but it did not bring to me a better startup time and SessionFactory instantiation time is the same.
I use the following spring / hibernate / others pertinent libraries versions: (from my pom.xml)
<hibernate.version>5.4.5.Final</hibernate.version>
<spring.version>5.1.9.RELEASE</spring.version>
<springSecurity.version>5.1.6.RELEASE</springSecurity.version>
<springBoot.version>2.1.8.RELEASE</springBoot.version>
<mssql-jdbc.version>7.4.1.jre11</mssql-jdbc.version>
<apache-commons-dbcp2.version>2.5.0</apache-commons-dbcp2.version>
<eh-cache.version>2.10.6</eh-cache.version>
<javaassist.version>3.25.0-GA</javaassist.version>
This is my hibernate SessionFactory configuration (part of application.mssql.properties file):
hibernate.dialect = org.hibernate.dialect.SQLServer2012Dialect
# useless cause I use GenerationType.IDENTITY
# for primary key (id) of all entities
hibernate.jdbc.batch_size = 20
hibernate.cache.use_query_cache = false
hibernate.cache.use_second_level_cache = true
hibernate.cache.provider_class = org.hibernate.cache.EhCacheProvider
hibernate.cache.region.factory_class = org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory
hibernate.connection.driver_class = com.microsoft.sqlserver.jdbc.SQLServerDriver
hibernate.enable_lazy_load_no_trans = true
hibernate.cache.use_reference_entries = true
hibernate.connection.url = jdbc:sqlserver://localhost:1433;databaseName=master;user=usr;password=pwd;
hibernate.connection.username = usr
hibernate.connection.password = pwd
# Multitenancy configuration
hibernate.multiTenancy = SCHEMA
hibernate.multi_tenant_connection_provider = MultiTenantConnectionProvider
hibernate.tenant_identifier_resolver = CurrentTenantIdentifierResolver
That is how I define my hibernate SessionFactory (I know that I can use standard JPA session factory but this app was made like that and I don't want to change that, moreover, I have simplified class content for more readeability):
#SpringBootApplication()
#EnableAutoConfiguration(
excludeName = {
"org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration"
}
)
#ComponentScan(
basePackages = {
// my packages list
}
)
#Configuration
#PropertySource({ "classpath:application.mssql.properties" })
#EnableCaching
#EnableTransactionManagement
public class ApplicationConfiguration {
#Autowired
private Environment env;
/**
* #return SQL-Server pooled connection using apache dbcp2
*/
#Primary
#Bean
public DataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setUrl(this.env.get("hibernate.connection.url"));
ds.setUsername(this.env.get("hibernate.connection.username"));
ds.setPassword(this.env.get("hibernate.connection.password"));
ds.setDriverClassName(this.env.get("hibernate.connection.driver_class"));
ds.setMaxIdle(10);
ds.setMaxOpenPreparedStatements(100);
return ds;
}
/**
* #return main sessionFactory (the mostly used sessionFactory in app)
*/
#Primary
#Autowired
#Bean
public LocalSessionFactoryBean sessionFactory(#Qualifier("dataSource") DataSource dataSource,
MultiTenantConnectionProvider multiTenantConnectionProviderImpl,
CurrentTenantIdentifierResolver currentTenantIdentifierResolverImpl) {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setPackagesToScan("my.root.pojo.package");
// get hibernate properties from this.env (application.mssql.properties file)
Properties hibernateProperties = getHibernateProperties();
hibernateProperties.put(AvailableSettings.MULTI_TENANT, "SCHEMA");
hibernateProperties.put(AvailableSettings.MULTI_TENANT_CONNECTION_PROVIDER, multiTenantConnectionProviderImpl);
hibernateProperties.put(AvailableSettings.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolverImpl);
sessionFactory.setHibernateProperties(hibernateProperties);
sessionFactory.setDataSource(dataSource);
return sessionFactory;
// FROM THIS POINT --->
}
/**
* #return transactionManager for main sessionFactory
*/
#Bean
#Autowired
#Primary
public HibernateTransactionManager transactionManager(#Qualifier("sessionFactory") SessionFactory sessionFactory) {
// ----> TO THIS POINT took 6 sec in development, 25 sec in production
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setNestedTransactionAllowed(true);
txManager.setSessionFactory(sessionFactory);
return txManager;
}
// some other stuff:
// - sessionFactory on another databases,
// - getHibernateProperties() implementation,
// - initServer() method annotated by #Bean(initMethod = "init")
// - etc ...
}
Is it normal to have this speed difference?
Did I forgotten something in hibernate configuration ?
Any help will be welcomed.

Springboot 2.0 JUnit test #Tranactional rollback doesn't works

I migrated some projects from Springboot 1.5.10 to 2.0.
Springboot 2.0.3Release, JDK10, mysql, hikari-cp
After this work, in JUnit test, all data in test cases remains at database. I think it doesn't works #Tranactional - org.springframework.transaction.annotation.Transactional
Here is part of application.yml and test class.
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
jpa:
show-sql: true
hibernate:
ddl-auto: none
database: mysql
database-platform: org.hibernate.dialect.MySQL5Dialect
Here is datasource.
#Configuration
#EnableTransactionManagement
public class DatasourceJPAConfig {
#Bean
#Primary
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
}
Here is part of JUnit test class.
#Transactional
#Rollback(true)
#RunWith(SpringRunner.class)
#ActiveProfiles("local")
#SpringBootTest
public class RepoTests {
#Autowired
private TestRepository testRepository;
#Test
public void saveTest() {
var name = "test";
var description = "test description"
var item = TestDomain.builder()
.name(name)
.description(description)
.build();
testRepository.save(item);
var optional = testRepository.findById(item.getId());
assertTrue(optional.isPresent());
assertEquals(optional.get().getDescription(), description);
assertEquals(optional.get().getName(), name);
}
}
after to run saveTest method, increase 1 row at database.
Add datasource to test and set auto commit to false
#Autowired
private DataSource dataSource;
And inside test
((HikariDataSource)dataSource).setAutoCommit(false);

Resources