新都在

新都在

基于原生mongoClient构建多数据源

21
2023-02-02
基于原生mongoClient构建多数据源

基于原生mongoClient构建多数据源

看了很多基于java构建多mongo多数据源客户端的文章,发现大部分都是通过SpringbootmongoTemplate去实现的,实现方式和mysq多数据源的方式比较类似。但是这种实现方式如果存在很多数据源的情况下一个是实现文件多,二不好管理,三每次都需要单独去继承config类,太麻烦了。

参考mongoClient官方文档写了一个简单、快速构建mongo多数据源客户端的实现。

本类兼容SpringBoot2.X版本和SpringBoot 1.5.x版本

一、First of all

引入maven依赖,如果原来引入了spring-boot-starter-data-mongodb请去掉,不然会出现包冲突。

<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongodb-driver-sync</artifactId>
    <version>4.0.5</version>
</dependency>

<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongodb-driver-legacy</artifactId>
    <version>4.0.5</version>
</dependency>

二、配置文件

uri里面的值就是每个mongo数据源的名字,名字可以自定义,用于使用mongoClient的时候获取对应的mongodatabase对象

每个uri里面的值和正常配置mongoTemplate的时候是一样的

spring.data.mongodb.uri[test1]=mongodb://localhost:17017/test1_dev
spring.data.mongodb.uri[test2]=mongodb://root:root@localhost:27017,localhost:27018/test2_dev

三、配置文件

import site.cnkj.common.utils.data.MongoClientsInit;
import com.mongodb.MongoClientOptions;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;

/*
 * @author  Carol
 * @create  2021/3/24 10:51
 * @Description
 *  1. 使用本bean的时候需要在启动类上加上下面这行注解,否则会自动链接本地mongo出现异常
 *      <code>@EnableAutoConfiguration(exclude = {MongoAutoConfiguration.class})</code>
 *  2. 使用MongoClient的时候不能同时使用SpringMongo,如果存在SpringMongo的maven依赖需要去除
 */
@Setter
@Configuration
@ConfigurationProperties(prefix = "spring.data.mongodb")
public class MongoClientConfig {

    private HashMap<String, String> uri = new HashMap<>();
    private String applicationName;
    private Boolean retryWrites = true;
    private Boolean retryReads = true;
    private Integer minConnectionsPerHost;
    private Integer maxConnectionsPerHost = 100;
    private Integer serverSelectionTimeout = 1000 * 30;
    private Integer maxWaitTime = 1000 * 60 * 2;
    private Integer maxConnectionIdleTime;
    private Integer maxConnectionLifeTime;
    private Integer connectTimeout = 1000 * 10;
    private Integer socketTimeout = 0;
    private Boolean sslEnabled = false;
    private Boolean sslInvalidHostNameAllowed = false;

    private Integer heartbeatFrequency = 10000;
    private Integer minHeartbeatFrequency = 500;
    private Integer heartbeatConnectTimeout = 20000;
    private Integer heartbeatSocketTimeout = 20000;
    private Integer localThreshold = 15;


    private MongoClientOptions.Builder builder(){
        //配制连接池
        MongoClientOptions.Builder mongoClientBuilder = new MongoClientOptions.Builder();
        mongoClientBuilder.applicationName(applicationName);
        mongoClientBuilder.connectionsPerHost(maxConnectionsPerHost);
        mongoClientBuilder.connectTimeout(connectTimeout);
        mongoClientBuilder.heartbeatConnectTimeout(heartbeatConnectTimeout);
        mongoClientBuilder.heartbeatFrequency(heartbeatFrequency);
        mongoClientBuilder.heartbeatSocketTimeout(heartbeatSocketTimeout);
        mongoClientBuilder.localThreshold(localThreshold);
        mongoClientBuilder.maxConnectionIdleTime(maxConnectionIdleTime);
        mongoClientBuilder.maxConnectionLifeTime(maxConnectionLifeTime);
        mongoClientBuilder.maxWaitTime(maxWaitTime);
        mongoClientBuilder.minConnectionsPerHost(minConnectionsPerHost);
        mongoClientBuilder.minHeartbeatFrequency(minHeartbeatFrequency);
        mongoClientBuilder.retryReads(retryReads);
        mongoClientBuilder.retryWrites(retryWrites);
        mongoClientBuilder.serverSelectionTimeout(serverSelectionTimeout);
        mongoClientBuilder.socketTimeout(socketTimeout);
        mongoClientBuilder.sslEnabled(sslEnabled);
        mongoClientBuilder.sslInvalidHostNameAllowed(sslInvalidHostNameAllowed);
        return mongoClientBuilder;
    }

