【shrimpcolo http://blog.csdn.net/shrimpcolo 未经允许严禁转载,请尊重作者劳动成果。】
背景
Mongodb本身是没有类似mysql那种_id自增功能的,Mongodb使用ObjectId作为自己的_id, Java中表现的是字符串。
ObjectId 示例
5a785ed5 07267d 4168 ac8957
其中:
1. 5a785ed5
时间戳
2. 07267d
主机的唯一标识符
3. 4168
同一机器不同的mongodb进程
4. ac8957
自动增加的计数器
但是,有时候又想实现这样的自增功能怎么办? 办法总是比问题多。
【shrimpcolo http://blog.csdn.net/shrimpcolo 未经允许严禁转载,请尊重作者劳动成果。】
实现原理
用一个collection保存 需要自增的collection 序列,然后每次使用的时候都去查询,获取当前最新的数字,然后 +1。
那么具体的方式 就会分为2种。函数调用 & 注解
Java实现
Java的实现网络上很多,参考如下。
1. spring-data-mongodb之自增ID实现
onBeforeConvert
需要注意,spring data mongodb版本不同会有不一样的方法
使用注解方式
2. Spring data-mongodb ID自增长注解实现
同样的注意 onBeforeSave
根据spring data mongodb版本不同而定
使用注解方式
3. Spring Data MongoDB – Auto Sequence ID example
时间是2014年的,使用的是 方法调用的方式,而不是注解方式。
4. MongoDB进阶(九)Java中实现MongoDB自增主键ID
这篇博客 先讲解了ObjectId 以及含义,然后使用注解方式实现自增, 实现方式跟上述几个相似
【shrimpcolo http://blog.csdn.net/shrimpcolo 未经允许严禁转载,请尊重作者劳动成果。】
Kotlin实现
相关依赖
spring-5.0.4
spring-boot-2.0-release
spring-boot-mongodb-3.6
kotlin-1.2.30
【shrimpcolo http://blog.csdn.net/shrimpcolo 未经允许严禁转载,请尊重作者劳动成果。】
Kotlin 注解定义
/**
* 自定义自增长ID注解
*
*/
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD)
annotation class AutoID01
Mongodb保存自增序列的集合类
@Document(collection = "sys_sequence")
class SequenceId (
@Id
val id: String = ObjectId().toHexString(),
@Field("seq_id")
val seqId: Long = 0L,
@Field("coll_name")
val collName: String = ""
)
MongoEvent监听类
@Component
class SaveEventListener01 : AbstractMongoEventListener<Any>() {
@Autowired
private val mongo: MongoTemplate? = null
override fun onBeforeSave(event: BeforeSaveEvent<Any>) {
val source = event.source
ReflectionUtils.doWithFields(source.javaClass) { field ->
println("===> onBeforeSave 11 ")
ReflectionUtils.makeAccessible(field)
// 如果字段添加了我们自定义的AutoValue注解
if (field.isAnnotationPresent(AutoID01::class.java) && field.get(source) is Number
&& field.getLong(source) == 0L) {
// field.get(source) instanceof Number &&
// field.getLong(source)==0
// 判断注解的字段是否为number类型且值是否等于0.如果大于0说明有ID不需要生成ID
// 设置自增ID
println("===> onBeforeSave 22")
field.set(source, getNextId(source.javaClass.simpleName))
}
}
}
override fun onBeforeConvert(event: BeforeConvertEvent<Any>) {
val source = event.source
if (source != null) {
println("===> onBeforeConvert 11 ")
ReflectionUtils.doWithFields(source.javaClass) { field ->
ReflectionUtils.makeAccessible(field)
// 如果字段添加了我们自定义的AutoValue注解
println("===> onBeforeConvert 2222 ")
if (field.isAnnotationPresent(AutoID01::class.java) && field.get(source) is Number
&& field.getLong(source) == 0L) {
// field.get(source) instanceof Number &&
// field.getLong(source)==0
// 判断注解的字段是否为number类型且值是否等于0.如果大于0说明有ID不需要生成ID
// 设置自增ID
field.set(source, getNextId(source.javaClass.simpleName))
println("集合的ID为======================= $source")
}
}
}
}
/**
* 获取下一个自增ID
*
* @param collName
* 集合(这里用类名,就唯一性来说最好还是存放长类名)名称
* @return 序列值
*/
private fun getNextId(collName: String): Long {
val query = Query(Criteria.where("coll_name").`is`(collName))
println("===> getNextId, collName = $collName")
val update = Update()
update.inc("seq_id", 1)
val options = FindAndModifyOptions()
options.upsert(true)
options.returnNew(true)
val seq = mongo!!.findAndModify(query, update, options, SequenceId::class.java)
println("===> getNextId, seqId = $seq")
return seq.seqId
}
}
【shrimpcolo http://blog.csdn.net/shrimpcolo 未经允许严禁转载,请尊重作者劳动成果。】
其他问题
以上是完整实现,注意上面都是kt 而不是java,原本直接使用java现有的方式,在本地代码测试都没有问题,但是将代码 gradlew build
成jar包,放在 云端环境,居然出现了问题,无法创建对应的 sys_sequence 集合,导致数据无法写入mongodb。 索性,全部使用kotlin实现,才解决。
onBeforeSave
& onBeforeConvert
都有执行到,但是在代码中onBeforeConvert
精确执行成功,估计跟 spring-data 版本有关。
【shrimpcolo http://blog.csdn.net/shrimpcolo 未经允许严禁转载,请尊重作者劳动成果。】