首页 > 解决方案 > 如何使用数据源手动初始化数据库?

问题描述

我当前的 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

标签: springspring-boot

解决方案


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

有几个选项:

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

推荐阅读