pom

因为项目中采用Morphia(MongoDBODM框架,对象-文档映射(object-document mapper)),因此需要在pom文件中引入相应依赖:

        <dependency>
<groupId>dev.morphia.morphia</groupId>
<artifactId>core</artifactId>
<version>1.5.3</version>
</dependency>

因为Morphia依赖于mongo-java-driver,因此无需在pom文件中继续导入mongo-java-driver.

配置

(1) 配置文件

application.yaml中配置如下所示:

  data:
mongodb:
database: {数据库名称}
uri: mongodb://{用户名}:{密码}@{服务器地址}:27017/{数据库名称}

(2) 代码配置

@Data
@Configuration
public class MorphiaConfig { private MongoClient mongoClient; /**
* 设置连接最大空闲时间(到达时间,连接关闭)
* @return mongo client属性
*/
@Bean
public MongoClientOptions mongoClientOptions() {
return MongoClientOptions.builder()
.maxConnectionIdleTime(6000 * 5)
.maxConnectionLifeTime(0)
.build();
} @Autowired
public MorphiaConfig(MongoClient mongoClient) {
this.mongoClient = mongoClient;
} @Bean
public Datastore datastore(@Autowired MongoClient mongoClient) {
Morphia res = new Morphia();
// 确定Mongo实体类的存放包名
res.mapPackage("com.test.log.entity");
Datastore datastore = res.createDatastore(mongoClient, "ops");
// 为实体类创建索引
datastore.ensureIndexes();
return datastore;
}
}

代码中无需自行配置MongoClient(Spring boot会自动完成MongoClient的初始化),当然也允许自行设置Mongo的连接参数.

实体类

项目中需要存放日志,并且通过MongoDBTTL特性设置过期时间,以便通过MongoDB完成日志删除任务.

@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
// 必须添加,以便Morphia识别此类为`MongoDB`的实体类,`logInfo`是表名
@Entity(value = "logInfo", noClassnameStored = true)
@Indexes({
// expireTime存放日志过期时间,创建TTL index
@Index(fields = @Field(value = "expireTime"), options = @IndexOptions(expireAfterSeconds = 0)),
// 创建复合索引
@Index(fields = {@Field("deviceId"), @Field("module"), @Field(value = "time", type = DESC)}),
@Index(fields = {@Field("deviceId"), @Field(value = "time", type = DESC)})
})
public class LogInfo {
@Id
private ObjectId id;
private Date time;
@JsonIgnore
private Date expireTime;
private String module;
private String level;
private String deviceId;
private String msg;
}

日志的存放和读取是在不同的项目完成,在查询MongoDB时遇到反序列化问题,异常如下所示:

WARN  dev.morphia.mapping.DefaultCreator - Class not found defined in dbObj:

查询源码分析,是因为Morphia在存储数据的时候,会将实体类名称存入数据库中.

查询时Morphia根据类名查找相应的实体类并进行反序列化.

因为不同的项目中,实体类的包名不一致导致出现以上错误.

解决方案比较简单,通过注解告知Morphia存储数据时不要存储包名即可,具体如下所示:

`@Entity(value = "logInfo", noClassnameStored = true)`.

CRUD操作

(1) 存储操作

    LogInfo logInfo = LogInfo.builder()
.time(dateTime)
.expireTime(dateExpireTime)
.level(level)
.module(module)
.deviceId(deviceId)
.msg(msg)
.build();
try {
//插入日志到mongoDB
datastore.save(logInfo);
} catch (RuntimeException e) {
log.error(AppStatus.IDB_WRITE_FAIL.getError(), e);
}

(2) 查询操作

    final Query<LogInfo> query = datastore.createQuery(LogInfo.class).filter("deviceId = ",     request.getDeviceId());
// 列表查询
if (!CollectionUtils.isEmpty(request.getModules())) {
query.filter("module in ", request.getModules().toArray());
}
// 根据时间查询,使用filter就不太恰当了
if (null != request.getStartTime()) {
query.field("time").greaterThanOrEq(request.getStartTime());
}
if (null != request.getFinishTime()) {
query.field("time").lessThanOrEq(request.getFinishTime());
} // 查询记录总数
int count = query.count()); // 设置排序规则,查询具体数据
query.order(Sort.descending("time"));
// 获取数据游标
MongoCursor<LogInfo> logInfoMongoCursor = query.find(
new FindOptions()
.skip((request.getPageNum() - 1) * request.getPageSize())
.limit(request.getPageSize())
);
// 通过数据游标,逐个获取数据记录
while (logInfoMongoCursor.hasNext()) {
logInfoPage.add(logInfoMongoCursor.next());
}
05-11 16:15