【问题标题】:How to manually initialize a database using a datasource?如何使用数据源手动初始化数据库?
【发布时间】:2021-08-21 15:41:50
【问题描述】:

我当前的 AbstractionDataSource 实现执行以下操作:

1- Spring 使用默认 URL/Schema 进行初始化,以便用户可以登录。

2- 成功登录后,默认架构更改为基于 UserDetails 类的另一个架构。

例如,来自公司 X 的用户被重定向到模式 X,而来自公司 K 的用户在成功登录后被重定向到模式 K。

问题: 我需要从第 2 步初始化数据库。使用 spring.jpa.hibernate.ddl-auto=create

目前,Spring boot 只初始化用户用来登录的默认数据库。但是,我需要为依赖于登录用户的其他模式执行不同的初始化。

public class UserSchemaAwareRoutingDataSource extends AbstractDataSource {

    @Autowired
    private UsuarioProvider usuarioProvider;

    /**
     * This is the data source that is dependent on the user.
     */
    @Autowired
    @Qualifier(value = "companyDependentDataSource")
    private DataSource companyDependentDataSource;

    /**
     * This is the initial datasource.
     */
    @Autowired
    @Qualifier(value = "loginDataSource")
    private DataSource loginDataSource;

    /**
     * Variable representing the environment in which the current application is
     * running.
     */
    @Autowired
    Environment env;

    /**
     * A semi-persistent mapping from Schemas to dataSources. This exists,
     * because ??? to increase performance and diminish overhead???
     */
    private LoadingCache<String, DataSource> dataSources = createCache();

    public UserSchemaAwareRoutingDataSource() {

    }

    /**
     * Creates the cache. ???
     *
     * @return
     */
    private LoadingCache<String, DataSource> createCache() {
        return CacheBuilder.newBuilder()
                .maximumSize(100)
                .expireAfterWrite(10, TimeUnit.MINUTES)
                .build(
                        new CacheLoader<String, DataSource>() {
                    public DataSource load(String key) throws Exception {
                        return buildDataSourceForSchema(key);
                    }
                });
    }

    /**
     * Builds the datasource with the schema parameter. Notice that the other
     * parameters are fixed by the application.properties.
     *
     * @param schema
     * @return
     */
    private DataSource buildDataSourceForSchema(String schema) {
        logger.info("building datasource with schema " + schema);

        String url = env.getRequiredProperty("companydatasource.url");

        String username = env.getRequiredProperty("companydatasource.username");
        String password = env.getRequiredProperty("companydatasource.password");

        DataSource build = DataSourceBuilder.create()
                .driverClassName(env.getRequiredProperty("companydatasource.driver-class-name"))
                .username(username)
                .password(password)
                .url(url)
                .build();

        return build;
    }

    /**
     * Gets the Schema from the Cache, or build one if it doesnt exist.
     *
     * @return
     */
    private DataSource determineTargetDataSource() {
        try {
            String db_schema = determineTargetSchema();
            logger.info("using schema " + db_schema);
            return dataSources.get(db_schema);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * Determine the schema based on the logger-in User.
     *
     * @return
     */
    private String determineTargetSchema() {
        try {
            Usuario usuario = usuarioProvider.customUserDetails(); // request scoped answer!
            return usuario.getTunnel().getDb_schema();
        } catch (RuntimeException e) {
            // This shouldn't be necessary, since we are planning to use a pre-initialized database.
            // And there should only be usages of this DataSource in a logged-in situation
            logger.info("usuario not present, falling back to default schema", e);
            return "default_company_schema";
        }
    }

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

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return determineTargetDataSource().getConnection(username, password);
    }

    @Override
    public ConnectionBuilder createConnectionBuilder() throws SQLException {
        return super.createConnectionBuilder();
    }

}

// 我期待有某种方式可以调用休眠工具以通过以下方法初始化数据库:

/**
 * Builds the datasource with the schema parameter. Notice that the other
 * parameters are fixed by the application.properties.
 *
 * @param schema
 * @return
 */
private DataSource buildDataSourceForSchema(String schema) {
    logger.info("building datasource with schema " + schema);

    String url = env.getRequiredProperty("companydatasource.url");

    String username = env.getRequiredProperty("companydatasource.username");
    String password = env.getRequiredProperty("companydatasource.password");

    DataSource build = DataSourceBuilder.create()
            .driverClassName(env.getRequiredProperty("companydatasource.driver-class-name"))
            .username(username)
            .password(password)
            .url(url)
            .build();

    //Init database or update it with hibernate
    String initDatabase = env.getRequiredProperty("spring.jpa.hibernate.ddl-auto");
    if(initDatabase.equalsIgnoreCase("update")){
    
        org.hibernate.tool.hbm2ddl.SchemaUpdate schemaUpdate = new SchemaUpdate();
        
        schemaUpdate.execute(??);
        
    }
    
    //
    
    return build;
}

//

存储库:https://github.com/KenobySky/SpringSchema

【问题讨论】:

    标签: spring spring-boot


    【解决方案1】:

    随着对象的初始化,不能保证 Spring 会在私有 dataSources 属性被设置之前将 Environment 对象分配给 Autowired 属性。

    有几个选项:

    1. 添加一个构造函数来为您分配该属性
    2. 使用@PostConstruct 注释使该赋值等待对象被构造。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-04
      • 1970-01-01
      • 1970-01-01
      • 2020-09-25
      相关资源
      最近更新 更多