【Spring连载】使用Spring Data访问 MongoDB----对象映射之JSON Schema

【Spring连载】使用Spring Data访问 MongoDB----对象映射之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

相关推荐
豪宇刘8 分钟前
Spring MVC
java·spring·mvc
White graces9 分钟前
Spring MVC练习(前后端分离开发实例)
java·开发语言·前端·后端·spring·java-ee·mvc
安安啦9 分钟前
SSM--SpringMVC复习(二)
spring·mvc
深蓝浅蓝的天20 分钟前
nginx超长讨论汇总与常见问题
java·nginx
小黄编程快乐屋3 小时前
各个排序算法基础速通万字介绍
java·算法·排序算法
材料苦逼不会梦到计算机白富美4 小时前
贪心算法-区间问题 C++
java·c++·贪心算法
小小李程序员8 小时前
LRU缓存
java·spring·缓存
cnsxjean9 小时前
SpringBoot集成Minio实现上传凭证、分片上传、秒传和断点续传
java·前端·spring boot·分布式·后端·中间件·架构
hadage2339 小时前
--- stream 数据流 java ---
java·开发语言
《源码好优多》9 小时前
基于Java Springboot汽配销售管理系统
java·开发语言·spring boot