当前位置: 首页 > 知识库问答 >
问题:

Hibernate的多租户和Spring的问题

晏永康
2023-03-14

我试图掌握如何使用Hibernate、Spring和MySQL实现多租户。对于第一个游乐场示例,我选择了单独的数据库方法:每个租户都有自己的数据库,名为accodingly。此外,还使用另一个数据库来管理租户。特定于租户的数据库保存了一些员工数据。对于第一种方法,我不强制用户进行身份验证。

我发现很难得到关于这个话题的全面教程,这就是为什么我现在有点迷路。当我尝试部署Tomcat时,会收到以下消息:

WARN : org.hibernate.engine.jdbc.connections.internal.MultiTenantConnectionProviderInitiator - Unable to instantiate specified class [sdb.persistence.TenantResolverImpl]
java.lang.ClassCastException: sdb.persistence.TenantResolverImpl cannot be cast to org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider
    at org.hibernate.engine.jdbc.connections.internal.MultiTenantConnectionProviderInitiator.initiateService(MultiTenantConnectionProviderInitiator.java:100)
    at org.hibernate.engine.jdbc.connections.internal.MultiTenantConnectionProviderInitiator.initiateService(MultiTenantConnectionProviderInitiator.java:45)
...
ERROR: org.springframework.web.servlet.DispatcherServlet - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'staffSessionFactory' defined in ServletContext resource [/WEB-INF/spring/appServlet/spring-data.xml]: Invocation of init method failed; nested exception is org.hibernate.service.spi.ServiceException: Unable to instantiate specified multi-tenant connection provider [sdb.persistence.TenantResolverImpl]
...
Caused by: org.hibernate.service.spi.ServiceException: Unable to instantiate specified multi-tenant connection provider [sdb.persistence.TenantResolverImpl]
    at org.hibernate.engine.jdbc.connections.internal.MultiTenantConnectionProviderInitiator.initiateService(MultiTenantConnectionProviderInitiator.java:104)
    at org.hibernate.engine.jdbc.connections.internal.MultiTenantConnectionProviderInitiator.initiateService(MultiTenantConnectionProviderInitiator.java:45)

这是我的Java类的结构:

xml配置分为三个文件。1.servlet上下文。xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc" 
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
             xmlns:beans="http://www.springframework.org/schema/beans"
             xmlns:context="http://www.springframework.org/schema/context"
             xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
                                 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                                 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:annotation-config />
    <beans:import resource="spring-data.xml" />
    <beans:import resource="spring-mvc.xml" />
    <context:component-scan base-package="sdb.controller" />
    <context:component-scan base-package="sdb.data" />
  <context:component-scan base-package="sdb.persistence" />
</beans:beans>
<?xml version="1.0" encoding="UTF-8"?>
<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:jpa="http://www.springframework.org/schema/data/jpa"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                           http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
                           http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

    <!-- connection to the database which holds tenant-management information -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/allTenants" />
        <property name="username" value="root" />
        <property name="password" value="root" />
    </bean>

    <bean id="tenantsSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>
        <property name="annotatedClasses">
            <list>
                <value>sdb.domain.Tenant</value>
            </list>
        </property>
    </bean>

    <bean id="tenantTransactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="tenantsSessionFactory" />
    </bean>

    <!-- not quite sure if this is right -->
    <bean id="staffSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="packagesToScan" value="sdb.domain" />
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.default_schema">public</prop>
                <prop key="hibernate.multiTenancy">SCHEMA</prop>
                <prop key="hibernate.tenant_identifier_resolver">sdb.persistence.ConnectionProviderImpl</prop>
                <prop key="hibernate.multi_tenant_connection_provider">sdb.persistence.TenantResolverImpl</prop>
            </props>
        </property>
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="staffSessionFactory" />
    </bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"            
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                           http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

    <mvc:default-servlet-handler />
    <mvc:annotation-driven />
    <mvc:resources mapping="/resources/**" location="/resources/" />

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix"><value>/WEB-INF/pages/</value></property>
        <property name="suffix"><value>.jsp</value></property>
    </bean>
</beans>

现在来看Java类。我的DAO都有一个会话工厂字段:

@Repository
public class TenantDaoImpl implements TenantDao {
    @Autowired
    private SessionFactory tenantsSessionFactory;
...
@Repository
public class StaffDaoImpl implements StaffDao {
    @Autowired
    private SessionFactory staffSessionFactory;

控制员:

@Controller
public class HomeController {

    @Autowired
    StaffDao staffDao;

