SpringBoot 整合mongoDB并自定义连接池的示例代码


Posted in MongoDB onFebruary 28, 2022

得力于SpringBoot的特性,整合mongoDB是很容易的,我们整合mongoDB的目的就是想用它给我们提供的mongoTemplate,它可以很容易的操作mongoDB数据库。

为了自定义连接池,我们在配置类中主要与MongoClientOptions、MongoCredential、MongoClient、MongoDbFactory打交道。最终的目的就是配置好一个MongoDbFactory的bean交由Spring管理。

Maven 依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

配置文件

mongodb:
  database: bfa_mongo
  username: "xxx"
  password: "xxxxx"
  address: "host:port"
  authenticationDatabase: [设置你的认证数据库,如果有的话]
  # 连接池配置
  clientName: ${spring.application.name} # 客户端的标识,用于定位请求来源等
  connectionTimeoutMs: 10000     # TCP连接超时,毫秒
  readTimeoutMs: 15000       # TCP读取超时,毫秒
  poolMaxWaitTimeMs: 3000        #当连接池无可用连接时客户端阻塞等待的时长,单位毫秒
  connectionMaxIdleTimeMs: 60000   #TCP连接闲置时间,单位毫秒
  connectionMaxLifeTimeMs: 120000    #TCP连接最多可以使用多久,单位毫秒
  heartbeatFrequencyMs: 20000      #心跳检测发送频率,单位毫秒
  minHeartbeatFrequencyMs: 8000    #最小的心跳检测发送频率,单位毫秒
  heartbeatConnectionTimeoutMs: 10000  #心跳检测TCP连接超时,单位毫秒
  heartbeatReadTimeoutMs: 15000    #心跳检测TCP连接读取超时,单位毫秒
  connectionsPerHost: 20       # 每个host的TCP连接数
  minConnectionsPerHost: 5     #每个host的最小TCP连接数
  #计算允许多少个线程阻塞等待可用TCP连接时的乘数,算法:threadsAllowedToBlockForConnectionMultiplier*connectionsPerHost,当前配置允许10*20个线程阻塞
  threadsAllowedToBlockForConnectionMultiplier: 10

注意:其中的address参数可以配置为一个数组(代表集群模式)

address: 
    - "host:port"
    - "host2:port2"

MongoConfig配置类

配置类中使用了lombok,如果你没有用lombok依赖和IDE插件,你要重写getter、Setter方法:
代码稍长,可以复制在IDEA中查看:

import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Slf4j
@Configuration
@EnableConfigurationProperties(MongoConfig.MongoClientOptionProperties.class)
public class MongoConfig {

    /**
     * monogo 转换器
     * @return
     */
    @Bean
    public MappingMongoConverter mappingMongoConverter(MongoDbFactory factory,
                                                       MongoMappingContext context, BeanFactory beanFactory, MongoCustomConversions conversions) {
        DbRefResolver dbRefResolver = new DefaultDbRefResolver(factory);
        MappingMongoConverter mappingConverter = new MappingMongoConverter(dbRefResolver, context);
        // remove _class field
//    mappingConverter.setTypeMapper(new DefaultMongoTypeMapper(null));
        mappingConverter.setCustomConversions(conversions);
        return mappingConverter;
    }

    /**
     * 自定义mongo连接池
     * @param properties
     * @return
     */
    @Bean
    public MongoDbFactory mongoDbFactory(MongoClientOptionProperties properties) {
        //创建客户端参数
        MongoClientOptions options = mongoClientOptions(properties);

        //创建客户端和Factory
        List<ServerAddress> serverAddresses = new ArrayList<>();
        for (String address : properties.getAddress()) {
            String[] hostAndPort = address.split(":");
            String host = hostAndPort[0];
            Integer port = Integer.parseInt(hostAndPort[1]);
            ServerAddress serverAddress = new ServerAddress(host, port);
            serverAddresses.add(serverAddress);
        }

        //创建认证客户端
        MongoCredential mongoCredential = MongoCredential.createScramSha1Credential(properties.getUsername(),
                properties.getAuthenticationDatabase() != null ? properties.getAuthenticationDatabase() : properties.getDatabase(),
                properties.getPassword().toCharArray());

        MongoClient mongoClient = new MongoClient(serverAddresses.get(0), mongoCredential, options);
        //集群模式
        if (serverAddresses.size() > 1) {
            mongoClient = new MongoClient(serverAddresses, new ArrayList<>(Arrays.asList(mongoCredential)));
        }
        /** ps: 创建非认证客户端*/
        //MongoClient mongoClient = new MongoClient(serverAddresses, mongoClientOptions);
        return new SimpleMongoDbFactory(mongoClient, properties.getDatabase());
    }