    @Bean(name = "mongoClientsInit")
    public MongoClientsInit mongoClientsInit(){
        return new MongoClientsInit(uri, builder());
    }

}

四、对象

构建对象,生成bean

import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoClientURI;
import com.mongodb.client.MongoDatabase;

import java.util.HashMap;

/*
 * @author  LXW
 * @create  2020/8/17 9:50
 * @Description
 */
public class MongoClientsInit {

    private HashMap<String, MongoDatabase> mongoClientDatabases = new HashMap<>();

    public HashMap<String, MongoDatabase> getMongoClientDatabases() {
        return mongoClientDatabases;
    }

    private String getDatabase(String mongoUri) throws Exception{
        String databaseName = "";
        String substring = mongoUri.substring(10, mongoUri.length());
        String[] split = substring.split("/");
        String database = split[1];
        if (database.contains("?")){
            databaseName = database.split("\\?")[0];
        }else {
            databaseName = database;
        }
        return databaseName;
    }

    public MongoClientsInit(HashMap<String, String> uri, MongoClientOptions.Builder mongoClientBuilder){
        try {
            init(uri, mongoClientBuilder);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void init(HashMap<String, String> uri, MongoClientOptions.Builder mongoClientBuilder) throws Exception{
        if (uri.size() > 0){
            for (String name : uri.keySet()) {
                String url = uri.get(name);
                //支持ssl连接
                MongoClient mongoClient = new MongoClient(new MongoClientURI(url, mongoClientBuilder));
                MongoDatabase mongoClientDatabase = mongoClient.getDatabase(getDatabase(url));
                mongoClientDatabases.put(name, mongoClientDatabase);
            }
        }
    }
}

五、怎么用

下面以一个带条件的查询作为示例,展示怎么使用这个类

类中的 DateUtil 包在我的github中可以看到

  1. 在启动类中引入配置信息并注入bean
/*
 * @author  LXW
 * @create  2021/3/23 16:31
 * @Description
 */
@ComponentScan(basePackages = "site.cnkj.*",
        basePackageClasses = {
                MongoClientConfig.class

})
@EnableAutoConfiguration(exclude = {MongoAutoConfiguration.class})
@SpringBootApplication
public class TestCommonApplication {

    public static void main(String[] args) {
        try {
            SpringApplication.run(TestCommonApplication.class, args);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Resource(name = "mongoClientsInit")
    private MongoClientsInit mongoClientsInit;

}
  1. 在实现类中使用MongoClientInit
import com.alibaba.fastjson.JSONObject;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/*
 * @author  Carol
 * @create  2020/8/14 17:40
 * @Description
 */
@Service
public class MongoSourceFind {
    
    @Autowired
    private MongoClientsInit mongoClientsInit;
    
    public void find(){
        String startTime = "2020-08-13 00:00:00";
        String endTime = "2020-08-14 00:00:00";
        Date start = DateUtil.dateStringToDate(startTime, DateUtil.DATEFORMATE.FULLTIMEBY_yMdHms);
        Date end = DateUtil.dateStringToDate(endTime, DateUtil.DATEFORMATE.FULLTIMEBY_yMdHms);
        Document document = new Document("lastModifiedTime", new Document("$gte", start).append("$lt", end));
        List<JSONObject> sources = new ArrayList<>();
        //获取你需要的客户端对象
        MongoDatabase mongoDatabase = mongoClientsInit.getMongoClientDatabases().get("test1");
        //查询 collection1 表中 lastModifiedTime 字段在 startTime 和 endTime 时间范围内的数据
        MongoCursor<Document> documentMongoCursor = mongoDatabase.getCollection("collection1").find(document).iterator();
        while (documentMongoCursor.hasNext()){
            //对获取到的每一条数据转换成Json对象
            JSONObject parseObject = JSONObject.parseObject(documentMongoCursor.next().toJson());
            sources.add(parseObject);
        }
        System.out.println(sources);
    }

}