此版本仍在开发中,尚未被视为稳定版。如需最新稳定版本,请使用 Spring Data MongoDB 5.0.4spring-doc.cadn.net.cn

JSON Schema

从 3.6 版本开始,MongoDB 支持在集合中根据提供的 JSON Schema 对文档进行验证。 如下例所示,该 schema 本身以及验证操作(validation action)和验证级别(validation level)均可在创建集合时定义:spring-doc.cadn.net.cn

示例 1. 示例 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 模式文档始终从根节点描述整个文档。模式本身就是一个模式对象,可以包含用于描述属性和子文档的嵌入式模式对象。
2 required 是一个属性,用于描述文档中哪些属性是必需的。它可以与其他 schema 约束一起可选地指定。请参阅 MongoDB 关于可用关键字的文档。
3 properties 与描述 object 类型的模式对象相关。它包含特定于属性的模式约束。
4 firstname 用于指定文档中 firstname 字段的约束条件。此处,它是一个基于字符串的 properties 元素,用于声明该字段可能的取值。
5 address 是一个子文档,用于为其 postCode 字段中的值定义模式。

你可以通过指定一个 schema 文档(即使用 Document API 来解析或构建一个文档对象),或者使用 Spring Data 在 org.springframework.data.mongodb.core.schema 包中提供的 JSON schema 工具来构建 schema。MongoJsonSchema 是所有 JSON schema 相关操作的入口点。以下示例展示了如何使用 MongoJsonSchema.builder() 创建一个 JSON schema:spring-doc.cadn.net.cn

示例 2. 创建一个 JSON 模式
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 获取一个模式构建器,以使用流畅的 API 配置模式。
2 直接在此处所示方式配置必需的属性,或按照第3节中的更详细说明进行配置。
3 配置必需的字符串类型 firstname 字段,仅允许 lukehan 这两个值。属性可以是有类型的,也可以是无类型的。使用 JsonSchemaProperty 的静态导入可使语法更简洁,并提供诸如 string(…) 之类的入口点。
4 构建 schema 对象。

网关接口的静态方法已经提供了一些预定义且强类型的 Schema 对象(JsonSchemaObjectJsonSchemaProperty)。 然而,你可能需要构建自定义的属性验证规则,这些规则可以通过构建器 API 创建,如下例所示:spring-doc.cadn.net.cn

// "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 提供了集合模式支持的入口点,如下例所示:spring-doc.cadn.net.cn

示例 3. 使用 $jsonSchema 创建集合
MongoJsonSchema schema = MongoJsonSchema.builder().required("firstname", "lastname").build();

template.createCollection(Person.class, CollectionOptions.empty().schema(schema));

生成架构

设置 schema 可能是一项耗时的工作,我们鼓励每一位决定进行此操作的人务必投入充足的时间。 这一点非常重要,因为 schema 的变更可能非常困难。 然而,有时人们可能不想被它所困扰,这时 JsonSchemaCreator 就派上用场了。spring-doc.cadn.net.cn

JsonSchemaCreator 及其实现类会根据映射基础设施提供的领域类型元数据生成一个 MongoJsonSchema。 这意味着,带注解的属性 以及可能存在的自定义转换都会被考虑在内。spring-doc.cadn.net.cn

示例4. 从领域类型生成JSON Schema
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));
{
    '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 String 类型的属性,通过转换器被转换为 Code
6 @Transient 属性在生成数据库模式时会被忽略。
使用可转换为 _id 的类型(例如 ObjectId)的 String 属性,会被映射为 { type : 'object' }, 除非通过 @MongoId 注解提供了更具体的信息。
表1. 特殊的模式生成规则
Java 模式类型 注释

Objectspring-doc.cadn.net.cn

type : objectspring-doc.cadn.net.cn

如果元数据可用,则使用 propertiesspring-doc.cadn.net.cn

Collectionspring-doc.cadn.net.cn

type : arrayspring-doc.cadn.net.cn

-spring-doc.cadn.net.cn

Mapspring-doc.cadn.net.cn

type : objectspring-doc.cadn.net.cn

-spring-doc.cadn.net.cn

Enumspring-doc.cadn.net.cn

type : stringspring-doc.cadn.net.cn

带有 enum 属性,用于保存可能的枚举值。spring-doc.cadn.net.cn

arrayspring-doc.cadn.net.cn

type : arrayspring-doc.cadn.net.cn

简单类型的数组,除非它是 byte[]spring-doc.cadn.net.cn

byte[]spring-doc.cadn.net.cn

bsonType : binDataspring-doc.cadn.net.cn

-spring-doc.cadn.net.cn

上面的示例演示了如何从一个类型非常精确的源中推导出模式(schema)。 在领域模型中使用多态元素可能会导致 Object 和泛型 <T> 类型的模式表示不准确,这些类型很可能被表示为 { type : 'object' } 而没有进一步的说明。 MongoJsonSchemaCreator.property(…) 允许定义额外的细节,例如在生成模式时应考虑的嵌套文档类型。spring-doc.cadn.net.cn

示例 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(…) 都可以帮助避免手动将多个模式合并为一个的需要。spring-doc.cadn.net.cn

示例6. 将多个Schema合并为单个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 给定类型的属性(及其继承的属性)将合并为一个模式。

具有相同名称的属性必须引用相同的 JSON schema 才能被合并。 以下示例展示了一个由于数据类型不匹配而无法自动合并的定义。 在这种情况下,必须向 ConflictResolutionFunction 提供一个 MongoJsonSchemaCreatorspring-doc.cadn.net.cn

class A extends Root {
	String value;
}

class B extends Root {
	Integer value;
}

加密字段

MongoDB 4.2 字段级加密 允许直接对单个属性进行加密。spring-doc.cadn.net.cn

在设置 JSON Schema 时,属性可以被包装在一个加密属性中,如下例所示。spring-doc.cadn.net.cn

示例 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 注解。spring-doc.cadn.net.cn

示例 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 (2)
    String bloodType;

    @Encrypted(algorithm = "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic") (3)
    Integer ssn;
}
1 将为 encryptMetadata 设置的默认加密配置。
2 使用默认加密设置的加密字段。
3 加密字段,用于覆盖默认的加密算法。