    /**
     * mongo客户端参数配置
     * @return
     */
    public MongoClientOptions mongoClientOptions(MongoClientOptionProperties properties) {
        return MongoClientOptions.builder()
                .connectTimeout(properties.getConnectionTimeoutMs())
                .socketTimeout(properties.getReadTimeoutMs()).applicationName(properties.getClientName())
                .heartbeatConnectTimeout(properties.getHeartbeatConnectionTimeoutMs())
                .heartbeatSocketTimeout(properties.getHeartbeatReadTimeoutMs())
                .heartbeatFrequency(properties.getHeartbeatFrequencyMs())
                .minHeartbeatFrequency(properties.getMinHeartbeatFrequencyMs())
                .maxConnectionIdleTime(properties.getConnectionMaxIdleTimeMs())
                .maxConnectionLifeTime(properties.getConnectionMaxLifeTimeMs())
                .maxWaitTime(properties.getPoolMaxWaitTimeMs())
                .connectionsPerHost(properties.getConnectionsPerHost())
                .threadsAllowedToBlockForConnectionMultiplier(
                        properties.getThreadsAllowedToBlockForConnectionMultiplier())
                .minConnectionsPerHost(properties.getMinConnectionsPerHost()).build();
    }

    @Getter
    @Setter
    @Validated
    @ConfigurationProperties(prefix = "mongodb")
    public static class MongoClientOptionProperties {

        /** 基础连接参数 */
        private String database;
        private String username;
        private String password;
        @NotNull
        private List<String> address;
        private String authenticationDatabase;

        /** 客户端连接池参数 */
        @NotNull
        @Size(min = 1)
        private String clientName;
        /** socket连接超时时间 */
        @Min(value = 1)
        private int connectionTimeoutMs;
        /** socket读取超时时间 */
        @Min(value = 1)
        private int readTimeoutMs;
        /** 连接池获取链接等待时间 */
        @Min(value = 1)
        private int poolMaxWaitTimeMs;
        /** 连接闲置时间 */
        @Min(value = 1)
        private int connectionMaxIdleTimeMs;
        /** 连接最多可以使用多久 */
        @Min(value = 1)
        private int connectionMaxLifeTimeMs;
        /** 心跳检测发送频率 */
        @Min(value = 2000)
        private int heartbeatFrequencyMs;

        /** 最小的心跳检测发送频率 */
        @Min(value = 300)
        private int minHeartbeatFrequencyMs;
        /** 计算允许多少个线程阻塞等待时的乘数,算法:threadsAllowedToBlockForConnectionMultiplier*connectionsPerHost */
        @Min(value = 1)
        private int threadsAllowedToBlockForConnectionMultiplier;
        /** 心跳检测连接超时时间 */
        @Min(value = 200)
        private int heartbeatConnectionTimeoutMs;
        /** 心跳检测读取超时时间 */
        @Min(value = 200)
        private int heartbeatReadTimeoutMs;

        /** 每个host最大连接数 */
        @Min(value = 1)
        private int connectionsPerHost;
        /** 每个host的最小连接数 */
        @Min(value = 1)
        private int minConnectionsPerHost;
    }
}

MappingMongoConverter可以自定义mongo转换器,主要自定义存取mongo数据时的一些操作,例如 mappingConverter.setTypeMapper(new DefaultMongoTypeMapper(null)) 方法会将mongo数据中的_class字段去掉。

最后通过 new SimpleMongoDbFactory(mongoClient, properties.getDatabase())方法配置了一个MongoDbFactory交由Spring管理,Springboot会拿这个MongoDbFactory工厂bean来new一个MongoTemplate,在MongoDbFactoryDependentConfiguration类下可以看到SpringBoot帮你做得事:

/**
 * Configuration for Mongo-related beans that depend on a {@link MongoDbFactory}.
 *
 * @author Andy Wilkinson
 */
@Configuration
@ConditionalOnBean(MongoDbFactory.class)
class MongoDbFactoryDependentConfiguration {

	private final MongoProperties properties;
	MongoDbFactoryDependentConfiguration(MongoProperties properties) {
		this.properties = properties;
	}
    
