MongoDB Client 使用核心点

2023年 7月 12日 93.7k 0

首先分享之前的所有文章 , 欢迎点赞收藏转发三连下次一定 >>>> 😜😜😜
文章合集 : 🎁 juejin.cn/post/694164…
Github : 👉 github.com/black-ant
CASE 备份 : 👉 gitee.com/antblack/ca…

一 . 前言

我这边通常的业务场景中,一般把MongoDB当成辅助库来适配一些特定的业务场景,这一篇用于整理快速操作手册,从而减少下次开发时间。

二. 基础使用

一般情况下使用SpringDataMongo 就可以满足绝大多数业务场景,通常有2种解决方式 :

2.1 Repository 模式

如果是单纯的页面CURD,使用 Repository 足够满足必要的操作,API 语法就不细说了,这一套基本上都是一个模式 :

// S1 : 创建对应 DO
@Data
@Document("test_user")
public class UserDO {

    private String username;
}

// S2 : 创建 MongoRepository
@Repository
public interface UserDas extends MongoRepository {
    int deleteByUserName(String username);
}

// S3 : 发起调用
UserDas.deleteByUserName(username);

这一块太简单了,简单记录下。

2.2 MongoTemplate 模式

稍微复杂点的就是 MongoTemplate 模式了 ,配置就不细说了,直接上代码 :

@Resource
private MongoTemplate mongoTemplate;

Bson sortBs = Sorts.descending("createTime");
Bson bson = Filters.eq("userCode", "DemoTest");
FindIterable collect = mongoTemplate.getCollection(COLLECTION).find(bson).limit(10).skip(1).sort(sortBs);



// BSON对象 : 
- BSON是MongoDB使用的内部数据表示格式

也是比较常用的处理方式,MongoTemplate 里面提供了大量的处理方法,为了方便后续输出查询语句,往内部简单看看 ,MongoTemplate 的处理可以分解为以下几步 :

// S1 : 获取 Collection
mongoTemplate.getCollection(COLLECTION)
- 获取 Collect 实际上是获取了一个 Collect 的连接对象
return new MongoCollectionImpl(new MongoNamespace(name, collectionName),
  documentClass, codecRegistry, readPreference,
  writeConcern, retryWrites, retryReads, 
  readConcern, uuidRepresentation, executor);


// S2 : 创建 FindIterable



// S3 : 设置分页
.limit(10).skip(1)
- findOptions.limit(limit);
- findOptions.skip(skip);



// S4 : 定义 sort 
sort 就是一个 Bson

2.3 Filter 的处理方式

MongoDB 的 Filter 是 用于构建查询参数的单元,常见的写法如下 :

Bson bson = Filters.eq("username", "ant-black");

Filter 的目的是生成对应的 BSON ,再通过传入的 BSON 发起查询。这里主要涉及以下 Filter :

// OperatorFilter 主要支持 $ne ,$gt , $lt , $gte ,$lte
return new Filters.OperatorFilter("$ne", fieldName, value);

// IterableOperatorFilter : 用于构建 In 列表查询
return new Filters.IterableOperatorFilter(fieldName, "$in", values);

// 关联查询
- OrNorFilter
- AndFilter
- NotFilter
- OrNorFilter

具体更细的就不看了,这些 Filter 最终都是通过查询关键字构建查询语句。

这种结构在很多查询构建器里面都能看到影子,通过Java可读的构建器构建查询,封装转变过程。而MongoDB 的 Filter 对比其他的一大特点就是结构很清晰,没有乱七八糟的常量类和枚举,点进去就能直观的看到查询参数。

2.4 瞅一眼连接池

这里涉及到 MongoDB 的连接创建方式 ,MongoDB Client 中通过 MongoClientDelegate 方法进行连接的创建 :

// 连接池配置
public class ConnectionPoolSettings {
    private final List connectionPoolListeners;
    private final int maxSize;
    private final int minSize;
    private final long maxWaitTimeMS;
    private final long maxConnectionLifeTimeMS;
    private final long maxConnectionIdleTimeMS;
    private final long maintenanceInitialDelayMS;
    private final long maintenanceFrequencyMS; 
}

