microsoft / spring-data-cosmosdb

Access data with Azure Cosmos DB
MIT License
94 stars 64 forks source link

Use Multiple DataSource(With CosmosDB) In Spring boot JPA #403

Closed meantiger closed 5 years ago

meantiger commented 5 years ago

In formal case If I want to use multiple datasource in Spring boot with JPA (For Example, Use Mysql For UserInfo & Use h2 for merchandise data)

I set the properties & just make two Kinds of Config like this

@Configuration @EnableTransactionManagement @EnableJpaRepositories( basePackages = {"====.repository"}) public class CustomerDataSourceConfig {

@Primary @Bean(name = "dataSource") @ConfigurationProperties(prefix = "====.datasource") public DataSource dataSource() { return DataSourceBuilder.create().build(); }

@Primary @Bean(name = "entityManagerFactory") public LocalContainerEntityManagerFactoryBean entityManagerFactory( EntityManagerFactoryBuilder builder, @Qualifier("dataSource") DataSource dataSource) { return builder .dataSource(dataSource) .packages("=====.domain") .persistenceUnit("customer") .build(); }

@Primary @Bean(name = "transactionManager") public PlatformTransactionManager transactionManager( @Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory) { return new JpaTransactionManager(entityManagerFactory); } }

But If I want to use Multiple DataSource(With CosmosDB) this kind of work dosen't work anymore. I should make different kind of config? or Is there any way I can use multiple datasource with cosmosDB? (ex. Use Mysql For UserInfo & Use CosmosDB for merchandise data)

thx.

saragluna commented 5 years ago

@meantiger Do you mean multi-store?

As I understand you could create separate configs for them if you want to use JPA with CosmosDB.

@Configuration
@EnableJpaRepositories(entityManagerFactoryRef = "customerEntityManagerFactory",
transactionManagerRef = "customerTransactionManager", 
basePackages = "xxx.xxx.your-jpa-package")
@EnableTransactionManagement
public class ConifgA {

  @Bean
  PlatformTransactionManager customerTransactionManager() { }

  @Bean
  LocalContainerEntityManagerFactoryBean customerEntityManagerFactory() { }

  @Bean
  DataSource customerDataSource() { }
}
@Configuration
@EnableDocumentDbRepositories(basePackages = "xxx.xxx.your-cosmosdb-package")
@EnableConfigurationProperties(DocumentDbProperties.class)
public class ConfigB extends AbstractDocumentDbConfiguration {

    @Autowired
    private DocumentDbProperties properties;

    @Override
    public DocumentDBConfig getConfig() {
        return DocumentDBConfig.builder(properties.getUri(), 
                                        properties.getKey(),
                                        properties.getDatabase()).build();
    }
}
meantiger commented 5 years ago

@saragluna First of all Thx U for comment. Yes. As U mentioned I try multi-store. And I did what U said. However unfortunately It's not working well.........

So, I upload my details src.

First I import this spring-data-jpa

org.springframework.data spring-data-jpa

and my application.properties are like this

Specify the DNS URI of your Azure Cosmos DB.

azure.cosmosdb.uri=(DBURI)
# Specify the access key for your database.
azure.cosmosdb.key=(DBKEY)
# Specify the name of your database.
azure.cosmosdb.database=(DBNAME)
# Specify the information of your user DB.
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.jdbc-url=jdbc:mysql:(DBURI)
spring.datasource.username=(DBusername)
spring.datasource.password=(DBuserpassword)

and my code is like this I think the DocumentDBConfig works well that I just write exactly same as U. However the mysqlConfig.... I constuct your code like this.

@primary
@bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder, @qualifier("dataSource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("=====.domain")
.persistenceUnit("customer")
.build();
}

However Could not autowire. no beans of EntityManagerFactoryBuilder error popup. this kind of error will be fine when I use spring-boot-starter-data-jpa this. but I use that it cannot connect with cosmos and mysql. I guess I'm doing something wrong...... do I made wrong config code?

*PS thx u for your help.

saragluna commented 5 years ago

@meantiger If you don't want to customize EntityManager or TransactionManager, you can simply remove these beans:

 @Bean
 PlatformTransactionManager customerTransactionManager() { }

 @Bean
 LocalContainerEntityManagerFactoryBean customerEntityManagerFactory() { }

 @Bean
 DataSource customerDataSource() { }

