Spring Data MongoDB序列生成器_id的初始化实现方案咨询
Spring Data MongoDB 自增ID生成优化方案
你现有实现的核心逻辑(基于findAndModify原子操作实现自增)是合理的,你遇到的初始化问题本质是首次插入序列文档时的默认值处理问题,以下是更规范的实现方案:
1. 优化序列实体类,设置字段默认值
原实体类的seq字段没有指定初始值,极端场景下可能触发空值异常,补充默认初始值即可:
import lombok.Getter; import lombok.Setter; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; @Getter @Setter @Document(collection = "auto_sequence") public class AutoIncrementSequence { @Id private String id; // 新增默认初始值,首次自增后从1开始计数 private Long seq = 0L; }
2. 优化序列生成服务的逻辑
原方法的空值兜底逻辑是合理的,不需要直接固定返回1L,可以保留兜底的同时兼容自定义起始值的需求,优化后代码如下:
package com.juhyun.shorturl.entity.sequence; import lombok.RequiredArgsConstructor; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.query.Update; import org.springframework.stereotype.Service; import java.util.Objects; import static org.springframework.data.mongodb.core.FindAndModifyOptions.options; import static org.springframework.data.mongodb.core.query.Criteria.where; import static org.springframework.data.mongodb.core.query.Query.query; @RequiredArgsConstructor @Service public class SequenceGeneratorService { private final MongoOperations mongoOperations; /** * 生成自增ID * @param seqKey 序列唯一标识 * @param initialValue 首次生成时的起始值,默认1 * @return 自增ID */ public Long generateSequence(String seqKey, Long initialValue) { // 原子操作:查询对应序列,seq字段+1,不存在则初始化 AutoIncrementSequence counter = mongoOperations.findAndModify( query(where("_id").is(seqKey)), new Update().inc("seq", 1), options().returnNew(true).upsert(true), AutoIncrementSequence.class ); // 兜底逻辑:如果返回为空(极端异常场景)返回起始值 return Objects.nonNull(counter) ? counter.getSeq() : initialValue; } // 重载方法,默认从1开始 public Long generateSequence(String seqKey) { return generateSequence(seqKey, 1L); } }
注:
findAndModify本身是MongoDB提供的原子操作,不会出现并发重复ID的问题,不需要额外加锁。
3. 可选优化:自动注入ID,避免业务代码手动调用
可以借助Spring Data MongoDB的生命周期监听器,实现实体保存前自动生成并注入ID,简化业务代码:
3.1 先自定义ID生成注解
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface AutoIncId { // 对应的序列名称 String seqName(); }
3.2 给业务实体加注解
@AutoIncId(seqName = ShortUrl.SEQUENCE_NAME) public class ShortUrl { @Transient public static final String SEQUENCE_NAME = "shorturl_sequence"; @Id private Long _id; // 其他业务字段省略 }
3.3 实现保存前监听器
@Component public class AutoIncIdListener extends AbstractMongoEventListener<Object> { @Autowired private SequenceGeneratorService sequenceGeneratorService; @Override public void onBeforeSave(BeforeSaveEvent<Object> event) { Object entity = event.getSource(); // 判断实体是否加了AutoIncId注解 AutoIncId autoIncId = entity.getClass().getAnnotation(AutoIncId.class); if (autoIncId != null) { try { Field idField = entity.getClass().getDeclaredField("_id"); idField.setAccessible(true); // 如果ID为空则自动生成 if (idField.get(entity) == null) { idField.set(entity, sequenceGeneratorService.generateSequence(autoIncId.seqName())); } } catch (Exception e) { throw new RuntimeException("自动生成ID失败", e); } } } }
优化后业务代码保存实体时不需要手动调用ID生成方法,直接调用mongoTemplate.save(shortUrl)即可自动填充ID。
内容的提问来源于stack exchange,提问作者Leelee
相关产品推荐
相关产品推荐

