如何实现多租户 Spring Boot 应用程序(其中每个用户都有自己的数据库)

Ren*_*235 8 java spring multi-tenant spring-data-jpa spring-boot

我正在使用 Spring Boot 构建 REST-API,我想实现一个多租户结构来处理数据。我想要一个名为的数据库,Main该数据库将包含一个User表,该表将包含有关用户的数据(用户名、密码......以及一个表示database指定给该用户的数据库的字段)。每次用户注册时,都会创建他各自的数据库(这是我面临困难的点之一)。我读过不同的教程,它们都Datasourceapplication.properties文件中预定义了。显然,这里的情况并非如此,因为每个用户的数据库将“即时”创建,或者如果已经创建,则进行访问。

工作流程是这样的(尽可能简单地解释):

  1. 用户注册
  2. 应用程序创建用户实体,将其保存到Main数据库并为用户创建相应的数据库
  3. 该应用程序检查每个调用,如果用户已通过身份验证,如果是,则从他的数据库中获取数据

然后,在自动创建数据库时,会出现很多关于填充数据库的问题。但首先要说的是:)

我的堆栈:POSTGRESQL、Spring Boot

先感谢您。

Mil*_*rve 7

可以根据您的需要通过以下步骤实现多租户。

  1. 添加2个配置类,一个用于共享数据库,一个用于租户数据库,用于配置LocalContainerEntityManagerFactoryBean。该 bean 应该为 LocalContainerEntityManagerFactoryBean 设置所需的多租户属性,例如
 Map<String, Object> properties = hibernateProperties.determineHibernateProperties(
        this.properties.getProperties(), new HibernateSettings());


    properties.put(Environment.MULTI_TENANT, MultiTenancyStrategy.SCHEMA);
    properties.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, this.connectionProvider);
    properties.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, this.resolver);
    properties.put(Environment.DIALECT, "org.hibernate.dialect.MySQLDialect");
Run Code Online (Sandbox Code Playgroud)

该类还应该为每种类型实现命名 bean transactionManager。例如

 @Bean(name = "tenantTransactionManager")
  public PlatformTransactionManager transactionManager() {
    JpaTransactionManager tm = new JpaTransactionManager();
    tm.setEntityManagerFactory(this.entityManagerFactory().getObject());
    return tm;
  }

Run Code Online (Sandbox Code Playgroud)
  1. 实现接口CurrentTenantIdentifierResolver和方法resolveCurrentTenantIdentifier。这应该根据当前登录用户返回租户的数据库名称。或者如果没有用户登录则使用默认数据库名称

  2. 用于记住当前租户名称的线程安全上下文持有者

  3. 使用 @Transactional 注释来注释实体类的服务实现,并传递适当的实体管理器的 bean 名称,例如

@Transactional("tenantTransactionManager") // for tenant database
Run Code Online (Sandbox Code Playgroud)

@Transactional("transactionManager") // for shared database.

Run Code Online (Sandbox Code Playgroud)
  1. 设置新用户注册时的数据库架构创建方法。并将租户数据库名称维护为共享模式中用户表中的列之一。

  2. 如果您使用 Spring Security,请实现 UserDetailsS​​ervice 接口并实现方法 loadUserByUsername ,以便它返回 TenantUser 类的对象,其中包含用户登录的附加信息(租户数据库名称)。

public class TenantUser extends org.springframework.security.core.userdetails.User {
 

  /** The tenand id. */
  private String tenantId;
Run Code Online (Sandbox Code Playgroud)

希望这些步骤可以帮助您实现您想要的目标。有许多文章详细解释了所有这些步骤。我的实现深深嵌入到我的项目中,因此它不处于可以作为工作示例共享的状态。

很高兴回答任何进一步的问题


Ren*_*235 4

我在这里找到了问题的完整解决方案:

多租户:使用 Spring Data JPA 管理多个数据源

非常感谢作者@Cepr0。

唯一缺少的是动态创建数据库。当我完成我的实现时,我将在这里更新答案。

更新

我使用以下代码创建了数据库,这是 @Milind Barve 推荐的。所以谢谢。

 Class.forName("org.postgresql.Driver");
 Connection con = DriverManager.getConnection("jdbc:postgresql://localhost:5432/","postgres", "password");
 Statement smt = con.createStatement();

 smt.executeUpdate("CREATE DATABASE [name_of_db_here] WITH OWNER DEFAULT");
Run Code Online (Sandbox Code Playgroud)

更新:初始化每个新创建的数据库的架构,我创建了一个包含所有表创建的 .sql 文件,并使用 FlyWay 初始化每个新创建的数据库

// INITIALIZE THE DB
            Flyway flyway = Flyway.configure()
                    .dataSource(dataSource)
                    .target(MigrationVersion.LATEST)
                    .load();

            flyway.migrate();
Run Code Online (Sandbox Code Playgroud)