    @RequestMapping(value = "/{companyName}/{staffId}", method = RequestMethod.GET)
    public String google(Model model, @PathVariable String companyName, @PathVariable String staffName) {
        List<Staff> staffs = staffDao.getStaff();
        // Yeah, I know that this functionality should actually reside in the Dao or a service layer
        Optional<Staff> foundStaff = staffs.stream().filter(staff -> staff.getSurName().equals(staffName)).findFirst();
        if (foundStaff.isPresent()) {
            model.addAttribute("foreName", foundStaff.get().getForeName());
            model.addAttribute("surName", foundStaff.get().getSurName());
        }

        model.addAttribute("companyName", companyName);

        return "staff";
    }
}

最后是多租户相关的源代码<代码>租户解决方案。java:

public class TenantResolverImpl implements CurrentTenantIdentifierResolver {
    @Override
    public String resolveCurrentTenantIdentifier() {
        if (FacesContext.getCurrentInstance() != null) {
            // I don't know if this will work - the line was copied from somewhere else
            return FacesContext.getCurrentInstance().getExternalContext().getRequestServerName();
        } 
        return null;
    }

    // Not sure what to do with this method...
    @Override
    public boolean validateExistingCurrentSessions() { return true; }
}

ConnectionProviderImpl。java

public class ConnectionProviderImpl implements MultiTenantConnectionProvider {
    private Map<String, ComboPooledDataSource> dataSources;
    @Autowired
    TenantDao tenantDao;

    @Override
    public Connection getAnyConnection() throws SQLException {
        // should probably return the connection for 'dummy' or so
        return getConnection("google");
    }

    @Override
    public Connection getConnection(String tenantIdentifier) throws SQLException {

        if (tenantDao.containsTenantWithName(tenantIdentifier)) {
            if (!dataSources.containsKey(tenantIdentifier)) {
                ComboPooledDataSource dataSource = new ComboPooledDataSource("Example");
                try {
                    dataSource.setDriverClass("com.mysql.jdbc.Driver");
                } catch (PropertyVetoException e) {
                    e.printStackTrace();
                    return null;
                }
                dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/" + tenantIdentifier);
                dataSource.setUser("root");
                dataSource.setPassword("root");
                dataSources.put(tenantIdentifier, dataSource);
            } 
            return dataSources.get(tenantIdentifier).getConnection();
        }
        return null;
    }
    ...
}

由于我对这些技术的(但)肤浅理解,我猜代码中有很多错误/缺点。请随意批评任何值得注意的事情。

共有1个答案

吴松
2023-03-14

希望你自己想出来,如果不是这里是我的提示:改变属性,你应该做;)

<prop key="hibernate.tenant_identifier_resolver">sdb.persistence.ConnectionProviderImpl</prop>
<prop key="hibernate.multi_tenant_connection_provider">sdb.persistence.TenantResolverImpl</prop>
 类似资料:
  • 我必须实现多租户Web应用程序,具有以下要求 > 单表多租户:特定实体的所有租户数据都将存储在一个表中,TENANT_DISCRIMINATOR(TENANT_ID)作为每个表中的一列。 一些表格,例如Master Countries、Masters,我希望它对所有租户都是通用的,即在这些表中,不会有像TENANT\u DISCRIMINATOR(TENANT\u ID)这样的列,但我仍然希望无缝

  • 我正试图在spring boot中开发一个REST API,并将React作为前端。React将发送前端发生的GET或POST请求,以通过REST API修改后端的MySQL数据库。在我的应用程序中,用户可以在应用程序中有多个公司,每个公司的数据彼此隔离。我在Spring遇到过多租户。如何实现REST API??如何为这个多租户配置React应用程序??Spring的反应核心有用吗??。任何我能找

  • 这段代码在使用sessionFactory的hibernate中运行良好。我想将这段代码转换为使用entityManager而不是sessionFactory,因为我将spring数据jpa与HibernateJavaEndorapter一起使用。 如果您有spring数据jpa和多租户(每个租户有单独的数据库)的任何示例/示例,请共享或提供信息。 提前谢谢。我们将非常感谢你的帮助。

  • 每个租户都有自己的数据库,可以更详细地处理用户,并且需要有一个中央数据库来处理: 代币(OAuth2) 我已经找到了多租户的解决方案,它允许我根据用户确定数据源。然而,我不确定如何将某些crud存储库链接到这个中心数据源,而将其他存储库链接到可变数据源。 另一个解决方案涉及更新属性文件,并使用配置服务器(即通过git)来触发@ReresScope注释配置。虽然我不确定这是否适用于数据源,或者这是否

  • 问题内容: 在Spring 3应用程序中,我试图通过Hibernate 4的本机MultiTenantConnectionProvider和CurrentTenantIdentifierResolver实现多租户。我发现在Hibernate 4.1.3 中存在此问题,但是我正在运行4.1.9并仍收到类似的异常: 以下是相关代码。在I中,我现在只写了一些简单的代码,每次都只返回一个新的连接,并且在这

  • 我在创建可以动态连接到多个数据库的spring boot应用程序时遇到问题,具体取决于用户输入。基本上,应用程序在不同的数据库上运行相同的sql查询。建模我的尝试在此之后,我收到以下错误: -- 配置类: 属性文件: 关于我如何实现这一点有什么想法吗?正如你所知,我对这种多数据库配置还不是非常精通。