1. 程式人生 > >SpringBoot+mongoDB實現id自增

SpringBoot+mongoDB實現id自增

ets pac beans http etc current 不用 inf ppi

這段時間給朋友做了一個微信小程序,順便練習一下spring boot,雖然項目使用的是JPA+MySQL,但是好奇嘗試了一下MongoDB實現自增ID,雖然MongoDB很少有自增ID的需求(在分布式環境中,多個機器同步一個自增ID不但費時且費力,MongoDB從一開始就是設計用來做分布式數據庫的,處理多個節點是一個核心要求,而ObjectId在分片環境中要容易生成的多),但是需求是多變的,難免會遇到需要自增的需求。
技術分享圖片
MongoDB有默認的ObjectId,是一個12字節的 BSON 類型字符串。按照字節順序,依次次代表:

  • 4字節:UNIX時間戳
  • 3字節:表示運行MongoDB的機器
  • 2字節:表示生成此_id的進程
  • 3字節:由一個隨機數開始的計數器生成的值

Spring boot中可以使用MongoTemplate操作MongoDB,但是不能自增ID,只能手動實現:

1).自定義註解用於標識需要自增的Field.

    package com.example.annotation;

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

    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AutoIncKey {}

2).創建數據表存儲當前的id號

id的自增進度需要一個數據表存儲(只是存儲當前id號數據,可以不用數據庫而用其他形式)

@Document(collection = "inc")
public class IncInfo {

    @Id
    private String id;// 主鍵

    @Field
    private String collName;// 需要自增id的集合名稱(這裏設置為MyDomain)

    @Field
    private Integer incId;// 當前自增id值

    // 省略getter、setter

3).實現監聽類

監聽器用於監聽Mongo Event,該類繼承AbstractMongoEventListener類,因為我們需要在JAVA對象轉換成數據庫對象的時候操作id字段實現id自增,所以覆蓋onBeforeConvert方法(詳見spring-data文檔,https://docs.spring.io/spring-data/data-document/docs/current/reference/html/,5.11節)

    package com.example.listener;

    import java.lang.reflect.Field;
    import com.example.annotation.AutoIncKey;
    import com.example.domain.MyDomain;
    import com.example.domain.IncInfo;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    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;
    
    @Component
    public class SaveEventListener extends AbstractMongoEventListener<Object>{
        private static final Logger logger= LoggerFactory.getLogger(SaveEventListener.class);
        @Autowired
        private MongoTemplate mongo;
    
        @Override
        public void onBeforeConvert(BeforeConvertEvent<Object> event) {
            logger.info(event.getSource().toString());
            MyDomain source=(MyDomain)event.getSource();
            if (source != null) {
                ReflectionUtils.doWithFields(source.getClass(), new ReflectionUtils.FieldCallback() {
                    public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                        ReflectionUtils.makeAccessible(field);
                        // 如果字段添加了我們自定義的AutoIncKey註解
                        if (field.isAnnotationPresent(AutoIncKey.class)) {
                            // 設置自增ID
                            field.set(source, getNextId(source.getClass().getSimpleName()));
                        }
                    }
                });
            }
    
        }
    
        private Integer getNextId(String collName) {
            Query query = new Query(Criteria.where("collName").is(collName));
            Update update = new Update();
            update.inc("incId", 1);
            FindAndModifyOptions options = new FindAndModifyOptions();
            options.upsert(true);
            options.returnNew(true);
            IncInfo inc= mongo.findAndModify(query, update, options, IncInfo.class);
            return inc.getIncId();
        }
    }

4).在我們的對象中id字段上添加註解

    @Id
    @AutoIncKey
    private Integer id=0;

至此就實現了Mongodb的自增id。

SpringBoot+mongoDB實現id自增