【Spring連載】使用Spring Data訪問 MongoDB----對象映射之JSON Schema
- 一、生成Schema
- 二、加密字段
- 三、JSON Schema類型
從3.6版本開始,MongoDB支持根據提供的 JSON Schema驗證documents的集合。在創建集合時,可以定義schema本身以及驗證操作和級別,如下例所示:
例1:示例JSON schema
{"type": "object", --------1 "required": [ "firstname", "lastname" ], --------2 "properties": { --------3 "firstname": { --------4 "type": "string","enum": [ "luke", "han" ]},"address": { --------5 "type": "object","properties": {"postCode": { "type": "string", "minLength": 4, "maxLength": 5 }}}}
}1. JSON schema documents總是從根描述整個document。schema是一個schema對象本身,它可以包含描述屬性和子文檔的嵌入schema對象。
2. required是一個屬性,用于描述文檔中需要哪些屬性。可以選擇性地指定它以及其他schema約束。請參閱MongoDB關于[可用關鍵字](https://www.mongodb.com/docs/manual/reference/operator/query/jsonSchema/#available-keywords)的文檔。
3. properties與描述對象類型的schema對象相關。它包含特定于屬性的schema約束。
4. firstname為document中的firstname字段指定約束。這里,它是一個基于字符串的屬性元素,聲明可能的字段值。
5. address是一個子文檔,在其postCode字段中定義值的schema。
你可以通過指定schema document(即,通過使用Document API解析或構建document對象)或使用Spring Data的JSON schema實用程序在org.springframework.data.mongodb.core.schema中構建它來提供schema。MongoJsonSchema是所有JSON模式相關操作的入口點。下面的示例展示了如何使用MongoJsonSchema.builder()來創建JSON schema:
例2:創建JSON schema
MongoJsonSchema.builder() --------1.required("lastname") --------2.properties(required(string("firstname").possibleValues("luke", "han")), --------3object("address").properties(string("postCode").minLength(4).maxLength(5))).build(); --------41. 獲取一個schema生成器,以使用fluent API配置schema。
2. 如圖所示直接配置所需屬性,或如第3步所示提供更多詳細信息。
3. 配置所需的String類型的firstname字段,只允許使用luke和han值。屬性可以是類型化的,也可以是非類型化的。使用JsonSchemaProperty的靜態導入使語法稍微緊湊一點,并獲取string(…)等入口點。
4. 生成schema對象。
通過gateway接口上的靜態方法,已經有一些預定義的強類型schema對象(JsonSchemaObject和JsonSchemaProperty)可用。但是,你可能需要構建自定義屬性驗證規則,這些規則可以通過builder API創建,如下面的示例所示:
// "birthdate" : { "bsonType": "date" }
JsonSchemaProperty.named("birthdate").ofType(Type.dateType());// "birthdate" : { "bsonType": "date", "description", "Must be a date" }
JsonSchemaProperty.named("birthdate").with(JsonSchemaObject.of(Type.dateType()).description("Must be a date"));
CollectionOptions為集合提供了schema支持的入口點,如下面的示例所示:
使用$jsonSchema創建集合
MongoJsonSchema schema = MongoJsonSchema.builder().required("firstname", "lastname").build();template.createCollection(Person.class, CollectionOptions.empty().schema(schema));
一、生成Schema
建立一個schema可能是一項耗時的任務,如果想快速構建schema,可以使用JsonSchemaCreator。
JsonSchemaCreator及其默認實現生成映射基礎設施提供的MongoJsonSchema域外類型元數據。這意味著,要考慮帶注解的屬性以及潛在的自定義轉換。
例4:從域類型生成Json Schema
public class Person {private final String firstname; --------1private final int age; --------2private Species species; --------3private Address address; --------4private @Field(fieldType=SCRIPT) String theForce; --------5private @Transient Boolean useTheForce; --------6public Person(String firstname, int age) { --------1,2 this.firstname = firstname;this.age = age;}// gettter / setter omitted
}MongoJsonSchema schema = MongoJsonSchemaCreator.create(mongoOperations.getConverter()).createSchemaFor(Person.class);template.createCollection(Person.class, CollectionOptions.empty().schema(schema));
{'type' : 'object','required' : ['age'], --------2 'properties' : {'firstname' : { 'type' : 'string' },--------1 'age' : { 'bsonType' : 'int' } --------2 'species' : { --------3 'type' : 'string','enum' : ['HUMAN', 'WOOKIE', 'UNKNOWN']}'address' : { --------4 'type' : 'object''properties' : {'postCode' : { 'type': 'string' }}},'theForce' : { 'type' : 'javascript'} --------5}
}1. 簡單對象屬性被認為是常規屬性。
2. 原始類型被認為是必需的屬性,
3. 枚舉被限制為可能的值。
4. 對象類型屬性被檢查并表示為嵌套文檔。
5. 由轉換器轉換為代碼的字符串類型屬性。
6. 在生成schema時忽略@Transient屬性。
_id屬性使用可以轉換為ObjectId的類型,如String,映射到{ type : ‘object’ },除非有更具體的信息可以通過@MongoId注解獲得。
表1:特殊Schema生成規則
Java | Schema Type | Notes |
---|---|---|
Object | type : object | with properties if metadata available. |
Collection | type : array | - |
Map | type : object | - |
Enum | type : string | with enum property holding the possible enumeration values. |
array | type : array | simple type array unless it’s a byte[] |
byte[] | bsonType : binData | - |
上面的示例演示了如何從非常精確的類型源派生schema。在域模型中使用多態元素可能導致Object和泛型<T>類型的模式表示不準確,它們很可能表示為{ type : ‘object’ },而沒有進一步的說明。MongoJsonSchemaCreator.property(…)允許定義額外的細節,比如在呈現schema時應該考慮的嵌套文檔類型。
例5:為屬性指定其他類型
class Root {Object value;
}class A {String aValue;
}class B {String bValue;
}
MongoJsonSchemaCreator.create().property("value").withTypes(A.class, B.class) --------1
{'type' : 'object','properties' : {'value' : {'type' : 'object','properties' : { --------1 'aValue' : { 'type' : 'string' },'bValue' : { 'type' : 'string' }}}}
}1. 給定類型的屬性被合并到一個元素中。
MongoDB的schema-free方法允許在一個集合中存儲不同結構的文檔。它們可以被建模為具有公共基類。無論選擇哪種方法,MongoJsonSchemaCreator.merge(…)都可以幫助滿足將多個schema合并為一個schema的需要。
例6:將多個Schemas合并到單個Schema定義中
abstract class Root {String rootValue;
}class A extends Root {String aValue;
}class B extends Root {String bValue;
}MongoJsonSchemaCreator.mergedSchemaFor(A.class, B.class) --------1
{'type' : 'object','properties' : { --------1'rootValue' : { 'type' : 'string' },'aValue' : { 'type' : 'string' },'bValue' : { 'type' : 'string' }}}
}1. 給定類型的屬性(及其繼承的屬性)被組合到一個schema中。
具有相同名稱的屬性需要引用相同的JSON schema才能進行組合。下面的示例展示了由于數據類型不匹配而無法自動合并的定義。在這種情況下,一個ConflictResolutionFunction必須提供給MongoJsonSchemaCreator。
class A extends Root {String value;
}class B extends Root {Integer value;
}
二、加密字段
MongoDB 4.2字段級加密允許直接加密單個屬性。
設置JSON Schema時,可以將屬性封裝在加密的屬性中,如下例所示。
例7:通過Json Schema進行客戶端字段級加密
MongoJsonSchema schema = MongoJsonSchema.builder().properties(encrypted(string("ssn")).algorithm("AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic").keyId("*key0_id")).build();
如果不想手動定義加密字段,可以利用@Encrypted注解,如下面的代碼片段所示。
例8:通過Json Schema進行客戶端字段級加密
@Document
@Encrypted(keyId = "xKVup8B1Q+CkHaVRx+qa+g==", algorithm = "AEAD_AES_256_CBC_HMAC_SHA_512-Random") --------1
static class Patient {@Id String id;String name;@Encrypted --------2String bloodType;@Encrypted(algorithm = "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic") --------3Integer ssn;
}1. 將為encryptMetadata設置默認的加密設置。
2. 使用默認加密設置的加密字段。
3. Encrypted字段覆蓋默認加密算法。
@Encrypted注解支持通過SpEL表達式解析keyIds。為此,需要提供額外的環境元數據(通過MappingContext)。
@Document
@Encrypted(keyId = "#{mongocrypt.keyId(#target)}")
static class Patient {@Id String id;String name;@Encrypted(algorithm = "AEAD_AES_256_CBC_HMAC_SHA_512-Random")String bloodType;@Encrypted(algorithm = "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic")Integer ssn;
}MongoJsonSchemaCreator schemaCreator = MongoJsonSchemaCreator.create(mappingContext);
MongoJsonSchema patientSchema = schemaCreator.filter(MongoJsonSchemaCreator.encryptedOnly()).createSchemaFor(Patient.class);
mongocrypt.keyId函數是通過EvaluationContextExtension定義的,如下面的代碼片段所示。提供自定義擴展提供了計算keyIds的最靈活的方法。
public class EncryptionExtension implements EvaluationContextExtension {@Overridepublic String getExtensionId() {return "mongocrypt";}@Overridepublic Map<String, Function> getFunctions() {return Collections.singletonMap("keyId", new Function(getMethod("computeKeyId", String.class), this));}public String computeKeyId(String target) {// ... lookup via target element name}
}
三、JSON Schema類型
下表展示了支持的JSON schema類型:
支持的JSON schema類型
Schema Type | Java Type | Schema Properties |
---|---|---|
untyped | - | description, generated description, enum, allOf, anyOf, oneOf, not |
object | Object | required, additionalProperties, properties, minProperties, maxProperties, patternProperties |
array | any array except byte[] | uniqueItems, additionalItems, items, minItems, maxItems |
string | String | minLength, maxLentgth, pattern |
int | int, Integer | multipleOf, minimum, exclusiveMinimum, maximum, exclusiveMaximum |
long | long, Long | multipleOf, minimum, exclusiveMinimum, maximum, exclusiveMaximum |
double | float, Float, double, Double | multipleOf, minimum, exclusiveMinimum, maximum, exclusiveMaximum |
decimal | BigDecimal | multipleOf, minimum, exclusiveMinimum, maximum, exclusiveMaximum |
number | Number | multipleOf, minimum, exclusiveMinimum, maximum, exclusiveMaximum |
binData | byte[] | (none) |
boolean | boolean, Boolean | (none) |
null | null | (none) |
objectId | ObjectId | (none) |
date | java.util.Date | (none) |
timestamp | BsonTimestamp | (none) |
regex | java.util.regex.Pattern | (none) |
untyped是由所有類型化schema類型繼承的泛型類型。它將所有untyped schema屬性提供給類型化schema類型。
有關更多信息,請參閱$jsonSchema。