@Encrypted 注解支持通过 SpEL 表达式解析 keyId。 为此,需要额外的环境元数据(通过 MappingContext),并且必须提供这些元数据。spring-doc.cadn.net.cn

@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 进行定义,如下方代码片段所示。 提供自定义扩展是计算 keyId 最灵活的方式。spring-doc.cadn.net.cn

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 架构类型

下表列出了支持的 JSON Schema 类型:spring-doc.cadn.net.cn

表2. 支持的JSON Schema类型
模式类型 Java 类型 架构属性

untypedspring-doc.cadn.net.cn

-spring-doc.cadn.net.cn

description、生成的 descriptionenumallOfanyOfoneOfnotspring-doc.cadn.net.cn

objectspring-doc.cadn.net.cn

Objectspring-doc.cadn.net.cn

required, additionalProperties, properties, minProperties, maxProperties, patternPropertiesspring-doc.cadn.net.cn

arrayspring-doc.cadn.net.cn

byte[] 之外的任何数组spring-doc.cadn.net.cn

uniqueItems, additionalItems, items, minItems, maxItemsspring-doc.cadn.net.cn

stringspring-doc.cadn.net.cn

Stringspring-doc.cadn.net.cn

minLength, maxLentgth, patternspring-doc.cadn.net.cn

intspring-doc.cadn.net.cn

int, Integerspring-doc.cadn.net.cn

multipleOf, minimum, exclusiveMinimum, maximum, exclusiveMaximumspring-doc.cadn.net.cn

longspring-doc.cadn.net.cn

long, Longspring-doc.cadn.net.cn

multipleOf, minimum, exclusiveMinimum, maximum, exclusiveMaximumspring-doc.cadn.net.cn

doublespring-doc.cadn.net.cn

float, Float, double, Doublespring-doc.cadn.net.cn

multipleOf, minimum, exclusiveMinimum, maximum, exclusiveMaximumspring-doc.cadn.net.cn

decimalspring-doc.cadn.net.cn

BigDecimalspring-doc.cadn.net.cn

multipleOf, minimum, exclusiveMinimum, maximum, exclusiveMaximumspring-doc.cadn.net.cn

numberspring-doc.cadn.net.cn

Numberspring-doc.cadn.net.cn

multipleOf, minimum, exclusiveMinimum, maximum, exclusiveMaximumspring-doc.cadn.net.cn

binDataspring-doc.cadn.net.cn

byte[]spring-doc.cadn.net.cn

(none)spring-doc.cadn.net.cn

booleanspring-doc.cadn.net.cn

boolean, Booleanspring-doc.cadn.net.cn

(none)spring-doc.cadn.net.cn

nullspring-doc.cadn.net.cn

nullspring-doc.cadn.net.cn

(none)spring-doc.cadn.net.cn

objectIdspring-doc.cadn.net.cn

ObjectIdspring-doc.cadn.net.cn

(none)spring-doc.cadn.net.cn

datespring-doc.cadn.net.cn

java.util.Datespring-doc.cadn.net.cn

(none)spring-doc.cadn.net.cn

timestampspring-doc.cadn.net.cn

BsonTimestampspring-doc.cadn.net.cn

(none)spring-doc.cadn.net.cn

regexspring-doc.cadn.net.cn

java.util.regex.Patternspring-doc.cadn.net.cn

(none)spring-doc.cadn.net.cn

untyped 是一个泛型类型,所有带类型的 schema 类型都继承自它。它将所有 untyped schema 属性提供给带类型的 schema 类型。

有关更多信息,请参阅 $jsonSchemaspring-doc.cadn.net.cn