3 months ago

當數據有新舊問題的時候,或是新舊移轉之類的需求,就必須同時存取新舊的表
在 SpringBoot Data Mongo 如何實現配置呢?

build.gradle
buildscript {
    ext {
        springBootVersion = '1.4.3.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

jar {
    baseName = 'convert'
    version = '0.0.1-SNAPSHOT'
}

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile('org.springframework.boot:spring-boot-starter-actuator')
    compile('org.springframework.boot:spring-boot-starter-data-mongodb')
    compileOnly('org.projectlombok:lombok')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

這邊配置了兩個 MongoDB 的數據源,則必須手動配置載入,不能透過 Spring Boot Data Mongo

application.yml
mongodb:

  primary:

    uri: mongodb://samzhu:123456@ds123456.mlab.com:59217/dbname

  secondary:

    uri: mongodb://samzhu:123456@ds123-a0.mlab.com:21971,ds456-a1.mlab.com:21971/dbname

讀入設定參數檔,其實只是要讀 uri 這個參數而已,所以透過原本 org.springframework.boot.autoconfigure.mongo.MongoProperties 來當載體

MultipleMongoProperties.java
@Data
@ConfigurationProperties(prefix = "mongodb")
public class MultipleMongoProperties {
    private MongoProperties primary = new MongoProperties();
    private MongoProperties secondary = new MongoProperties();
}

配置主從資料庫的設定,透過 basePackages 區分主從資料庫對應物件,透過 mongoTemplateRef 聲明 bean

PrimaryMongoConfig.java
@Configuration
@EnableMongoRepositories(basePackages = "com.giish.convert.repositories.main", mongoTemplateRef = PrimaryMongoConfig.MONGO_TEMPLATE)
public class PrimaryMongoConfig {
    public static final String MONGO_TEMPLATE = "primaryMongoTemplate";
}
SecondaryMongoConfig.java
@Configuration
@EnableMongoRepositories(basePackages = "com.giish.convert.repositories.secondary", mongoTemplateRef = SecondaryMongoConfig.MONGO_TEMPLATE)
public class SecondaryMongoConfig {
    protected static final String MONGO_TEMPLATE = "secondaryMongoTemplate";
}

配置 MongoTemplate 跟 MongoDbFactory

MultipleMongoConfig.java
import com.mongodb.MongoClientURI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.mongo.MongoProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;

/**
 * Created by samchu on 2017/1/3.
 */

@Configuration
public class MultipleMongoConfig {
    private MultipleMongoProperties mongoProperties;
        
    // @SuppressWarnings 加這個注解讓IDE 不報: Could not autowire

    @Autowired
    public void setMongoProperties(@SuppressWarnings("SpringJavaAutowiringInspection") MultipleMongoProperties mongoProperties) {
        this.mongoProperties = mongoProperties;
    }

    @Primary
    @Bean(name = PrimaryMongoConfig.MONGO_TEMPLATE)
    public MongoTemplate primaryMongoTemplate() throws Exception {
        return new MongoTemplate(primaryFactory(this.mongoProperties.getPrimary()));
    }

    @Bean
    @Qualifier(SecondaryMongoConfig.MONGO_TEMPLATE)
    public MongoTemplate secondaryMongoTemplate() throws Exception {
        return new MongoTemplate(secondaryFactory(this.mongoProperties.getSecondary()));
    }

    @Bean
    @Primary
    public MongoDbFactory primaryFactory(@SuppressWarnings("SpringJavaAutowiringInspection") MongoProperties mongoProperties) throws Exception {
//        這邊是使用另一種方式連接

//        MongoCredential credential = MongoCredential.createCredential("username", "databasename", "password".toCharArray());

//        ServerAddress serverAdress = new ServerAddress(mongo.getHost(), mongo.getPort());

//        new SimpleMongoDbFactory(new MongoClient(serverAdress, Arrays.asList(credential)), mongo.getDatabase());

        return new SimpleMongoDbFactory(new MongoClientURI(mongoProperties.getUri()));
    }

    @Bean
    public MongoDbFactory secondaryFactory(@SuppressWarnings("SpringJavaAutowiringInspection") MongoProperties mongoProperties) throws Exception {
        return new SimpleMongoDbFactory(new MongoClientURI(mongoProperties.getUri()));
    }
}

啟動程式,前面說過必需自己配置 mongodb 連線,所以這邊我們必須停掉 org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration 的自動配置,以及載入 MultipleMongoProperties

GiishConvertApplication.java
@EnableConfigurationProperties(MultipleMongoProperties.class)
@SpringBootApplication(exclude = MongoAutoConfiguration.class)
public class GiishConvertApplication {
    public static void main(String[] args) {
        SpringApplication.run(GiishConvertApplication.class, args);
    }
}

實際執行效果

ApplicationLoader.java
@RequiredArgsConstructor
@Component
public class ApplicationLoader implements CommandLineRunner {
    //@Autowired

    @NonNull
    private com.giish.convert.repositories.main.UserRep mainUserRep;
    //@Autowired

    @NonNull
    private com.giish.convert.repositories.secondary.OldUserRep secondaryUserRep;

    @Override
    public void run(String... args) throws Exception {
        // 取得主資料庫

        System.out.println(mainUserRep.count());
        // 取得從資料庫

        System.out.println(secondaryUserRep.count());
    }
}

這樣一來只要操作對應的數據物件, spring 就會透過對應的 MongoDbFactory 設定連到正確的 MongoDB 取資料了

參考資料
marcosbarbero/spring-boot-multi-mongo: Multiple mongo connector

← Java NIO read write SpringBoot use liquibase →
 
comments powered by Disqus