【Spring连载】使用Spring Data访问 MongoDB----对象映射之JSON Schema
- 一、生成Schema
- 二、加密字段
- [三、JSON Schema类型](#三、JSON Schema类型)
从3.6版本开始,MongoDB支持根据提供的 JSON Schema验证documents的集合。在创建集合时,可以定义schema本身以及验证操作和级别,如下例所示:
例1:示例JSON schema
json
{
"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
java
MongoJsonSchema.builder() --------1
.required("lastname") --------2
.properties(
required(string("firstname").possibleValues("luke", "han")), --------3
object("address")
.properties(string("postCode").minLength(4).maxLength(5)))
.build(); --------4
1. 获取一个schema生成器,以使用fluent API配置schema。
2. 如图所示直接配置所需属性,或如第3步所示提供更多详细信息。
3. 配置所需的String类型的firstname字段,只允许使用luke和han值。属性可以是类型化的,也可以是非类型化的。使用JsonSchemaProperty的静态导入使语法稍微紧凑一点,并获取string(...)等入口点。
4. 生成schema对象。
通过gateway接口上的静态方法,已经有一些预定义的强类型schema对象(JsonSchemaObject和JsonSchemaProperty)可用。但是,你可能需要构建自定义属性验证规则,这些规则可以通过builder API创建,如下面的示例所示:
java
// "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创建集合
java
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
java
public class Person {
private final String firstname; --------1
private final int age; --------2
private Species species; --------3
private Address address; --------4
private @Field(fieldType=SCRIPT) String theForce; --------5
private @Transient Boolean useTheForce; --------6
public 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));
json
{
'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:为属性指定其他类型
java
class Root {
Object value;
}
class A {
String aValue;
}
class B {
String bValue;
}
MongoJsonSchemaCreator.create()
.property("value").withTypes(A.class, B.class) --------1
json
{
'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定义中
java
abstract class Root {
String rootValue;
}
class A extends Root {
String aValue;
}
class B extends Root {
String bValue;
}
MongoJsonSchemaCreator.mergedSchemaFor(A.class, B.class) --------1
json
{
'type' : 'object',
'properties' : { --------1
'rootValue' : { 'type' : 'string' },
'aValue' : { 'type' : 'string' },
'bValue' : { 'type' : 'string' }
}
}
}
1. 给定类型的属性(及其继承的属性)被组合到一个schema中。
具有相同名称的属性需要引用相同的JSON schema才能进行组合。下面的示例展示了由于数据类型不匹配而无法自动合并的定义。在这种情况下,一个ConflictResolutionFunction必须提供给MongoJsonSchemaCreator。
java
class A extends Root {
String value;
}
class B extends Root {
Integer value;
}
二、加密字段
MongoDB 4.2字段级加密允许直接加密单个属性。
设置JSON Schema时,可以将属性封装在加密的属性中,如下例所示。
例7:通过Json Schema进行客户端字段级加密
java
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进行客户端字段级加密
java
@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 --------2
String bloodType;
@Encrypted(algorithm = "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic") --------3
Integer ssn;
}
1. 将为encryptMetadata设置默认的加密设置。
2. 使用默认加密设置的加密字段。
3. Encrypted字段覆盖默认加密算法。
@Encrypted注解支持通过SpEL表达式解析keyIds。为此,需要提供额外的环境元数据(通过MappingContext)。
java
@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的最灵活的方法。
java
public class EncryptionExtension implements EvaluationContextExtension {
@Override
public String getExtensionId() {
return "mongocrypt";
}
@Override
public 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。