spring - 如何使用数据源手动初始化数据库?
问题描述
我当前的 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;
}
//
解决方案
通过对象的初始化,不能保证 Spring 会在dataSources
设置私有属性之前将 Environment 对象分配给 Autowired 属性。
有几个选项:
- 添加一个构造函数来为您分配该属性
- 使用
@PostConstruct
注释使该分配等待对象被构造。