Spring Data MongoDB 教程

类型映射

MongoDB 集合可以包含代表各种类型实例的文档。如果你存储了一个类的层次结构,或者有一个具有 Object 类型属性的类,那么这个功能就很有用。在后一种情况下,当检索对象时,必须正确地读入该属性内的值。

为了实现这一点,MappingMongoConverter 使用了 MongoTypeMapper 接口的默认实现类  DefaultMongoTypeMapper。它的默认行为是在文档中的 _class 字段下存储完全限定的类名(如:{ _class:'com.hxstrive.Test' })。

类型提示是为顶级文档以及每个值编写的(如果它是一个复杂类型和声明的属性类型的子类型)。下面的例子(在结尾有一个 JSON 表示)展示了映射是如何工作的:

Java 代码:

(1)抽象类 Contact,什么也不做,代码如下:

package com.hxstrive.springdata.mongodb.entity;

public abstract class Contact {
   //...
}

(2)继承 Contact 抽象类的 Person 类,定义了用户姓名、年龄,代码如下:

package com.hxstrive.springdata.mongodb.entity;

import lombok.Builder;
import lombok.Data;
import lombok.ToString;

@Data
@Builder
@ToString
public class Person extends Contact {
   /** 自动映射到 MongoDB 的 _id 字段 */
   private String id;
   private String name;
   private int age;
}

(3)编写一个 Sample 类,成员变量引用 Contact 类,代码如下:

package com.hxstrive.springdata.mongodb.entity;

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class Sample {
   // 必须是抽象类,不能是具体类 Person
   // 否则,保存到 MongDB 中的 JSON 的 contact 中不会添加 _class 字段
   private Contact Contact;
   private String title;
}

(4)客户端代码,如下:

package com.hxstrive.springdata.mongodb;

import com.hxstrive.springdata.mongodb.entity.Person;
import com.hxstrive.springdata.mongodb.entity.Sample;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.core.MongoTemplate;

@SpringBootTest
class MongoTypeMapperTest {

   @Autowired
   private MongoTemplate mongoTemplate;

   @Test
   void contextLoads() {
       Person p = Person.builder().name("Joe").age(34).build();
       Sample sample = Sample.builder().title("MongoTypeMapper Demo").person(p).build();
       // 保存到 MongoDB
       mongoTemplate.save(sample);
   }

}

MongoDB 内部 JSON 表示:

{
    "_id" : ObjectId("63a925e2a19ac53e1d21f6b8"),
    "contact" : {
        "name" : "Joe",
        "age" : NumberInt(34),
        "_class" : "com.hxstrive.springdata.mongodb.entity.Person"
    },
    "title" : "MongoTypeMapper Demo",
    "_class" : "com.hxstrive.springdata.mongodb.entity.Sample"
}

Spring Data MongoDB 将类型信息存储为实际根类以及嵌套类型的最后一个字段(因为它是复杂的并且是 Contact 的子类型)。因此,如果您现在使用mongoTemplate.findAll(Object.class, "sample"),您可以发现存储的文档是一个 sample 实例。您还可以发现 value 属性实际上是一个 Person。

自定义类型映射

如果你想避免把整个 Java 类的完全限定名称写成类型信息(_class),而是想使用一个键(简短的名字),你可以在实体类上使用 @TypeAlias 注解。如果你需要进一步定制映射,可以看看 TypeInformationMapper 接口。该接口的实例可以在 DefaultMongoTypeMapper 上配置。反过来,也可以在 MappingMongoConverter 上配置。

// 定义类型别名
@TypeAlias("pers")
class Person {
    //...
}

注意:此时生成的文档中的 _class 字段值为 “pers”,而不在是 “com.hxstrive.springdata.mongodb.entity.Person”。

MongoDB 内部 JSON 表示示例:

{
    "_id" : ObjectId("63a92670fda8272641e51459"),
    "contact" : {
        "name" : "Joe",
        "age" : NumberInt(34),
        "_class" : "pers"
    },
    "title" : "MongoTypeMapper Demo",
    "_class" : "com.hxstrive.springdata.mongodb.entity.Sample"
}

