Установка гибернации диалекта динамически - PullRequest
0 голосов
/ 11 мая 2018

Я реализовал Архитектуру мультитенантной базы данных Hibernate , где в зависимости от арендатора выбирается конкретное соединение с базой данных. Я использую Spring 4.3 и Hibernate 5.2.

Все хорошо, когда арендаторы используют одну и ту же СУБД, но когда они отличаются, мне приходится динамически изменять настройки диалекта в свойствах гибернации, которые я не знаю, как.

Мои свойства гибернации находятся в 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>

Ниже представлена ​​реализация Hibernate 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;
    }
}

и реализация 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;
    }
}

Файл application.properties выглядит следующим образом:

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

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

Есть ли способ динамически изменять спящий диалект?

Ответы [ 4 ]

0 голосов
/ 28 мая 2018

Я не понимаю, почему вы не смогли добиться этого, используя конфигурацию, основанную исключительно на аннотациях.Я установил свой диалект следующим образом:

properties.put("hibernate.dialect", "org.hibernate.dialect.SQLServerDialect");

У меня фактически есть некоторый код, который полностью перестраивает базу данных вместе с некоторыми тестовыми данными, если значение в файле конфигурации установлено на ON.Вы должны быть в состоянии сделать то же самое, переключившись на полностью основанный на Java конфиг.

0 голосов
/ 25 мая 2018

Я думаю, вы должны создать два сессионных факторных объекта для двух разных баз данных.

См. Здесь: Hibernate, конфигурирующий несколько источников данных и несколько фабрик сеансов

В качестве альтернативы , пожалуйста, посмотрите мой нижеприведенный sol и сообщите мне, если возникнут проблемы:

Вы можете использовать метод setHibernateProperties (java.util.Properties hibernateProperties) метода LocalSessionFactoryBean для изменения hibernate.dialect.

Вы внедрили бин hibernate сессионный завод через Spring. Так что все, что вам нужно сделать, это получить бин из контейнера Spring и изменить свойства гибернации. Ниже я добавил только диалект к фабрике сессий, но я рекомендую вам установить все необходимые свойства.

Приведенный ниже метод предназначен только для справки и понимания, при необходимости измените логику и параметры.

Вызовите метод ниже, если вы хотите изменить спящий диалект.

@Autowired
private LocalSessionFactoryBean sessionFactory; 

//Change the logic and parameters as needed.
public void changedialect(String database) {    

    Properties prop=new Properties();
    String dialect="org.hibernate.dialect.MySQLDialect";

    if("postgresql".equals(database)) dialect="org.hibernate.dialect.PostgreSQLDialect";


    prop.setProperty("hibernate.dialect", dialect);
    sessionFactory.setHibernateProperties(prop);

}
0 голосов
/ 28 мая 2018

Я бы сделал это с помощью Spring's https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/datasource/lookup/AbstractRoutingDataSource.html. Так что, как сказал Анил Сатия, вы должны определить два источника данных, а затем настроить механизм, когда вы могли бы использовать каждый из них.У вас есть примеры для этого метода в ссылках ниже: https://github.com/wmeints/spring-multi-tenant-demo

http://www.baeldung.com/spring-abstract-routing-data-source

0 голосов
/ 25 мая 2018

Нельзя добиться использования одного файла конфигурации Hibernate.У вас должны быть разные файлы конфигурации для каждой базы данных.

Например, у вас есть две базы данных MySql и Oracle:

Для настройки базы данных mysql

hibernate-mysql.cfg.xml

Для настройки оракулабаза данных

hibernate-oracle.cfg.xml

Создайте два разных сеанса, и код должен выглядеть следующим образом.

private static SessionFactory sessionAnnotationFactory; 

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

Session MySqlSession = sessionAnnotationFactory.openSession();

Для конфигурации базы данных Oracle

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

Session OracleSession = sessionAnnotationFactory.openSession()
...