If you have to customize EntityManager and since you're using spring-data-jpa, you can customize the bean like this:

  @Bean 
  LocalContainerEntityManagerFactoryBean customerEntityManagerFactory(DataSource dataSource) {
      HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
      jpaVendorAdapter.setGenerateDdl(true);

      LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
      factoryBean.setDataSource(dataSource);
      factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
      factoryBean.setPackagesToScan(ConfigB.class.getPackage().getName());
      return factoryBean;
  }
meantiger commented 5 years ago

@saragluna Really thank U again. and I hope This is my Last Question. Below is My Code. ` @Configuration @EnableJpaRepositories(entityManagerFactoryRef = "customerEntityManagerFactory", transactionManagerRef = "customerTransactionManager", basePackages = "com.sktelecom.iottelemetry.User") @EnableTransactionManagement public class MySQLConfig {

@Autowired
private Environment env;

@Primary
@Bean(name = "customerTransactionManager")
PlatformTransactionManager customerTransactionManager(@Qualifier("customerEntityManagerFactory") EntityManagerFactory entityManagerFactory ){
    return new JpaTransactionManager(entityManagerFactory);
}

@Primary
@Bean(name = "customerEntityManagerFactory")
LocalContainerEntityManagerFactoryBean customerEntityManagerFactory(DataSource dataSource) {
    HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
    jpaVendorAdapter.setGenerateDdl(true);

    LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
    factoryBean.setDataSource(dataSource);
    factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
    factoryBean.setPackagesToScan(MySQLConfig.class.getPackage().getName());
    return factoryBean;
}

@Primary
@Bean(name = "datasource")
DataSource customerDataSource() {
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName(env.getProperty("spring.datasource.driver-class-name"));
    dataSource.setUrl(env.getProperty("spring.datasource.url"));
    dataSource.setUsername(env.getProperty("spring.datasource.username"));
    dataSource.setPassword(env.getProperty("spring.datasource.password"));
    return dataSource;
}

} `

This is my MySQL Config code. and My properties are Like this

azure.cosmosdb.uri=(MY DB URI) azure.cosmosdb.key=(MYDB KEY) azure.cosmosdb.database=(MYDBNAME)

spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.jdbc-url=(MY DB URL) spring.datasource.username=(MY DB USERNAME) spring.datasource.password=(MY DB PASSWORD)

Howerver This kind of error still occur.

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customerEntityManagerFactory' defined in class path resource [com/------/--------/Config/MySQLConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean]: Factory method 'customerEntityManagerFactory' threw exception; nested exception is java.lang.NoClassDefFoundError: org/hibernate/Session

Can U teach me how can I make whole MySQL config code?

Thx U For your kindness.

saragluna commented 5 years ago

@meantiger I'm thinking is it possible for you to create a minimal project so I can help to find the problem?

meantiger commented 5 years ago

@saragluna THX. https://github.com/meantiger/iot-device-telemetry This is my minimal project.

Thx U.

saragluna commented 5 years ago

@meantiger after test CosmosDB can work with multi-store, you could check it with your repo PR. Close this issue.

meantiger commented 5 years ago

@saragluna It's Work! and I merge it my real project. and It work well too. Sincerely Thx U.

*PS If you do not mind, can I suggest one thing?

There are a lot of people who want to use cosmos DB & sql DB in the same time like me. Cuz. CosmosDB is expensive and in process sometimes we need sql DB. And this kind of config is different from common multi-source database use.

In common case should make two kind of config like my code.

So, I think It would be really great make cosmos config guide and this kind of case guide upload in azure-cosmos document or starting guide.

bajrangprasad01 commented 4 years ago

I have tried to connect with 2 datasource of cosmos but not able to connect. I have tried all the solution given but not a single thing is working.

