2

I am working on my Problem for a very long time by now and I can't find a proper solution for my issue.

I need sequential access to different databases. I tried to change the link to the databases as u can see in the code below.

public static void changeDB(String dbname) throws IOException{

    File f = new File("application.properties");
    if (f.exists()) {
        f.delete(); 
    }
    f.createNewFile();
    System.out.println("gelöscht");


    FileWriter fw = new FileWriter("application.properties");
    BufferedWriter bw = new BufferedWriter(fw);

    String dbchanger = "spring.datasource.url = jdbc:mysql://localhost:3306/kamis" + dbname;

    bw.write("server.port = 8000");
    bw.write("\n");
    bw.write(dbchanger);
    bw.write("\n");
    bw.write("spring.datasource.username=root");
    bw.write("\n");
    bw.write("spring.datasource.password=");
    bw.write("\n");
    bw.write("spring.datasource.driver-class-name = com.mysql.jdbc.Driver");
    bw.write("\n");
    bw.write("spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect");
    bw.write("\n");
    bw.write("spring.jpa.hibernate.ddl-auto=update");
    bw.write("\n");
    bw.write("spring.devtools.livereload.enabled=true");

    bw.close();
    System.out.println("neu erstellt");

}

This is working in some way so far. The problem is that the application needs an reload to read the changes from my application.properties to connect to the other database which isnt really a solution for me because the application restarts. It also has to be dynamically changeable because i'm working with more than 30 databases.

Maybe you guys can help me figure out how to reload it or maybe give me a more fitting solution approach to my issue.

Thanks in advance. Kai

3 Answers 3

2

You can use the following configuration i have done to use multiple database in spring boot. Application.properties:-

server.port=6060
spring.ds_post.url =jdbc:postgresql://localhost:5432/kode12
spring.ds_post.username =postgres
spring.ds_post.password =root
spring.ds_post.driverClassName=org.postgresql.Driver
spring.ds_mysql.url = jdbc:mysql://localhost:3306/kode12
spring.ds_mysql.username = root
spring.ds_mysql.password = root
spring.ds_mysql.driverClassName=com.mysql.jdbc.Driver

MultipleDBConfig.java

@Configuration
public class MultipleDBConfig {
    @Bean(name = "mysqlDb")
    @ConfigurationProperties(prefix = "spring.ds_mysql")
    public DataSource mysqlDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "mysqlJdbcTemplate")
    public JdbcTemplate jdbcTemplate(@Qualifier("mysqlDb") DataSource dsMySQL) {
        return new JdbcTemplate(dsMySQL);
    }

    @Bean(name = "postgresDb")
    @ConfigurationProperties(prefix = "spring.ds_post")
    public DataSource postgresDataSource() {
        return  DataSourceBuilder.create().build();
    }

    @Bean(name = "postgresJdbcTemplate")
    public JdbcTemplate postgresJdbcTemplate(@Qualifier("postgresDb") 
                                              DataSource dsPostgres) {
        return new JdbcTemplate(dsPostgres);
    }
}

This is the annotated configuration class, containing functions and annotations for loading our PostgreSQL and MySQL configuration. It is also responsible for creating a JDBCTemplate instance for each.

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you for your answer but this is also not very fitting in my case since i'm having 30 databases already. Can't type them all manually because there are more coming on a daily base. Maybe you have another idea.
are you serious. Fine in that scenario also you have to define the database url ,username and password for each of them in application.properties as there is no alternative for that. and also bean you have to define in order to access that database .Still i will look after for some suitable answer and let you know.We will definitely crack this problem. Thanks
yeah sadly im serious :) Thanks for being optimistic since i slowly feel like giving up.
Don't give up we will find solution for it.
0

You can define multiple DataSource objects in your Spring configuration and use desired one.

Mark both data sources as @Bean. Also you need to define two EntitYManagers (each one based on own DataSource).

Read the example for more information

UPDATE: For the case I would implement Proxy DataSource to keep the logic. The idea is to implement DataSource interface which internally can have a Map of DataSources. (Key is e.g. JDBC URL + username)

Initially the map is empty. On the first call we check whether there is an instance in the map for provided user. If not we create a new DataSource instance on fly and place in the map. All the ProxyDataSource methods just get (or create) the delegating user specific DataSource instances and invoke the same method of the user's instance.

You declare @Bean DataSource whcih returns the ProxyDataSource wchich handles the logic internally.