MongDB 的连接池类叫 DefaultConnectionPool ,其中的连接单元叫 PooledConnection。

这一块没细读,后面会单独学习一下连接池的写法。

2.5 最终数据的发送

最终数据发送是在 DefaultServerConnection 实现类中进行的 ,该类有以下几个核心的方法 :

// command(): 基本命令接口
- String database :对应数据库
- BsonDocument :对应的 BSON 命令
{
  "find": "userTable",
  "filter": {
    "username": "test"
  },
  "sort": {
    "createTime": -1
  },
  "skip": 1,
  "limit": 10
}

FieldNameValidator : 字段校验
ReadPreference readPreference : 读偏好
Decoder commandResultDecoder
SessionContext  : 

最终在命令执行类 CommandProtocolImpl 中被调用

public T execute(InternalConnection connection) {
    return connection.sendAndReceive(this.getCommandMessage(connection), this.commandResultDecoder, this.sessionContext);
}

// 注意 : 这里的 connection 就是 DefaultConnectionPool$PooledConnection

// 连接发起消息
this.wrapped.sendAndReceive(message, decoder, sessionContext)

三 . 学会怎么配置

使用一个组件中比较麻烦的一点就是不知道配置怎么处理的,通常Spring的 AutoConfiguration 在官网上都能找到配置方式 :

3.1 配置写法

@Configuration
@EnableConfigurationProperties
public class MongoConfig {

    private String database = "dp...";
    private String localhost = ".....";
    private int port = ....;
    private String username = "....";
    private String password = "123456";

    @Bean
    public MongoTemplate mongoTemplate(MongoDatabaseFactory mongoDatabaseFactory) {
        MongoTemplate mongoTemplate = new MongoTemplate(mongoDatabaseFactory);
        mongoTemplate.setReadPreference(ReadPreference.secondaryPreferred());
        return mongoTemplate;
    }

    @Bean
    public MappingMongoConverter mappingMongoConverter(MongoDatabaseFactory factory, MongoMappingContext context,
                                                       MongoCustomConversions conversions) {
        DbRefResolver dbRefResolver = new DefaultDbRefResolver(factory);
        MappingMongoConverter mappingConverter = new MappingMongoConverter(dbRefResolver, context);
        mappingConverter.setCustomConversions(conversions);
        // remove _class field
        mappingConverter.setTypeMapper(new DefaultMongoTypeMapper(null));
        return mappingConverter;
    }

    @Bean
    public MongoDatabaseFactory mongoDbFactory() {
        // 准备 Builder 对象用于构建配置
        MongoClientSettings.Builder builder = MongoClientSettings.builder();

        // 设置服务器地址
        builder.applyToClusterSettings(clusterSettingsBuilder ->
                clusterSettingsBuilder.hosts(Arrays.asList(new ServerAddress(localhost, port))));

        // 设置认证信息
        builder.credential(MongoCredential.createCredential(username, database, password.toCharArray()));

        // 设置其他选项,如连接超时、读写超时等
        builder.applyToSocketSettings(socketSettingsBuilder ->
                socketSettingsBuilder
                        .connectTimeout(5000, TimeUnit.SECONDS)
                        .readTimeout(5000, TimeUnit.SECONDS));

        // 设置连接池的信息
        builder.applyToConnectionPoolSettings(connectPoolBuilder -> {
            connectPoolBuilder
                    .maxSize(20);
        });

        // 创建MongoClientSettings对象
        MongoClientSettings settings = builder.build();

        // 创建MongoClient客户端
        MongoClient client = MongoClients.create(settings);
        return new SimpleMongoClientDatabaseFactory(client, database);
    }


}

3.2 关于怎么写配置类

之前写了一个方法论 > juejin.cn/post/725087… , 这里就基于这个思路讲讲当版本变化后,怎么进行 MongoDB 的配置 :

  • S1 : 确定入口,MongoDB 的入口就是 MongoTeplate , 这个一般情况下不会再有变动了
  • S2 : MongoTemplate 中东西比较多,可以设置读偏好,写级别,索引类,连接工厂等等, 找到自己需要配置的类
  • S3 : 通常配置的类为接口,找到对应的实现类进行配置即可

