mongoDB生成自增Id

问题:mongoBD在写入数据之后系统会自动生成的12字节唯一标识作为ID,即相当于mysql的主键ID,这里想使用自增的数值来作为ID,但是mongo并不支持
解决:需要ID数值自增,则基本上需要自己控制累加操作,可以借助redis生成唯一ID,redis本身单线程也有原子操作,保证ID唯不重复。这里既然已经接入了mongo,并且mongo也有原子操作,完全可以支持ID自增,直接使用mongo省时省力。
解决逻辑:关注方法findAndModify,这方法本身是原子操作,并且提供了设置项

// 是否返修改后的数据
private boolean returnNew;
//假如不存在是否插入新数据
private boolean upsert;

这两个可以保证,每一次操作都会有最新的ID返回,
所以在这里只需要新建一个collection来维护其他collection的ID,在这里collection中每一条记录中字段(自增的ID,collection名称)。这样就能直接维护起来ID随时可以取用。
下面考虑ID设置的问题,找到类AbstractMongoEventListener,看下源码就知道实现了 ApplicationListener 接口,并且这里面维护了很多事件响应,只要找到在生成数据的时候或者之前修改设置我们需要的ID就可以达到我们的目的,并且事件响应也不需要空考虑有的修改不到。
为了灵活控制某些文档需要使用自己的id有写不需要,这个时候可以使用注解达成来控制。

新建注解:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 用于Mongo生成自增ID注解
 *
 * @author hsf
 * @since 2020/3/19
 **/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface AutoIncId {
}

新建一个维护ID的entity

import lombok.Data;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;

import java.io.Serializable;

/**
 * 自增Id 实体
 *
 * @author hsf
 * @since 2020/3/19
 **/
@Data
public class IncIdDocument implements Serializable {

    private static final long serialVersionUID = -9141159479430770232L;

    /**
     * id 静态变量
     */
    public final static String ID = "id";

    /**
     * seqId 静态变量
     */
    public final static String SEQ_ID = "seqId";

    /**
     * collectionName 静态变量
     */
    public final static String COLLECTION_NAME = "collectionName";
    
    /**
     * 默认ID
     */
    @Id
    private ObjectId id;

    /**
     * 自增ID
     */
    private long seqId;

    /**
     * document名称 即生成的是哪个document的ID
     */
    private String collectionName;
    
}

增加事件监听

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.BeforeConvertEvent;
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.ReflectionUtils;

/**
 * 原理就是维护一个 collection 来保存自增ID每个
 * 其余的每一个 collection 在这个表里面就是一条数据,并记录一个数
 * 每次插入之间通过事件触发 然后通过 findAndModify 直接更新值,并且返回最新值
 * findAndModify 操作本身是原子操作所以不需要当心并发问题
 *
 * @author hsf
 * @since 2020/3/19
 **/
@Component
public class SaveMongoEventListener extends AbstractMongoEventListener<Object> {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Override
    public void onBeforeConvert(final BeforeConvertEvent<Object> event) {
        ReflectionUtils.doWithFields(event.getSource().getClass(), field -> {
            ReflectionUtils.makeAccessible(field);
            if (field.isAnnotationPresent(AutoIncId.class) && field.get(event.getSource()) == null) {
                field.set(event.getSource(), getId(event.getCollectionName()));
            }
        });
    }

    /**
     * 获取自增id
     * 这边是利用mongo的findAndModify的原子性实现的
     * 也可以使用redis来实现
     */
    private Long getId(final String collName) {
        final Query query = new Query().addCriteria(new Criteria(IncIdDocument.COLLECTION_NAME).is(collName));
        final Update update = new Update();
        update.inc(IncIdDocument.SEQ_ID, 1);
        final FindAndModifyOptions options = new FindAndModifyOptions().upsert(true).returnNew(true);
        final IncIdDocument sequence = mongoTemplate.findAndModify(query, update, options, IncIdDocument.class);
        return sequence.getSeqId();
    }
}

测试:
选一个entity加上在id属性上加上注解@AutoIncId,直接使用insert保存即可
数据库查看结果
自增ID表mongoDB生成自增Id_第1张图片

你可能感兴趣的:(nosql,mongo,JAVA)