首先分享之前的所有文章 , 欢迎点赞收藏转发三连下次一定 >>>> 😜😜😜
文章合集 : 🎁 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);
总结
基本上以上的知识点覆盖了一个开发的客户端需求。当然从服务端角度还涉及到服务的维系,优化等等操作,这个后续深度了解。
后续有时间就会开始深入索引使用了,这一块之前也积累了一些,但是比较凌乱,得看看资料才能弄清楚。