老黄牛金丝猴 發表於 2019-6-21 17:20:00

Spring boot配置MongoDB以及Morphia踩坑记录

<h1 id="pom">pom</h1>
<p>因为项目中采用<code>Morphia</code>(<code>MongoDB</code>的<code>ODM</code>框架,对象-文档映射(<code>object-document mapper</code>)),因此需要在<code>pom</code>文件中引入相应依赖:</p>
<pre><code class="language-shell">      &lt;dependency&gt;
            &lt;groupId&gt;dev.morphia.morphia&lt;/groupId&gt;
            &lt;artifactId&gt;core&lt;/artifactId&gt;
            &lt;version&gt;1.5.3&lt;/version&gt;
      &lt;/dependency&gt;
</code></pre>
<p>因为<code>Morphia</code>依赖于<code>mongo-java-driver</code>,因此无需在<code>pom</code>文件中继续导入<code>mongo-java-driver</code>.</p>
<h1 id="配置">配置</h1>
<p>(1) 配置文件</p>
<p><code>application.yaml</code>中配置如下所示:</p>
<pre><code class="language-shell">data:
    mongodb:
      database: {数据库名称}
      uri: mongodb://{用户名}:{密码}@{服务器地址}:27017/{数据库名称}
</code></pre>
<blockquote>
<p>上述配置中,<code>{}</code>包围的地方请按照各自项目实际情况填写.</p>
</blockquote>
<p>(2) 代码配置</p>
<pre><code class="language-java">@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;
    }
}
</code></pre>
<p>代码中无需自行配置<code>MongoClient</code>(<code>Spring boot</code>会自动完成<code>MongoClient</code>的初始化),当然也允许自行设置<code>Mongo</code>的连接参数.</p>
<h1 id="实体类">实体类</h1>
<p>项目中需要存放日志,并且通过<code>MongoDB</code>的<code>TTL</code>特性设置过期时间,以便通过<code>MongoDB</code>完成日志删除任务.</p>
<pre><code class="language-java">@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;
}
</code></pre>
<p>日志的存放和读取是在不同的项目完成,在查询<code>MongoDB</code>时遇到反序列化问题,异常如下所示:</p>
<pre><code class="language-shell">WARNdev.morphia.mapping.DefaultCreator - Class not found defined in dbObj:
</code></pre>
<p>查询源码分析,是因为<code>Morphia</code>在存储数据的时候,会将<code>实体类</code>的<code>名称</code>存入数据库中.<br>
查询时<code>Morphia</code>根据<code>类名</code>查找相应的<code>实体类</code>并进行<code>反序列化</code>.<br>
因为不同的项目中,实体类的包名不一致导致出现以上错误.<br>
解决方案比较简单,通过注解告知<code>Morphia</code>存储数据时不要存储包名即可,具体如下所示:</p>
<pre><code class="language-java">`@Entity(value = "logInfo", noClassnameStored = true)`.
</code></pre>
<h1 id="crud操作"><code>CRUD</code>操作</h1>
<p>(1) 存储操作</p>
<pre><code class="language-java">    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);
    }
</code></pre>
<p>(2) 查询操作</p>
<pre><code class="language-java">    final Query&lt;LogInfo&gt; 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&lt;LogInfo&gt; logInfoMongoCursor = query.find(
            new FindOptions()
                .skip((request.getPageNum() - 1) * request.getPageSize())
                .limit(request.getPageSize())
      );
    // 通过数据游标,逐个获取数据记录
    while (logInfoMongoCursor.hasNext()) {
      logInfoPage.add(logInfoMongoCursor.next());
    }
</code></pre>
<blockquote>
<p>删除以及更新操作就不再赘述,<code>Morphia</code>接口比较明晰,容易入门.</p>
</blockquote>
<blockquote>
<p>PS:<br>
如果您觉得我的文章对您有帮助,请关注我的微信公众号,谢谢!<br>
<img src="https://img2018.cnblogs.com/blog/764719/201910/764719-20191017123633925-2038306954.jpg"></p>
</blockquote><br><br>
来源:https://www.cnblogs.com/jason1990/p/11065800.html
頁: [1]
查看完整版本: Spring boot配置MongoDB以及Morphia踩坑记录