@Configuration @EnableCosmosRepositories(basePackages = "com.bajrang.repositories.kooprepositories") public class KoopCosmosConfig extends AbstractCosmosConfiguration { @Value("${spring.koop.azure.cosmosdb.uri}") private String uri; @Value("${spring.koop.azure.cosmosdb.key}") private String key; @Value("${spring.koop.azure.cosmosdb.database}") private String database;

@Bean
@Primary
public CosmosDBConfig koopCosmosDBConfig() {
    CosmosDBConfig cosmosDBConfig = CosmosDBConfig.builder(uri, key, database).build();
    return cosmosDBConfig;
}

===================================

@Configuration @EnableTransactionManagement @EnableCosmosRepositories(basePackages = "com.bajrang.repositories.galactusrepositories") public class GalactusCosmosConfig extends AbstractCosmosConfiguration { @Value("${spring.galactus.azure.cosmosdb.uri}") private String uri; @Value("${spring.galactus.azure.cosmosdb.key}") private String key; @Value("${spring.galactus.azure.cosmosdb.database}") private String database;

@Bean
public CosmosDBConfig galactusCosmosDBConfig() {
    CosmosDBConfig cosmosDBConfig = CosmosDBConfig.builder(uri, key, database).build();
    return cosmosDBConfig;
}

This error is comes

Parameter 0 of method cosmosTemplate in com.microsoft.azure.spring.data.cosmosdb.config.AbstractCosmosConfiguration required a single bean, but 2 were found:

Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

Process finished with exit code 1

I have tried with this configuration but not able to connect with two data source.

Please help me I urgently need to do this but I am not able to do. I need to connect with 2 databases but it is not happening.

saragluna commented 4 years ago

@bajrangprasad01

AFAIK the current implementation doesn't support multiple databases. @kushagraThapar could help provide more info on this.

bajrangprasad01 commented 4 years ago

@bajrangprasad01

AFAIK the current implementation doesn't support multiple databases. @kushagraThapar could help provide more info on this.

Thank You for the information.

kushagraThapar commented 4 years ago

@bajrangprasad01 - This will be supported in upcoming spring-data-cosmosdb release train v3.x.x

moabduikea commented 2 years ago

Please could share with me the final solution for this issue @meantiger

kushagraThapar commented 2 years ago

@moabduikea - here you go - https://docs.microsoft.com/en-us/azure/developer/java/spring-framework/how-to-guides-spring-data-cosmosdb#multi-database-configuration

moabduikea commented 2 years ago

I need to use cosmos and spring jpa for mssql in the same project @kushagraThapar Could please give me with headlines how to do that and if you have a POC that should be awesome and thank you a lot for your reply.

kushagraThapar commented 2 years ago

In that case, I think you just need to use one single cosmos db account and map it through your application.properties files. Unfortunately we don't have a POC for the same, however, implementing it should be straightforward. If you can give it a try, I can help out with any issues that you may face.

meantiger commented 2 years ago

@moabduikea sorry for late. I'm not sure. cuz this project is so old to me. Use spring boot.

pom.xml

            <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.microsoft.azure</groupId>
            <artifactId>azure-cosmosdb-spring-boot-starter</artifactId>
            <version>2.0.13</version>
        </dependency>

CosmosConfig.java

package com.test.iottelemetry.Config;

import com.microsoft.azure.spring.autoconfigure.cosmosdb.DocumentDBProperties;
import com.microsoft.azure.spring.data.cosmosdb.config.AbstractDocumentDbConfiguration;
import com.microsoft.azure.spring.data.cosmosdb.config.DocumentDBConfig;
import com.microsoft.azure.spring.data.cosmosdb.repository.config.EnableDocumentDbRepositories;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableDocumentDbRepositories(basePackages = "com.test.iottelemetry.devicetelemetry")
@EnableConfigurationProperties(DocumentDBProperties.class)
public class CosmosConfig extends AbstractDocumentDbConfiguration {
    @Autowired
    private DocumentDBProperties properties;

    @Override
    public DocumentDBConfig getConfig() {
        return DocumentDBConfig.builder(properties.getUri(),
                properties.getKey(),
                properties.getDatabase()).build();
    }
}

MySQLConfig.java

package com.test.iottelemetry.Config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableJpaRepositories(
        basePackages = "com.test.iottelemetry.User")
@EnableTransactionManagement
public class MySQLConfig {
}

in this case. I use UserInfo in MySQL and iottelemetry in CosmosDB So in UserInfo class use Entity annotation and in Iottelemetry class use Document annotation I hope It will help U.