Home > database >  Configure a bean with a parameter sent with API Call
Configure a bean with a parameter sent with API Call

Time:02-11

I have a configuration class to configure a sqlite database connection. Here I do configure it manually.

@Configuration
@EnableJpaRepositories(basePackages = "fr.company.dashboard.io.repository")
public class DBConfiguration {
    private final ApplicationProperties applicationProperties;

    @Autowired
    public DBConfiguration(ApplicationProperties applicationProperties) {
        this.applicationProperties = applicationProperties;
    }

    @Bean
    public DataSource dataSource() {
        final DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(applicationProperties.getProperty("hibernate.connection.driverClassName"));
        dataSource.setUrl(applicationProperties.getProperty("url"));
        dataSource.setUsername(applicationProperties.getProperty("user"));
        dataSource.setPassword(applicationProperties.getProperty("password"));
        return dataSource;
    }
}

I would like to set the database by using an API get method which includes the database name in the URL

CodePudding user response:

You need firstly, to change the scope of the bean to be "request", then pass the parameters you want to the been like this:

    @Scope("request")
    @bean
      public DataSource dataSource(String dbName){
           ......
           ......
        return dataSource;
      }

And on your controller, you should do something like that,

@Controller
public class YourController{

   @Autowired
   private BeanFactory beanFactory;

   @RequestMapping("/")
   public String exFunc(){
      String dbName = "....."
      DataSource dataSource = 
         beanFactory.getBean(DataSource.class, dbName);
   }
}

CodePudding user response:

In my case, I have multiple sqlite db in a same folder. I changed my controller in order to retrieve the database name from entry point:

@RestController
@RequestMapping("/dbfiles")
public class AppController {
    private final AppService appService;
    private final DataSourceContextHolder dataSourceContextHolder;

    @Autowired
    public DatabaseListController(AppService AppService,
                                  DataSourceContextHolder dataSourceContextHolder) {
        this.appService= appService;
        this.dataSourceContextHolder = dataSourceContextHolder;
    }
   
    @GetMapping("/{dbName}")
    public List<AppResponse> getAll(@PathVariable("dbName") String dbName) throws IOException {
        Set<String> databases = Utils.getDatabaseList();
        if(databases.contains(dbName) || databases.contains(dbName   ".db")) {
            dataSourceContextHolder.setBranchContext(dbName);
        }

        List<AppResponse> returnValue = new ArrayList<>();
        ModelMapper modelMapper = new ModelMapper();
        modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
        List<AppResponse> appResponses = appService.getAll();
        returnValue = modelMapper.map(
                appResponses ,
                new TypeToken<List<AppDto>>() {}.getType()
        );
        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        String json = gson.toJson(returnValue);

        System.out.println(json);
        return returnValue;
    }
}

I used a component class where i pass the database name:

@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public class DataSourceContextHolder {
    private static ThreadLocal<String> threadLocal;

    public DataSourceContextHolder() {
        threadLocal = new ThreadLocal<>();
    }

    public void setBranchContext(String dbName) {
        threadLocal.set(dbName);
    }

    public String getBranchContext() {
        return threadLocal.get();
    }

    public static void clearBranchContext() {
        threadLocal.remove();
    }
}

I also created a DataSourceRouting class that handles at start the databases:

@Component
public class DataSourceRouting extends AbstractRoutingDataSource {
    private DataSourceContextHolder dataSourceContextHolder;
    private DataSourceConfig dataSourceConfig;

    public DataSourceRouting(DataSourceContextHolder dataSourceContextHolder,
                             DataSourceConfig dataSourceConfig) throws IOException {
        this.dataSourceContextHolder = dataSourceContextHolder;
        this.dataSourceConfig = dataSourceConfig;

        Map<Object, Object> dataSourceMap = new HashMap<>();
        Set<String> databases = Utils.getDatabaseList();
        databases.forEach(database -> {
            DataSource dataSource = dataSourceBuilder(database);
            dataSourceMap.put(database, dataSource);
            this.setDefaultTargetDataSource(dataSource);

        });
        this.setTargetDataSources(dataSourceMap);

    }

    private DataSource dataSourceBuilder(String database) {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(dataSourceConfig.getDriverClassName());
        dataSource.setUrl(dataSourceConfig.getUrl()   database);
        dataSource.setUsername(dataSourceConfig.getUsername());
        dataSource.setPassword(dataSourceConfig.getPassword());
        return dataSource;
    }

    @Override
    protected Object determineCurrentLookupKey() {
        //System.out.println(dataSourceContextHolder.getBranchContext().toString());
        return dataSourceContextHolder.getBranchContext();
    }
}

I created a POJO that reads in application.properties :

@Component
@ConfigurationProperties(prefix="datasource")
@Getter @Setter
public class DataSourceConfig {
    private String url;
    private String password;
    private String username;
    private String driverClassName;
}

This code allow to pass the database name and to make queries on the right one. As the databases have the same structure, I do output the correct values as expected.

  • Related