4 Comments

I agree with you there. Maybe i forgot to mention that every user can create a new Database, when creating a new channel for my program and this should be running for around 100-200 people at the same time. The solution you are giving me that i can't write those things for everyone.
Sounds good in the first place. Do you have an example or a site where i can read up in more detail about that? Thanks
No, no site or example. I implemented something like this but cannot share the code. Tried to express the idea.
Thats no problem :) thanks for giving the idea. Im going to make thought on that one.
0

Seems like you could implement a multi-tenant approach, topic I covered in a blog post a few months ago: Multi-tenant applications using Spring Boot, JPA, Hibernate and Postgres

The steps to configure the persistence layer for multitenancy support includes:

  • Hibernate, JPA and datasources properties. Something like:

application.yml

...
multitenancy:
  dvdrental:
    dataSources:
      -
        tenantId: TENANT_01
        url: jdbc:postgresql://172.16.69.133:5432/db_dvdrental
        username: user_dvdrental
        password: changeit
        driverClassName: org.postgresql.Driver
      -
        tenantId: TENANT_02
        url: jdbc:postgresql://172.16.69.133:5532/db_dvdrental
        username: user_dvdrental
        password: changeit
        driverClassName: org.postgresql.Driver
...

I used a properties file to store tenants data but this could be adapted to store tenant information in a kind of master DB.

MultiTenantJpaConfiguration.java

 ...
 @Configuration
 @EnableConfigurationProperties({ MultiTenantDvdRentalProperties.class, JpaProperties.class })
 @ImportResource(locations = { "classpath:applicationContent.xml" })
 @EnableTransactionManagement
 public class MultiTenantJpaConfiguration {

   @Autowired
   private JpaProperties jpaProperties;

   @Autowired
   private MultiTenantDvdRentalProperties multiTenantDvdRentalProperties;
 ...
 }

MultiTenantDvdRentalProperties.java

...
@Configuration
@ConfigurationProperties(prefix = "multitenancy.dvdrental")
public class MultiTenantDvdRentalProperties {

  private List<DataSourceProperties> dataSourcesProps;
  // Getters and Setters

  public static class DataSourceProperties extends org.springframework.boot.autoconfigure.jdbc.DataSourceProperties {

    private String tenantId;
    // Getters and Setters
  }
}
  • Datasources beans

MultiTenantJpaConfiguration.java

 ...
 public class MultiTenantJpaConfiguration {
 ...
   @Bean(name = "dataSourcesDvdRental" )
   public Map<String, DataSource> dataSourcesDvdRental() {
       ...
   }
 ...
 }
  • Entity manager factory bean

MultiTenantJpaConfiguration.java

 ...
 public class MultiTenantJpaConfiguration {
 ...
   @Bean
   public MultiTenantConnectionProvider multiTenantConnectionProvider() {
       ...
   }

   @Bean
   public CurrentTenantIdentifierResolver currentTenantIdentifierResolver() {
       ...
   }

   @Bean
   public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(MultiTenantConnectionProvider multiTenantConnectionProvider,
     CurrentTenantIdentifierResolver currentTenantIdentifierResolver) {
       ...  
   }
 ...
 }
  • Transaction manager bean

MultiTenantJpaConfiguration.java

 ...
 public class MultiTenantJpaConfiguration {
 ...
   @Bean
   public EntityManagerFactory entityManagerFactory(LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
       ...
   }

   @Bean
   public PlatformTransactionManager txManager(EntityManagerFactory entityManagerFactory) {
       ...
   }
 ...
 }
  • Spring Data JPA and transaction support configuration

applicationContent.xml

...
<jpa:repositories base-package="com.asimio.dvdrental.dao" transaction-manager-ref="txManager" />
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true" />
...

ActorDao.java

public interface ActorDao extends JpaRepository<Actor, Integer> {
}

Depending on your needs something like this could be done:

...
@Autowired
private ActorDao actorDao;
...

DvdRentalTenantContext.setTenantId("TENANT_01");
this.actorDao.findOne(...);
...

// Or
DvdRentalTenantContext.setTenantId("TENANT_02");
this.actorDao.save(...);
...

Setting the tenantId could be done in a servlet filter / Spring MVC interceptor / thread that is going to execute the JPA operation, etc.

Again, a detailed explanation and source code could be found at: Multi-tenant applications using Spring Boot, JPA, Hibernate and Postgres

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.