项目实训7

项目实训7

1. 背景

由于本项目中除了使用了mysql数据库,还使用了mongo数据库,但是在一些情况下,mongo数据库无法实现自增的id,所以打算借助java的监听器实现表中字段的自增

由于MongoDB本身并不维护自增ID,因此只能利用MongoDB的原子性通过findAndModify自增1来实现,思路就是

2. 实现过程

导入依赖

        <dependency>
                <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>


首先看一下项目中mongo对应的实体类

package com.example.guke.entity;

import com.example.guke.annotation.AutoDec;
import com.example.guke.annotation.AutoInc;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.util.Date;
import java.util.List;

/**
 * @program: GuKe
 * @description: 社区的帖子类,内含多个回复,类似于百度贴吧的形式
 * @author: NiuYiq
 * @date: 2022-04-04 16:30
 **/

@Data
@Document(value = "post")
public class Post {

    @AutoInc
    private long postId;     // 帖子的id
    private String owner; // 帖子的发起人的所有者 --- openid
    private String title; // 帖子的标题
    private Date time; // 发布时间
//    private List<CommentToPost> comments; // 用户跟帖
    @AutoDec
    private int commentCount;   // 评论数量
    private List<String> likes;     // 由于使用的是nosql,所以这里的like不需要再分一个表出去了
//    private List<String> annexes;   // 博客提供的附件
}


package com.example.guke.entity;

import com.example.guke.annotation.AutoInc;
import lombok.Data;
import org.springframework.data.mongodb.core.mapping.Document;

import java.util.Date;
import java.util.List;


/**
 * 一级评论类
 */
@Data
@Document(value = "commentToPost")
public class CommentToPost {

    @AutoInc
    private long C2Pid;  // 评论的id

    private String owner;   // 评论的用户的openid User.openid
    private String content; // 用户评论的正文内容
    private long belong;     // 属于哪一条博客的评论 Post.id
    private List<CommentToComment> comments;    // 评论下的评论列表
    private Date time; // 发布时间
    private List<String> likes; // 点赞列表,存的是点赞用户的openid

}


以上等实体类需要用到id,便于进行使用,所以有定义@AutoInc的注解标识,便于在监听器中对其进行处理。

我们先看一下springboot中集成mongo的生命周期

AbstractMappingEventListener 中提供了以下回调方法:

方法描述
onBeforeConvert调用 MongoTemplateinsertinsertListsave 操作,在通过 MongoConverter 将对象转换为文档之前的处理。
onBeforeSave调用 MongoTemplateinsertinsertListsave操作,在数据库中插入或保存文档之前的处理。
onAfterSave调用 MongoTemplateinsertinsertListsave操作,在数据库中插入或保存文档之后的处理。
onAfterLoad调用 MongoTemplate 中的 findfindAndRemovefindOnegetCollection方法,从数据库检索文档后的处理。
onAfterConvert调用 MongoTemplate 中的 findfindAndRemovefindOnegetCollection方法,从数据库检索文档被转换为 POJO 后的处理。

也就是说,只需要我们在其保存到mongo之前,对其自增id进行处理,就可以达到一种自增id的效果,所以重写的方法是onBeforeSave方法,在数据库中插入或保存文档之前的处理。

下面是监听器的代码,其中用到了java原生中的一些工具类进行处理,包括ReflectionUtils等工具类

package com.example.guke.listener;

import com.example.guke.annotation.AutoInc;
import com.example.guke.entity.Incr;
import com.example.guke.entity.Post;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.FindAndModifyOptions;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener;
import org.springframework.data.mongodb.core.mapping.event.BeforeSaveEvent;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

import java.util.Objects;

/**
 * @program: GuKe
 * @description: 对mongodb保存过程进行监听,实现主键的递增替换
 * @author: NiuYiq
 * @date: 2022-04-05 09:56
 **/

@Slf4j
@Component
public class SaveMongoEventListener extends AbstractMongoEventListener<Object> {
    private final MongoTemplate mongoTemplate;

    @Autowired
    public SaveMongoEventListener(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }


    /**
     * 在保存的时候进行监听,通过反射对ID进行自增
     * 此时MongoDB data已经将要操作的实体类转为Document{@link Document}对象,
     * 所以这时候应该对document对象进行换ID
     *
     * @param event 事件响应
     */
    @Override
    public void onBeforeSave(BeforeSaveEvent<Object> event) {
        // 得到操作的实体类对象
        Object source = event.getSource();


        //spring-mongo-data与MongoDB交互的document对象
        Document document = event.getDocument();
//        log.info(event.getCollectionName());
        if (Objects.nonNull(source)) {
            //利用反射进行相关操作
            ReflectionUtils.doWithFields(source.getClass(), field -> {
                //使操作的成员可访问 取消私有字段权限
                ReflectionUtils.makeAccessible(field);
                // 该字段是否使用自增注解且是Number类型
                if (field.isAnnotationPresent(AutoInc.class) && field.get(source) instanceof Number) {
                    String collectionName = source.getClass().getSimpleName().substring(0, 1).toLowerCase()
                            + source.getClass().getSimpleName().substring(1);
                    //判断document不能为空
                    Assert.notNull(document, "event.document must not be null");
                    //获取自增主键
                    Long incrId = getIncrId(collectionName);
                    //对ID进行替换
                    if(collectionName.equals("post")){
                        document.put("postId", incrId);
                        field.set(source, incrId);
                    }else if(collectionName.equals("commentToPost")){
//                        log.info(document.toString());
                        document.put("C2Pid", incrId);
                        field.set(source, incrId);
                        incCommentCount((Long) document.get("belong"));
                    }

                }
            });
        }
        super.onBeforeSave(event);
    }

    /**
     * 返回下一个自增ID
     *
     * @param collectionName 集合名
     * @return
     */
    private Long getIncrId(String collectionName) {
        Query query = new Query(Criteria.where("collectionName").is(collectionName));
        Update update = new Update();
        update.inc("incrId");
        FindAndModifyOptions options = FindAndModifyOptions.options();
        options.upsert(true);//没有就新增
        options.returnNew(true);//返回最新
        Incr andModify = mongoTemplate.findAndModify(query, update, options, Incr.class);
        Assert.notNull(andModify, "主键自增时返回参数异常");
        return andModify.getIncrId();
    }

    /*todo: 找到对应的collection 并对相应的字段递增*/

    /** 
     * @name: incCommentCount
     * @description: TODO  使用listener在回帖的时候自动将帖子的回帖数量+1
     * @param postId 帖子的id
     * @return: boolean
     * @date: 2022/4/5 14:40
     * @author: NiuYiq
     * 
     */
     
    public boolean incCommentCount(Long postId){
        Query query = new Query(Criteria.where("postId").is(postId));
        Update update = new Update();
        update.inc("commentCount");
        FindAndModifyOptions options = FindAndModifyOptions.options();
        options.upsert(true);// 没有就新增
        options.returnNew(true);// 返回最新
        Post andModify = mongoTemplate.findAndModify(query, update, options, Post.class);
        Assert.notNull(andModify, "post自增时返回参数异常");
        return true;
    }
}


如此,便实现了mongo中自增id的一个效果。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值