Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting hibernate dialect dynamically

I have implemented Hibernate's multitenant database architecture, where a specific database connection is chosen depending on the tenant. I'm using Spring 4.3 and Hibernate 5.2.

All good when tenants are using the same RDBMS, but when they are different, I have to change the dialect setting in hibernate properties dynamically which I don't know how.

My hibernate properties are in dispatcher-servlet.xml:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
         http://www.springframework.org/schema/tx  http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.example"/>
    <mvc:annotation-driven/>
    <context:property-placeholder location="classpath:application.properties"/>
    <tx:annotation-driven transaction-manager="transactionManager"/>

    <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean" >
        <property name="packagesToScan">
            <list>
                <value>com.example.model</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <!--<prop key="hibernate.dialect">${hibernate.dialect}</prop>-->
                <prop key="hibernate.show_sql">${hibernate.show_sql:false}</prop>
                <prop key="hibernate.format_sql">${hibernate.format_sql:false}</prop>
                <prop key="hibernate.multiTenancy">DATABASE</prop>
                <prop key="hibernate.tenant_identifier_resolver">com.example.multitenancy.CurrentTenantIdentifierResolverImpl</prop>
                <prop key="hibernate.multi_tenant_connection_provider">com.example.multitenancy.MultiTenantConnectionProviderImpl</prop>
            </props>
        </property>
    </bean>
    <bean id="transactionManager"  class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
</beans>

Below is the implementation of Hibernate's CurrentTenantIdentifierResolver:

public class CurrentTenantIdentifierResolverImpl implements CurrentTenantIdentifierResolver {
    @Override
    public String resolveCurrentTenantIdentifier() {

        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return Helper.getTenantFromAuthentication(authentication);
    }
    @Override
    public boolean validateExistingCurrentSessions() {
        return true;
    }
}

and the implementation of AbstractDataSourceBasedMultiTenantConnectionProviderImpl:

public class MultiTenantConnectionProviderImpl extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl {

    @Override
    protected DataSource selectAnyDataSource() {
        return getDataSource("tenantId1");
    }
    @Override
    protected DataSource selectDataSource(String tenantIdentifier) {
        return getDataSource(tenantIdentifier);
    }

    private DataSource getDataSource(String prefix) {

        Properties properties = new Properties();
        try {
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("application.properties"));

        } catch (IOException e) {
            throw new RuntimeException();
        }

        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(properties.getProperty(prefix + ".driverClassName"));
        dataSource.setUrl(properties.getProperty(prefix + ".url"));
        dataSource.setUsername(properties.getProperty(prefix + ".username"));
        dataSource.setPassword(properties.getProperty(prefix + ".password"));
        return dataSource;
    }
}

The application.properties file looks like this:

tenantId1.driverClassName = org.postgresql.Driver
tenantId1.url = <...>
tenantId1.username = <...>
tenantId1.password = <...>

tenantId2.driverClassName = com.mysql.jdbc.Driver
tenantId2.url = <...>
tenantId2.username = <...>
tenantId2.password = <...>

Is there a way to change the hibernate dialect dynamically?

like image 865
gdrt Avatar asked May 11 '18 12:05

gdrt


People also ask

What is dialect in hibernate?

A password will be e-mailed to you. Dialect in Hibernate – Dialect is a class and a bridge between Java JDBC types and SQL types, which contains mapping between java language data type and database datatype. Dialect allows Hibernate to generate SQL optimized for a particular relational database.

How does dialectresolutioninfo work in hibernate?

With the DialectResolutionInfo in place, Hibernate can pick up the proper Dialect and configure its behavior based on the underlying database server and client capabilities. For instance, in Hibernate 6, the MySQLDialect configures the maximum VARCHAR and VARBINARY column lengths based on the underlying MySQL database server version:

How to change database dynamically at runtime in hibernate?

You know, we usually use Hibernate with a fixed database whose connection information is specified via hibernate.cfg.xml file. To allow changing database dynamically at runtime, we need to use programmatic configuration for Hibernate. For example, the following code snippet illustrates how to create a SessionFactory from a Configuration object:

How does hibernate generate a native query?

For your all of entities, Hibernate generates a native query for create tables and executes it using JDBC. In order to generate native query, Hibernate uses your entity mapping and uses the provided dialect class to map java types to database data types.


1 Answers

You cannot achieve using single hibernate configuration file. You need to have different configurations files for each database.

For example, you have two database MySql and Oracle:

To configure mysql database

hibernate-mysql.cfg.xml

To configure oracle database

hibernate-oracle.cfg.xml

Create two different sessions for and code should be like this.

private static SessionFactory sessionAnnotationFactory; 

    sessionAnnotationFactory = new Configuration().configure("hibernate-mysql.cfg.xml").buildSessionFactory();

Session MySqlSession = sessionAnnotationFactory.openSession();

For Oracle database configuration

sessionAnnotationFactory = new Configuration().configure("hibernate-oracle.cfg.xml").buildSessionFactory();

Session OracleSession = sessionAnnotationFactory.openSession()
like image 139
Anil Satija Avatar answered Sep 18 '22 20:09

Anil Satija