    //SpringBoot创建MongoTemplate实例
	@Bean
	@ConditionalOnMissingBean
	public MongoTemplate mongoTemplate(MongoDbFactory mongoDbFactory, MongoConverter converter) {
		return new MongoTemplate(mongoDbFactory, converter);
	@ConditionalOnMissingBean(MongoConverter.class)
	public MappingMongoConverter mappingMongoConverter(MongoDbFactory factory, MongoMappingContext context,
			MongoCustomConversions conversions) {
		DbRefResolver dbRefResolver = new DefaultDbRefResolver(factory);
		MappingMongoConverter mappingConverter = new MappingMongoConverter(dbRefResolver, context);
		mappingConverter.setCustomConversions(conversions);
		return mappingConverter;
	
	//...
}

SpringBoot利用我们配置好的MongoDbFactory在配置类中生成一个MongoTemplate,之后我们就可以在项目代码中直接@Autowired了。因为用于生成MongoTemplate的MongoDbFactory是我们自己在MongoConfig配置类中生成的,所以我们自定义的连接池参数也就生效了。

到此这篇关于SpringBoot 整合mongoDB并自定义连接池的文章就介绍到这了,更多相关SpringBoot自定义连接池内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

MongoDB 相关文章推荐
MongoDB数据库的安装步骤
Jun 18 MongoDB
MongoDB 常用的crud操作语句
Jun 20 MongoDB
详解MongoDB的条件查询和排序
Jun 23 MongoDB
常用的MongoDB查询语句的示例代码
Jul 25 MongoDB
mongodb数据库迁移变更的解决方案
Sep 04 MongoDB
mongodb清除连接和日志的正确方法分享
Sep 15 MongoDB
MongoDB使用场景总结
Feb 24 MongoDB
剖析后OpLog订阅MongoDB的数据变更就没那么难了
Feb 24 MongoDB
MongoDB支持的索引类型
Apr 11 MongoDB
Mongodb 迁移数据块的流程介绍分析
Apr 18 MongoDB
详解MongoDB排序时内存大小限制与创建索引的注意事项
May 06 MongoDB
剖析后OpLog订阅MongoDB的数据变更就没那么难了
MongoDB使用场景总结
SpringBoot系列之MongoDB Aggregations用法详解
MongoDB连接数据库并创建数据等使用方法
springboot + mongodb 通过经纬度坐标匹配平面区域的方法
Nov 01 #MongoDB
centos8安装MongoDB的详细过程
关于CentOS 8 搭建MongoDB4.4分片集群的问题
You might like
php中$_SERVER[PHP_SELF] 和 $_SERVER[SCRIPT_NAME]之间的区别
2009/09/05 PHP
用PHP实现小写金额转换大写金额的代码(精确到分)
2012/01/10 PHP
PHP取二进制文件头快速判断文件类型的实现代码
2013/08/05 PHP
详解PHP数据压缩、加解密(pack, unpack)
2016/12/17 PHP
PHP实现单文件、多个单文件、多文件上传函数的封装示例
2019/09/02 PHP
PHP pthreads v3下worker和pool的使用方法示例
2020/02/21 PHP
JS 用6N±1法求素数 实例教程
2009/10/20 Javascript
初学Jquery插件制作 在SageCRM的查询屏幕隐藏部分行的功能
2011/12/26 Javascript
JQuery中DOM事件绑定用法详解
2015/06/13 Javascript
同步文本框内容JS代码实现
2016/08/04 Javascript
AngularJS入门教程之REST和定制服务详解
2016/08/19 Javascript
JS获得一个对象的所有属性和方法实例
2017/02/21 Javascript
详解Vuejs2.0 如何利用proxyTable实现跨域请求
2017/08/03 Javascript
ReactNative Image组件使用详解
2017/08/07 Javascript
Angular5中状态管理的实现
2018/09/03 Javascript
vue2.0 路由模式mode=&quot;history&quot;的作用
2018/10/18 Javascript
vue项目打包之后背景样式丢失的解决方案
2019/01/17 Javascript
js核心基础之闭包的应用实例分析
2019/05/11 Javascript
vue实现直播间点赞飘心效果的示例代码
2019/09/20 Javascript
Element-ui 自带的两种远程搜索(模糊查询)用法讲解
2021/01/29 Javascript
[03:01]DOTA2英雄基础教程 露娜
2014/01/07 DOTA
[43:43]完美世界DOTA2联赛PWL S2 FTD.C vs Rebirth 第一场 11.22
2020/11/24 DOTA
python 实现网上商城,转账,存取款等功能的信用卡系统
2016/07/15 Python
TensorFlow高效读取数据的方法示例
2018/02/06 Python
python如何实现一个刷网页小程序
2018/11/27 Python
Django数据库类库MySQLdb使用详解
2019/04/28 Python
pytorch 实现模型不同层设置不同的学习率方式
2020/01/06 Python
keras多显卡训练方式
2020/06/10 Python
python程序实现BTC(比特币)挖矿的完整代码
2021/01/20 Python
python 第三方库paramiko的常用方式
2021/02/20 Python
哪些情况下不应该使用索引
2015/07/20 面试题
社区学习十八大感想
2014/01/22 职场文书
期末评语大全
2014/05/04 职场文书
2014党委书记四风对照检查材料思想汇报
2014/09/21 职场文书
golang中字符串MD5生成方式总结
2021/07/04 Golang
OpenCV绘制圆端矩形的示例代码
2021/08/30 Python