【问题标题】:Setting hibernate dialect dynamically动态设置休眠方言
【发布时间】:2018-10-21 21:32:40
【问题描述】:

我实现了Hibernate's multitenant database architecture,根据租户选择特定的数据库连接。我正在使用 Spring 4.3 和 Hibernate 5.2。

当租户使用相同的 RDBMS 时一切都好,但当它们不同时,我必须动态更改休眠属性中的方言设置,我不知道如何。

我的休眠属性在 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 = <...>

有没有办法动态改变休眠方言?

【问题讨论】:

  • @nikesh,这看起来确实相关,但您能否详细说明如何获取现有配置并进行更改?我很乐意接受你的回答。
  • 从 Spring 中获取 SessionFactory bean 并调用 GetConfiguration 方法。它不工作。在代码中使用 id : sessionFactory。

标签: java spring hibernate multi-tenant


【解决方案1】:

如果您没有设置显式方言,hibenrate 使用 DialectResolver 实现的类自动解析它。

您可以编写自己的自动解析器。 OwnDialectResolver example

【讨论】:

    【解决方案2】:

    我认为你应该为两个不同的数据库创建两个 sessionfactory 对象。

    参考这里:Hibernate configuring multiple datasources and multiple session factories

    或者,如果有任何问题,请查看我的以下 sol 并告诉我:

    您可以使用 LocalSessionFactoryBean 的 setHibernateProperties(java.util.Properties hibernateProperties) 方法来更改 hibernate.dialect。

    您已经通过 Spring 注入了 hibernate sessionfactory bean。所以你所要做的就是从 spring 容器中获取 bean 并更改 hibernate 属性。下面我只将方言添加到会话工厂,但我建议您设置所有必需的属性。

    以下方法仅供参考和理解,根据需要更改逻辑和参数。

    当你想改变休眠方言时调用下面的方法。

    @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);
    
    }
    

    【讨论】:

      【解决方案3】:

      我不明白为什么你不能使用纯粹的基于注释的配置来实现这一点。我这样设置我的方言:

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

      如果配置文件中的值设置为 ON,我实际上有一些代码可以完全重建数据库以及一些测试数据。您应该能够通过切换到完全基于 java 的配置来做同样的事情。

      【讨论】:

        【解决方案4】:

        您无法使用单个休眠配置文件来实现。您需要为每个数据库设置不同的配置文件。

        比如你有两个数据库MySql和Oracle:

        配置mysql数据库

        hibernate-mysql.cfg.xml
        

        配置oracle数据库

        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()
        

        【讨论】:

        • 这是老版本的代码写法。根据当前版本,默认情况下会加载 hibernate.cfg.xml。
        猜你喜欢
        • 2023-03-23
        • 1970-01-01
        • 1970-01-01
        • 2015-07-26
        • 2017-02-28
        • 1970-01-01
        • 2020-05-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多