最近项目使用mongo作为持久层
遇到问题:
项目中使用的主键(包括内嵌文档)都是ObjectId类型.以前实体使用String类型.
在测试的时候使用spring-data-mongo 发现数据 都能通过主键查询得到结果.
可以在上线后 发现,mongo内嵌文档通过spring-data-mongo主键查询不出来的.
数据结构类似:
{
"_id" : ObjectId("571867bde4b0855e73cf72a8"),
"isDel" : "0",
"delTime" : NumberLong(0),
"lModTime" : NumberLong(0),
"uid" : "8af0b0e4bc671857fe7aaa59i",
"isEnCV" : "0",
"cvName" : "xxxx",
"verifyTime" : NumberLong(0),
"views" : NumberLong(0),
"downloads" : NumberLong(0),
"cnName" : "xxxx",
"gender" : "xxx",
"birthday" : NumberLong(606585600),
"degreeId" : "5",
"degreeName" : "xxxx",
"marry" : "M",
"nationality" : "中国",
"email" : "xxxx",
"education" : [
{
"_id" : ObjectId("571867b7e4b0855e73cf729f"),
"degreeId" : "5",
"degreeName" : "本科",
"college" : "xxx",
"major" : "xxxx",
"start" : NumberLong(1188576000),
"end" : NumberLong(1309449600),
"description" : ""
}
]
}
主键查询: (这样是能查询到的)
(cvid = 571867bde4b0855e73cf72a8)
Query.query(Criteria.where(
"_id"
).is(Cvid))
内嵌文档查询: (这样式是查询不到的)
(eduid = 571867b7e4b0855e73cf729f)
Query.query(Criteria.where(
"education._id"
).is(eduid)
)
于是 好奇心驱使下开始排查问题。发现spring-data-mongo对于主键查询做了优化判断.如果字段名称
contains 匹配 _id 如果匹配上了 底层的会对入参进行默认的ObjectId转换.
而内嵌文档的查询主键是 xx._id ,所以判断是不包含的,所以没有进行ObjectId转换.
最终解决方案:
在主键查询时自己去显示调用ObjectId的判断.使查询语句能识别ObjectId类型或者String类型
Query.query(Criteria.where("education._id").is(getObjectValue(eduid)))
import org.bson.types.ObjectId;
public Object getObjectValue(String value)
{
return ObjectId.isValid(value) ? new ObjectId(value) : value;
}
spring-data-mongodb-1.3.2
package org.springframework.data.mongodb.core.convert.QueryMapper;
public class QueryMapper {
private static final List DEFAULT_ID_NAMES = Arrays.asList("id", "_id");
public DBObject getMappedObject(DBObject query, MongoPersistentEntity> entity) {
if (isNestedKeyword(query)) {
return getMappedKeyword(new Keyword(query), entity);
}
DBObject result = new BasicDBObject();
for (String key : query.keySet()) {
// TODO: remove one once QueryMapper can work with Query instances directly
if (Query.isRestrictedTypeKey(key)) {
@SuppressWarnings("unchecked")
Set> restrictedTypes = (Set>) query.get(key);
this.converter.getTypeMapper().writeTypeRestrictions(result, restrictedTypes);
continue;
}
if (isKeyword(key)) {
result.putAll(getMappedKeyword(new Keyword(query, key), entity));
continue;
}
Field field = entity == null ? new Field(key) : new MetadataBackedField(key, entity, mappingContext);
Object rawValue = query.get(key);
String newKey = field.getMappedKey();
if (isNestedKeyword(rawValue) && !field.isIdField()) {
Keyword keyword = new Keyword((DBObject) rawValue);
result.put(newKey, getMappedKeyword(field, keyword));
} else {
result.put(newKey, getMappedValue(field, rawValue));
}
}
return result;
}
private Object getMappedValue(Field documentField, Object value) {
if (documentField.isIdField()) {
if (value instanceof DBObject) {
DBObject valueDbo = (DBObject) value;
if (valueDbo.containsField("$in") || valueDbo.containsField("$nin")) {
String inKey = valueDbo.containsField("$in") ? "$in" : "$nin";
List
-----------------------------ObjectId 校验规则 ----------------------------------
package org.bson.types;
public class ObjectId implements Comparable , java.io.Serializable {
public static boolean isValid( String s ){
if ( s == null )
return false;
final int len = s.length();
if ( len != 24 )
return false;
for ( int i=0; i= '0' && c <= '9' )
continue;
if ( c >= 'a' && c <= 'f' )
continue;
if ( c >= 'A' && c <= 'F' )
continue;
return false;
}
return true;
}
}