MongoTemplate 里面有哪些东西

  • MongoDatabaseFactory : 最核心的数据库工厂,用于管理数据库连接
  • MongoConverter : 用于转换 Java 对象和 MongoDB 文档
  • ReadPreference : 读偏好,用于进行读写分离,和节点优先读排序
  • WriteConcern : 用于指定写操作的确认级别,如ACKNOWLEDGED、UNACKNOWLEDGED等
  • QueryMapper : 查询映射器,用于将 Query 查询对象转换为 MongoDB 查询文档
  • EntityCallbacks : 可以在实体类创建时进行额外的定制化逻辑
  • MongoPersistentEntityIndexCreator : 索引创建器,用于定制化索引的创建逻辑
  • XXXOperation : 对应具体操作的实现,比如 QueryOperations 可以进行查询条件的创建,构建复杂的查询

尽管随着代码迭代,这些可能会过时,但是涉及到的组件应该是差不多的

四. 复杂需求

4.1 ReadPreference 读偏好是什么 ?

  • 一个副本集由一个主节点(Primary) 和多个副本节点 (Secondary)组成 , 以及可选的仲裁节点 (Arbiter)
  • 主节点处理所有写入操作和读取请求
  • 副本节点可以根据配置的读偏好 来决定自己读取的方式
    • Primary : 默认模式,直接读取主节点
    • PrimaryPreferred : 主节点不可用时,选择从节点读取
    • Secondary : 只从从节点读取
    • SecondaryPreferred : 尽量从从节点读取,如果找不到,则从主节点读取
    • Nearest : 根据Ping值高低,选择性的读取

这里对应的配置就是:

private static final ReadPreference PRIMARY = new ReadPreference.PrimaryReadPreference((SyntheticClass_1)null);
private static final ReadPreference SECONDARY = new SecondaryReadPreference();
private static final ReadPreference SECONDARY_PREFERRED = new SecondaryPreferredReadPreference();
private static final ReadPreference PRIMARY_PREFERRED = new PrimaryPreferredReadPreference();
private static final ReadPreference NEAREST = new NearestReadPreference();

// MongoTemplate 中进行配置即可
MongoTemplate mongoTemplate = new MongoTemplate(mongoDatabaseFactory);
mongoTemplate.setReadPreference(ReadPreference.secondaryPreferred());

ReadPreference readPreference = ReadPreference.secondary();
mongoTemplate.setReadPreference(readPreference);

4.2 WriteConcern 是什么

MongoDB 的写操作的确认级别和错误处理。用来保证数据的一致性和持久性。

// 写操作在主节点上完成后被确认为成功
public static final WriteConcern ACKNOWLEDGED = new WriteConcern((Object)null, (Integer)null, (Boolean)null);
// 写操作在主节点和至少一个副本集成员上完成后被确认为成功
public static final WriteConcern W1 = new WriteConcern(1);
// 写操作在主节点和指定数量的副本集成员上完成后被确认为成功
public static final WriteConcern W2 = new WriteConcern(2);
public static final WriteConcern W3 = new WriteConcern(3);
// 写操作在发送到服务器后不会等待任何确认
public static final WriteConcern UNACKNOWLEDGED = new WriteConcern(0);
// 写操作在持久化到日志文件(journal)后才被确认为成功
public static final WriteConcern JOURNALED;
// 写操作在主节点和大多数(超过半数)副本集成员上完成后被确认为成功
public static final WriteConcern MAJORITY;

WriteConcern writeConcern = WriteConcern.MAJORITY.withWTimeout(5000,TimeUnit.SECONDS);
mongoTemplate.setWriteConcern(writeConcern);

总结

基本上以上的知识点覆盖了一个开发的客户端需求。当然从服务端角度还涉及到服务的维系,优化等等操作,这个后续深度了解。

后续有时间就会开始深入索引使用了,这一块之前也积累了一些,但是比较凌乱,得看看资料才能弄清楚。

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论