spring data mongodb配置+月庫實現

FonLin發表於2018-06-29

基礎的東西不談,先說思路。

我們跟蹤原始碼可以發現每次查詢時都會在MongoTemplate中去獲取MongoDatabase例項,大概是這樣:

FindIterable<Document> iterable = collectionCallback
						.doInCollection(getAndPrepareCollection(getDb(), collectionName));
複製程式碼

其中getDb()是由工廠類MongoDbFactory實現的。那麼很簡單了,我們只需要寫一個自己的MongoDbFactory配置進來,來實現“預設是當前月庫,也可以手動選擇月庫”的效果。

上程式碼,首先是MongoDbFactory

/**
 * 繼承 {@link SimpleMongoDbFactory },重寫{@code getDB}方法,新增月庫實現
 *
 * @author fonlin
 * @date 2018/6/28
 */
public class MonthlyMongoDbFactory extends SimpleMongoDbFactory {

    /**
     * 沒有月庫字尾的資料庫名
     */
    private String databaseName;

    public MonthlyMongoDbFactory(MongoClientURI uri) {
        super(uri);
    }

    public MonthlyMongoDbFactory(MongoClient mongoClient, String databaseName) {
        super(mongoClient, databaseName);
        this.databaseName = databaseName;
    }

    public MongoDatabase getDb() throws DataAccessException {
        //獲取當前ThreadLocal中的month
        String month = MonthSelector.getAndRemove();
        //如果沒有,則設定當前月
        if (StringUtils.isEmpty(month)) {
            month = LocalDateTime.now().format(DateTimeFormatter.ofPattern(Constants.MONTH_PATTERN));
        }
        return getDbByMonth(month);
    }

    private MongoDatabase getDbByMonth(String month) throws DataAccessException {

        Assert.hasText(month, "month must not be empty.");
        //拼出test_201806
        String name = this.databaseName + "_" + month;
        //呼叫父類
        return super.getDb(name);
    }

}
複製程式碼

很簡單,繼承預設實現SimpleMongoDbFactory,重寫getDb()方法加入我們自己的邏輯,最終還是呼叫父類的getDb(String dbName)方法。注意MonthSelector,這是利用ThreadLocal實現的手動選擇月份策略,用法大概就是這樣子:

MonthSelector.set("201805");
List<User> users = userDao.findAll();
複製程式碼

這裡把MonthSelector粗略程式碼貼一下,不太懂的朋友們要去看下ThreadLocal原理

/**
 * @author fonlin
 * @date 2018/6/28
 */
public class MonthSelector {

    private static final ThreadLocal<String> LOCAL_MONTH = new ThreadLocal<>();

    public static String getAndRemove() {
        String month = LOCAL_MONTH.get();
        LOCAL_MONTH.remove();
        return month;
    }

    public static void set(String month) {
        Assert.notNull(month, "month must not be null");
        LOCAL_MONTH.set(month);
    }

    public static String get() {
        return LOCAL_MONTH.get();
    }
}

複製程式碼

最後是springboot整合mongodb的java config:

@Configuration
//必須要禁用spring boot的自動配置
@EnableAutoConfiguration(exclude={MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})
@EnableConfigurationProperties(MongoProperties.class)
//啟用@Repository註解,並且設定dao掃描包
@EnableMongoRepositories(basePackages = "com.fonlin.cloudmanager.dao.mongodb")
public class MongoDbConfig extends AbstractMongoConfiguration {

    private final MongoProperties properties;

    private final MongoClientFactory factory;

    private final MongoClientOptions options;

    private MongoClient mongo;

    public MongoDbConfig(MongoProperties properties,
                         ObjectProvider<MongoClientOptions> options, Environment environment) {
        this.properties = properties;
        this.options = options.getIfAvailable();
        this.factory = new MongoClientFactory(properties, environment);
    }

    @Bean
    public MongoDbFactory mongoDbFactory() {
        //這裡new我們自己的MongoDbFactory即可
        return new MonthlyMongoDbFactory(mongoClient(), getDatabaseName());
    }

    @PreDestroy
    public void close() {
        if (this.mongo != null) {
            this.mongo.close();
        }
    }

    @Override
    public MongoClient mongoClient() {
        this.mongo = this.factory.createMongoClient(this.options);
        return this.mongo;
    }

    @Override
    protected String getDatabaseName() {
        return this.properties.getDatabase();
    }
}
複製程式碼

到此配置就結束了,使用的話就像jpa Repository一樣使用即可。

相關文章