(一)了解ObjectId
MongoDB的文档固定是使用“_id”作为主键的,它可以是任何类型的,默认是个ObjectId对象(在Java中则表现为字符串),那么为什么MongoDB没有采用其他比较常规的做法(比如MySql的自增主键),而是采用了ObjectId的形式来实现?别着急,咱们看看ObjectId的生成方式便可知悉。
ObjectId使用12字节的存储空间,每个字节两位十六进制数字,是一个24位的字符串。由于看起来很长,不少人会觉得难以处理,其实不然。ObjectId是由客户端生成的,按照如下方式生成:
- 前4位是一个从标准纪元开始的时间戳,是一个int类别,只不过从十进制转换为了十六进制。这意味着这4个字节隐含了文档的创建时间,将会带来一些有用的属性。并且时间戳处于字符的最前面,同时意味着ObjectId大致会按照插入顺序进行排序,这对于某些方面起到很大作用,如作为索引提高搜索效率等等。使用时间戳还有一个好处是,某些客户端驱动可以通过ObjectId解析出该记录是何时插入的,这也解答了我们平时快速连续创建多个Objectid时,会发现前几位数字很少发现变化的现实,因为使用的是当前时间,很多用户担心要对服务器进行时间同步,其实这个时间戳的真实值并不重要,只要其总不停增加就好。
- 接下来的3个字节,是所在主机的唯一标识符,一般是机器主机名的散列值,这样就确保了不同主机生成不同的机器hash值,确保在分布式中不造成冲突,这也就是在同一台机器生成的objectid中间的字符串都是一模一样的原因。
- 上面的机器字节是为了确保在不同机器产生的ObjectId不冲突,而PID就是为了在同一台机器不同的mongodb进程产生了ObjectId不冲突。
- 前面的9个字节是保证了一秒内不同机器不同进程生成ObjectId不冲突,最后的3个字节是一个自动增加的计数器,用来确保在同一秒内产生的ObjectId也不会冲突,允许256的3次方等于16777216条记录的唯一性。
因此,MongoDB不使用自增主键,而是使用ObjectId。在分布式环境中,多个机器同步一个自增ID不但费时且费力,MongoDB从一开始就是设计用来做分布式数据库的,处理多个节点是一个核心要求,而ObjectId在分片环境中要容易生成的多。
(二)手动实现自增ID
定义一个存储主键ID的集合:
@Document(collection = "sequence")
public class SeqInfo implements Serializable {
@Id
private String id;//主键
private int seqId;//序列值 BySeq中的主键
定义操作的实体类:
@Document(collection = "byseq")
public class BySeq implements Serializable {
@Id
public int id;
@Field
public String name;
@Field
public int age;
定义一个自增ID的类:
@Component
public class MongodbSaveEventListener{
public int getNextSequence(String collectionName) {
SeqInfo seq = MongoDBClientUtil.mongoTemplate().findAndModify(
//找不见则创建 id:collectionName
query(where("id").is(collectionName)),
new Update().inc("seqId", 1),
options().upsert(true).returnNew(true),
SeqInfo.class);
return seq.getSeqId();
}
}
实验操作:
@ApiOperation(value = "Mongogodb 新增(自增ID)", notes = "Mongogodb 新增(自增ID)")
@RequestMapping(value = "/mongodbInsertByIncId", method = RequestMethod.POST)
public List<BySeq> mongodbInsertByIncId() {
List<BySeq> list = new ArrayList<>();
BySeq bySeq = new BySeq();
bySeq.setId(new MongodbSaveEventListener().getNextSequence("byseq"));
bySeq.setName("LJ");
bySeq.setAge(14);
BySeq insert = MongoDBClientUtil.mongoTemplate().insert(bySeq);
BySeq bySeq1 = new BySeq();
bySeq1.setId(new MongodbSaveEventListener().getNextSequence("byseq"));
bySeq1.setName("连喜灯");
bySeq1.setAge(45);
BySeq insert1 = MongoDBClientUtil.mongoTemplate().insert(bySeq1);
BySeq bySeq2 = new BySeq();
bySeq2.setId(new MongodbSaveEventListener().getNextSequence("byseq"));
bySeq2.setName("李会政");
bySeq2.setAge(56);
BySeq insert2 = MongoDBClientUtil.mongoTemplate().insert(bySeq2);
list.add(insert);
list.add(insert1);
list.add(insert2);
/*BySeq insert = MongoDBClientUtil.mongoTemplate().insert(bySeq);
BySeq insert1 = MongoDBClientUtil.mongoTemplate().insert(bySeq1);
BySeq insert2 = MongoDBClientUtil.mongoTemplate().insert(bySeq2);*/
return list;
}