我正在努力使用休眠模式在基于Spring的应用程序中启用 Multi-Tenancy 。我创建了CurrentTenantIdentifierResolver的自定义实现,并覆盖了resolveCurrentTenantIdentifier()方法以确定租户标识符。当我提供硬编码的租户标识符时,该应用程序运行良好。

但是随后提出了一个要求,即根据请求标头中的值从数据库默认架构的表中获取租户标识符。我在很多地方都进行了搜索,做了一些命中和尝试,但收效甚微。

任何帮助,将不胜感激。请让我知道我需要提供什么所有信息以更好地了解问题情况。

CustomTenantIdentifierResolver.java

public class CustomTenantIdentifierResolver implements CurrentTenantIdentifierResolver {

     public static final String DEFAULT_TENANT_SCHEMA = "public";

    @Override
    public String resolveCurrentTenantIdentifier() {
        try {
            Provider<TenantRequestContext> tenantProvider = SpringContext
                .getBean(Provider.class);
            if (tenantProvider == null) {
                return DEFAULT_TENANT_SCHEMA;
            } else {
                TenantRequestContext tenantRequestContext = tenantProvider
                    .get();
                String tenantId = tenantRequestContext.getTenantIdValue();
                String tenantSchema = tenantRequestContext.getTenantSchema(tenantId);
                return tenantSchema;
            }
        } catch (Exception ex) {
            return DEFAULT_TENANT_SCHEMA;
        }
        // return "myschema";
    }

    @Override
    public boolean validateExistingCurrentSessions() {
        return true;
    }

}

TenantRequestContextImpl.java
@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class TenantRequestContextImpl  implements TenantRequestContext{

    @Autowired
    private TenantReadService tenantReadService;

    @Override
    public String getTenantIdValue() {
        String tenantId =((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes())
            .getRequest().getHeader("tenantId");
        return tenantId;
    }

    @Override
    public String getTenantSchema(String tenantId) {
        Tenant tenant = tenantReadService.findById(Integer.parseInt(tenantId));
        return tenant.getTenantSchemaName();
    }
}

TenantReadServiceImpl.java
@Repository
public class TenantReadServiceImpl implements TenantReadService {

    @Autowired
    private SessionFactory defaultSessionFactory;

    public TenantReadServiceImpl() {

    }

    public TenantReadServiceImpl(SessionFactory defaultSessionFactory) {
        this.defaultSessionFactory = defaultSessionFactory;
    }

    @Override
    @Transactional
    public Tenant findById(Integer tenantId) {
        String hql = "from Tenant where id=" + tenantId;
        Query query = defaultSessionFactory.getCurrentSession().createQuery(hql);
        Tenant tenant = (Tenant) query.uniqueResult();
        defaultSessionFactory.getCurrentSession().clear();
        return tenant;
    }
}

MultitenancyPlatformConfig.java
@Configuration
@EnableTransactionManagement
@ComponentScan("com.mypackage")
public class MultitenancyPlatformConfig {

    @Bean(name="defaultDataSource")
    public DataSource dataSource() {
        final JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
        DataSource dataSource = dsLookup.getDataSource(java:comp/env/jdbc/myDataSource);
        return dataSource;
    }

    @Autowired
    @Bean(name = "defaultSessionFactory")
    public SessionFactory getSessionFactory(DataSource defaultDataSource) {
        LocalSessionFactoryBuilder sessionBuilder = new LocalSessionFactoryBuilder(
            defaultDataSource);
        sessionBuilder.addAnnotatedClasses(Tenant.class);
        sessionBuilder.addProperties(hibProperties());
        return sessionBuilder.buildSessionFactory();
    }

    private Properties hibProperties() {
       Properties properties = new Properties();
       properties.put("hibernate.format_sql",
            "true");
       properties.put("hibernate.dialect",
            "org.hibernate.dialect.PostgreSQLDialect");
       properties.put("hibernate.default_schema", "public");
       return properties;
    }

    @Autowired
    @Bean(name = "tenantReadService")
    public TenantReadService getTenantReadService(SessionFactory defaultSessionFactory) {
        return new TenantReadServiceImpl(defaultSessionFactory);
    }
}

MyPlatformConfig.java
@Configuration
@EnableTransactionManagement
@ComponentScan("com.mypackage")
@EnableJpaRepositories("com.mypackage.repository")
    public class MyPlatformConfig {

    @Bean
    public DataSource dataSource() {
        final JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
        DataSource dataSource = dsLookup.getDataSource("java:comp/env/jdbc/ihubDataSource");
        return dataSource;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setGenerateDdl(false);
        vendorAdapter.setDatabase(Database.POSTGRESQL);
        vendorAdapter.setDatabasePlatform("org.postgresql.Driver");

        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(vendorAdapter);
        factory.setPackagesToScan("com.mypackage.entity");
        factory.setJpaProperties(hibProperties());
        return factory;
    }

    private Properties hibProperties() {
        Properties properties = new Properties();
        properties.put("hibernate.dialect","org.hibernate.dialect.PostgreSQLDialect");
        properties.put("hibernate.format_sql","true");
        properties.put("hibernate.tenant_identifier_resolver"," com.mypackage.tenantresolvers.CustomTenantIdentifierResolver");
        properties.put("hibernate.multi_tenant_connection_provider", "com.mypackage.connectionproviders.MultiTenantConnectionProviderImpl");
        properties.put("hibernate.multiTenancy", "SCHEMA");
        return properties;
    }

    @Bean
    public JpaTransactionManager transactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
        return transactionManager;
    }
}

最佳答案

这是解决问题的方法:

  • 使用自定义ServletFilter从请求标头中提取值。
  • 在您自定义的ServletFilter中:对默认模式执行查询以获取租户标识符并将其放入ThreadLocal
  • resolveCurrentTenantIdentifier 中,只需从ThreadLocal返回值即可。
  • 09-28 12:42