多租户在Spring-boot中使用动态Postgresql数据库

问题描述 投票:0回答:1

我目前在Spring-boot项目中集成多租户时遇到问题。

[这里,情况是:我将spring boot项目与一个数据库源链接到Postgresql:已知池中的数据库之一,即“ master”,这是spring-boot项目将连接的默认DB至。池中的其他数据库是未知的,我希望能够在应用程序运行时通过API调用将数据库连接切换到它们。在TablesController中,我创建了一个GET METHOD,在查询中获取参数“ envname”,并尝试切换与envname连接的当前数据库,并返回其表的列表。

我尝试过this tutorial,但是我找不到为什么它不起作用。这是我的代码:

pom.xml

<properties>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jdbc</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>com.googlecode.json-simple</groupId>
        <artifactId>json-simple</artifactId>
        <version>1.1</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

application.properties

# General properties
server.port = 9090

# Hibernate properties
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=none
spring.jpa.hibernate.show-sql=true
spring.jpa.hibernate.tenant_identifier_resolver= com.example.myproject.util.CurrentTenantIdentifierResolverImp

# Postgresql properties
spring.datasource.url=jdbc:postgresql://localhost:5432/master
spring.datasource.username=postgres
spring.datasource.password=password
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.initialization-mode=always
spring.datasource.initialize=true
spring.datasource.continue-on-error=true

TenantContext

public class TenantContext {

  final public static String DEFAULT_TENANT = "master";

  private static ThreadLocal<String> currentTenant = new ThreadLocal<String>()
  {
    @Override
    protected String initialValue() {
      return DEFAULT_TENANT;
    }
  };

  public static void setCurrentTenant(String tenant) {
    currentTenant.set(tenant);
  }

  public static String getCurrentTenant() {
    return currentTenant.get();
  }

  public static void clear() {
    currentTenant.remove();
  }
}

TablesController

@RestController
public class TablesController {

@Autowired
GetTablesName getTablesNames;

@Autowired
DataSource dataSource;

@Autowired
private JdbcTemplate jdbcTemplate;

@Autowired
private ApplicationContext appContext;

@Autowired
public BackXPressConfig backXPressConfig;

@GetMapping("/{envname}/tables")
public ResponseEntity<?> getTablesByEnv(@PathVariable("envname") String envname){
    TenantContext.setCurrentTenant(envname);    
    try {
        System.out.println(TenantContext.getCurrentTenant());
        Object o = JdbcUtils.extractDatabaseMetaData(dataSource, getTablesNames);
        System.out.println(o);
        return ResponseEntity.ok(o);
    } catch (MetaDataAccessException e) {
        System.out.println(e);
        return null;
    }
}
}

TenantIdentifier

@Component
public class TenantIdentifier implements CurrentTenantIdentifierResolver{

  @Override
  public String resolveCurrentTenantIdentifier() {
    return TenantContext.getCurrentTenant();
  }

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

TenantInterceptor

@Component
public class TenantInterceptor extends HandlerInterceptorAdapter {

@Override
public void postHandle(
  HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
      throws Exception {
TenantContext.clear();
  }
}

HibernateConfig

@Configuration
public class HibernateConfig {    
@Autowired
private JpaProperties jpaProperties;

@Bean
public JpaVendorAdapter jpaVendorAdapter() {
   return new HibernateJpaVendorAdapter();
}

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,
  MultiTenantConnectionProvider multiTenantConnectionProviderImpl,
  CurrentTenantIdentifierResolver currentTenantIdentifierResolverImpl) {

   Map<String, Object> properties = new HashMap<>();
   properties.putAll(jpaProperties.getProperties());
   properties.put(Environment.MULTI_TENANT, MultiTenancyStrategy.DATABASE);
   properties.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, multiTenantConnectionProviderImpl);
   properties.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolverImpl);

   LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
   em.setDataSource(dataSource);
   em.setPackagesToScan("com.example.myproject");
   em.setJpaVendorAdapter(jpaVendorAdapter());
   em.setJpaPropertyMap(properties);
   return em;
   }
}

GetTables

@Service
public class GetTablesName implements DatabaseMetaDataCallback {

public List<String> processMetaData(DatabaseMetaData metaData) throws SQLException {
    ResultSet rawDataSet = metaData.getTables(metaData.getUserName(), null, null, new String[]{"TABLE"});
    List<String> listTable = new ArrayList<String>();
    while (rawDataSet.next()) {
        listTable.add(rawDataSet.getString(3));
    }
    return listTable;
}

}

MultiTenantConnectionProviderImpl

@Component
public class MultiTenantConnectionProviderImpl implements MultiTenantConnectionProvider {  
  private static final long serialVersionUID = 6246085840652870138L;

  @Autowired
  private DataSource dataSource;

  @Override
  public Connection getAnyConnection() throws SQLException {
    return dataSource.getConnection();
  }

  @Override
  public void releaseAnyConnection(Connection connection) throws SQLException {
    connection.close();
  }

  @Override
  public Connection getConnection(String tenantIdentifier) throws SQLException {
    final Connection connection = getAnyConnection();
    try {
      connection.createStatement().execute( "USE " + tenantIdentifier );
    }
    catch ( SQLException e ) {
      throw new HibernateException(
          "Could not alter JDBC connection to specified schema [" + tenantIdentifier + "]",
          e
          );
    }
    return connection;
  }

  @Override
  public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException {
    try {
      connection.createStatement().execute( "USE " + tenantIdentifier );
    }
    catch ( SQLException e ) {
      throw new HibernateException(
      "Could not alter JDBC connection to specified schema [" + tenantIdentifier + "]",
      e
      );
    }
    connection.close();
  }

  @SuppressWarnings("rawtypes")
  @Override
  public boolean isUnwrappableAs(Class unwrapType) {
    return false;
  }

  @Override
  public <T> T unwrap(Class<T> unwrapType) {
    return null;
  }

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

}
postgresql spring-boot hibernate spring-data-jpa multi-tenant
1个回答
© www.soinside.com 2019 - 2024. All rights reserved.