[英]How to add new Datasource at runtime Spring Boot
我有這個,但我找不到任何可能的解決方案。 我找到的所有答案都是關於配置兩個或多個 Datasource 或 Multitenant Dabatase,但這不是我需要的。
我必須這樣做:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "primaryEntityManagerFactory",
basePackages = "com.example.primary")
public class SmartConnectConfig {
@Primary
@Bean(name = "primaryDatasource")
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl(env.getProperty("spring.datasource.url"));
dataSource.setUsername(env.getProperty("spring.datasource.username"));
dataSource.setPassword(env.getProperty("spring.datasource.password"));
return dataSource;
}
@Primary
@Bean(name = "primaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder,
@Qualifier("primaryDatasource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("com.example.primary")
.persistenceUnit("primary")
.build();
}
@Primary
@Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(
@Qualifier("primaryEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
有人可以給一些建議。
謝謝
在 Spring Boot 中有大量資源解釋如何配置數據源,使用各種選項和各種方式來檢索連接的配置詳細信息(從外部文件、從 application.properties、硬編碼等)。 另一方面,如果您已經配置了一個數據源並且在運行時您想要更改此連接的詳細信息(可能是數據庫更改的主機名)而不重新啟動應用程序,則信息有點稀缺。
我所說的用例是允許用戶更改全部或部分數據庫連接詳細信息(例如主機名)的情況。 用戶有一個設置頁面,他可以在其中編輯這些詳細信息。 此設置保存在外部文件中,而不是在 application.properties 中。 在這種情況下,對於對該數據庫的后續請求,需要使用新參數。 Spring 的默認行為是將 Beans 創建為 Singleton,包括您配置的 DataSource,因此,如果您更改設置,則不會重新創建 bean(它仍將使用舊參數)。
在這里,我將介紹如何實現這一目標的眾多方法中的一種。
創建數據源
有多種方法可以創建數據源以在 Spring Boot 中使用,並且為此提供了大量資源。 在這里,我將展示如何創建一個簡單的基於 JdbcTemplate 的存儲庫,該存儲庫在單獨的 package 中配置(不會使用 application.properties 中定義的數據庫屬性)。
這是數據源的配置片段:
@Configuration
public class CustomDataSourceConfiguration {
@Lazy
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public DataSource customDataSource() {
DataSourceBuilder dsBuilder = DataSourceBuilder.create();
dsBuilder.driverClassName(„oracle.jdbc.driver.OracleDriver“);
CustomDatabaseSettings dbSettings = <….>//Here obtain the settings from whereever you need
dsBuilder.url(dbSettings.jdbcUrl());
dsBuilder.username(dbSettings.username());
dsBuilder.password(dbSettings.password());
return dsBuilder.build();
}
@Lazy
@Qualifier(„customJdbcTemplate“)
@Bean
public JdbcTemplate customJdbcTemplate() {
return new JdbcTemplate(customDataSource());
}
}
在這里,使用注釋為配置 class 的 class,我定義了 DataSource 和 JdbcTemplate 所需的 Bean。 @Lazy 注釋在這種情況下很重要,因為我從 Java 代碼讀取了包含數據庫配置的外部文件,這發生在 Spring 容器初始化所有 bean 之后。 默認情況下,Beans 是 Eagerly 加載的,這意味着當 Spring 掃描到它們並遇到它們時,它將實例化它們。 Lazy 將等待初始化,直到對該 bean 發出第一個請求。 請參閱@Lazy 的文檔 – https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Lazy.html
bean 的 Scope 被設置為“Prototype”。 默認情況下,scope 設置為 Singleton,但正如我在文章開頭所說,我們需要一種方法來使用新的配置詳細信息重新初始化數據源,最簡單的方法是使用這個 scope。這個 scope 每次都會調用初始化請求 bean。 從代碼中可以看出,這非常適合我們的情況,因為再次調用初始化方法也將獲得新配置的數據庫連接詳細信息。 使用 Prototype 有一些注意事項。 有關范圍的更多信息可以在文檔中找到: https://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch04s04.html
這也是存儲庫的代碼:
@Lazy
@Repository
@ComponentScan(basePackages = „com.cli.jdbc.datasource“)
public class CustomRepository{
@Qualifier(„customJdbcTemplate“)
@Autowired
private JdbcTemplate jdbcTemplate;
//Implement methods here
因此,我們通過讀取在運行時動態更改的自定義配置文件來配置數據源。 現在,當設置更改時,我們需要一種方法來重新初始化數據源。 從概念上講,這是通過簡單地從 Spring 上下文中檢索數據源 bean 來完成的。 因為我們使用了 Prototype scope,這將導致再次調用上面定義的方法中的邏輯,這反過來將對我們的數據庫進行最新更改。 在這一步之后,我們還需要從上下文中檢索 jdbcTemplate 並將這個新的 DataSource 設置給它。
代碼如下所示:
public void refreshCustomJdbc() {
DataSource ds = (DataSource) getSpringContext().getBean(„customDataSource“);
JdbcTemplate customJdbcTemplate = (JdbcTemplate) getSpringContext().getBean(„customJdbcTemplate“);
customJdbcTemplate.setDataSource(ds);
}
我建議您嘗試將數據源信息存儲到HttpSession
。
例:
@RestController
public class CategoryController {
@PostMapping("init")
public void createDatasource(HttpSession session, @RequestBody CategoryRequestDto request) {
if (session != null && session.isNew()) {
DBInfo dbInfo = request.getUserinfo().getDatabaseInfo();
HikariDataSource hikariDataSource = new HikariDataSource();
hikariDataSource.setUsername(dbInfo.getUsername());
hikariDataSource.setPassword(dbInfo.getPassword());
hikariDataSource.setJdbcUrl("jdbc:h2:mem:"+session.getId());
hikariDataSource.setDriverClassName(Driver.class.getName());
session.setAttribute("datasource", hikariDataSource);// store datasource for later use
}
}
}
如果需要配置兩個數據源,則只需使用不同的名稱來調用它,例如:
@Bean("dataSource")
public DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(this.hProperties.getProperty("db.driverClass"));
dataSource.setUrl(this.hProperties.getProperty("db.location"));
dataSource.setUsername(this.hProperties.getProperty("db.username"));
dataSource.setPassword(this.hProperties.getProperty("db.password"));
return dataSource;
}
@Bean("dataSourceTwo")
public DataSource getDataSourceLucca() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(this.hProperties.getProperty("db.lucca.driverClass"));
dataSource.setUrl(this.hProperties.getProperty("db.lucca.location"));
dataSource.setUsername(this.hProperties.getProperty("db.lucca.username"));
dataSource.setPassword(this.hProperties.getProperty("db.lucca.password"));
return dataSource;
}
@Bean("sessionFactory")
public LocalSessionFactoryBean getSessionFactory() {
loadProperties();
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(getDataSource());
sessionFactory.setPackagesToScan(new String[] { "com.monty.goofy" });
sessionFactory.setHibernateProperties(getHibernateProperties());
return sessionFactory;
}
@Bean("sessionFactoryTwo")
public LocalSessionFactoryBean getSessionFactoryTwo() {
loadProperties();
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(getDataSourceTwo());
return sessionFactory;
}
@Bean("transactionManager")
public HibernateTransactionManager transactionManager() {
HibernateTransactionManager txManager = new HibernateTransactionManager();
SessionFactory sessionFactory = getSessionFactory().getObject();
txManager.setSessionFactory(sessionFactory);
return txManager;
}
@Bean("transactionManagerTwo")
public HibernateTransactionManager transactionManagerLucca() {
HibernateTransactionManager txManager = new HibernateTransactionManager();
SessionFactory sessionFactoryTwo = getSessionFactoryTwo().getObject();
txManager.setSessionFactory(sessionFactoryTwo);
return txManager;
}
這是同時使用它們的示例:
@Override
@Transactional("transactionManager")
public void saveOne(){
}
@Override
@Transactional("transactionManagerTwo")
public void saveTwo(){
}
(這是手動配置)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.