警告

类型别名只有在映射上下文知道实际类型的情况下才起作用。所需的实体元数据要么在第一次保存时确定,要么必须通过配置的初始实体集提供。默认情况下,配置类会扫描基础包以寻找潜在的候选者。

下面例子展示如何通过 @Configuration 配置类手动将 Sample 类添加到上下文,代码如下:

package com.hxstrive.springdata.mongodb.config;

import com.hxstrive.springdata.mongodb.entity.Sample;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration;
import java.util.Set;

@Configuration
public class AppConfig extends AbstractMongoClientConfiguration {

   @Override
   protected String getDatabaseName() {
       return "test";
   }

   // 扫描映射基础包中标注了 @Document 的类。
   // 默认情况下,它扫描 getMappingBasePackages() 返回的所有包中的实体。
   @Override
   protected Set<Class<?>> getInitialEntitySet() throws ClassNotFoundException {
       Set<Class<?>> superSet = super.getInitialEntitySet();
       superSet.add(Sample.class);
       return superSet;
   }
}

配置自定义类型映射

以下示例显示如何在 MappingMongoConverter 中配置自定义 MongoTypeMapper:

(1)自定义的 MongoTypeMapper

class CustomMongoTypeMapper extends DefaultMongoTypeMapper {
    // implement custom type mapping here
}

(2)配置类

package com.hxstrive.springdata.mongodb.config;

import com.hxstrive.springdata.mongodb.constom.CustomMongoTypeMapper;
import com.hxstrive.springdata.mongodb.entity.Person;
import com.hxstrive.springdata.mongodb.entity.Sample;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import org.springframework.data.mongodb.core.convert.MongoTypeMapper;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import java.util.Set;

@Configuration
public class AppConfig extends AbstractMongoClientConfiguration {

   @Override
   protected String getDatabaseName() {
       return "test";
   }

   /**
    * 重写父类方法,重写了 MappingMongoConverter 的 bean 定义
    * @param databaseFactory
    * @param customConversions
    * @param mappingContext
    * @return
    */
   @Bean
   @Override
   public MappingMongoConverter mappingMongoConverter(MongoDatabaseFactory databaseFactory,
           MongoCustomConversions customConversions, MongoMappingContext mappingContext) {
       MappingMongoConverter mmc = super.mappingMongoConverter(databaseFactory, customConversions, mappingContext);
       // 指定自定义的 MongoTypeMapper
       mmc.setTypeMapper(customTypeMapper());
       return mmc;
   }

   /**
    * 自定义的 MongoTypeMapper
    * @return
    */
   @Bean
   public MongoTypeMapper customTypeMapper() {
       return new CustomMongoTypeMapper();
   }

}

当然,我们也可以通过 XML 文件的形式进行配置,例如:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mongo="http://www.springframework.org/schema/data/mongo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/data/mongo
           http://www.springframework.org/schema/data/mongo/spring-mongo.xsd">

   <!-- 定义 MongoClient -->
   <mongo:mongo-client id="mongoClient" host="localhost" port="27017"/>

   <!-- 定义 MongoDatabaseFactory -->
   <mongo:db-factory id="mongoDbFactory" dbname="test" mongo-client-ref="mongoClient"/>

   <!-- 配置自定义的MongoTypeMapper -->
   <mongo:mapping-converter type-mapper-ref="customMongoTypeMapper"/>

   <!-- 定义自定义 MongoTypeMapper -->
   <bean name="customMongoTypeMapper" class="com.hxstrive.springdata.mongodb.constom.CustomMongoTypeMapper"/>

   <!-- 定义 MongoTemplate -->
   <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
       <constructor-arg ref="mongoClient"/>
       <constructor-arg name="databaseName" value="test"/>
   </bean>

</beans>

请注意,前面的例子扩展了 AbstractMongoClientConfiguration 类,并重写了 MappingMongoConverter 的 bean 定义,我们在这里配置了我们的自定义 MongoTypeMapper。

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号