Pregunta

El valor por defecto Mappingmongoconverter agrega una tecla de tipo personalizada ("_class") a cada objeto en la base de datos. Entonces, si creo una persona:

package my.dto;
public class Person {
    String name;
    public Person(String name) {
        this.name = name; 
    }
}

y guárdelo en el DB:

MongoOperations ops = new MongoTemplate(new Mongo(), "users");
ops.insert(new Person("Joe"));

El objeto resultante en el Mongo será:

{ "_id" : ObjectId("4e2ca049744e664eba9d1e11"), "_class" : "my.dto.Person", "name" : "Joe" }

Preguntas:

  1. ¿Cuáles son las implicaciones de mover la clase de la persona a un espacio de nombres diferente?

  2. ¿Es posible no contaminar el objeto con la tecla "_class"? ¿Sin escribir un convertidor único solo para la clase de persona?

¿Fue útil?

Solución

Así que aquí está la historia: agregamos el tipo de forma predeterminada como algún tipo de pista de qué clase instanciar en realidad. Como tiene que encender un tipo para leer el documento en Via MongoTemplate De todos modos, hay dos opciones posibles:

  1. Usted entrega un tipo al que se puede asignar el tipo almacenado real. En ese caso, consideramos el tipo almacenado, use eso para la creación de objetos. El ejemplo clásico aquí es hacer consultas polimórficas. Supongamos que tienes una clase abstracta Contact y tu Person. Entonces podrías consultar para ContactS y nosotros esencialmente tengo que Determine un tipo para instanciar.
  2. Si, por otro lado, pase por un tipo completamente diferente, simplemente marcaríamos en ese tipo dado, no en el almacenado en el documento en realidad. Eso cubriría su pregunta qué sucede si mueve el tipo.

Puede que te interese ver este boleto que cubre algún tipo de estrategia de mapeo de tipo conectable para convertir la información de tipo en un tipo real. Esto puede servir simplemente fines de ahorro de espacio, ya que puede querer reducir un nombre de clase calificado largo a un hash de algunas letras. También permitiría escenarios de migración más complejos en los que puede encontrar claves de tipo completamente arbitrarias producidas por otro cliente de Datastore y vincularlos a los tipos de Java.

Otros consejos

Aquí está mi anotación, y funciona.

@Configuration
public class AppMongoConfig {

    public @Bean
    MongoDbFactory mongoDbFactory() throws Exception {
        return new SimpleMongoDbFactory(new Mongo(), "databasename");
    }

    public @Bean
    MongoTemplate mongoTemplate() throws Exception {

        //remove _class
        MappingMongoConverter converter = new MappingMongoConverter(mongoDbFactory(), new MongoMappingContext());
        converter.setTypeMapper(new DefaultMongoTypeMapper(null));

        MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory(), converter);

        return mongoTemplate;

    }

}
<mongo:mongo host="hostname" port="27017">
<mongo:options
...options...
</mongo:mongo>
<mongo:db-factory dbname="databasename" username="user" password="pass"                     mongo-ref="mongo"/>
<bean id="mongoTypeMapper"     class="org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper">
<constructor-arg name="typeKey"><null/></constructor-arg>
</bean>
<bean id="mongoMappingContext"      class="org.springframework.data.mongodb.core.mapping.MongoMappingContext" />
<bean id="mongoConverter"     class="org.springframework.data.mongodb.core.convert.MappingMongoConverter">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
<constructor-arg name="mappingContext" ref="mongoMappingContext" />
<property name="typeMapper" ref="mongoTypeMapper"></property>
</bean>
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
<constructor-arg name="mongoConverter" ref="mongoConverter" />
<property name="writeResultChecking" value="EXCEPTION" /> 
</bean>

Si quieres deshabilitar _class atributo de forma predeterminada, pero preservar el polimorfismo para las clases especificadas, puede definir explictamente el tipo de _class (Opcional) Campo Configurando:

@Bean
public MongoTemplate mongoTemplate() throws Exception {
    Map<Class<?>, String> typeMapperMap = new HashMap<>();
    typeMapperMap.put(com.acme.domain.SomeDocument.class, "role");

    TypeInformationMapper typeMapper1 = new ConfigurableTypeInformationMapper(typeMapperMap);

    MongoTypeMapper typeMapper = new DefaultMongoTypeMapper(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, Arrays.asList(typeMapper1));
    MappingMongoConverter converter = new MappingMongoConverter(mongoDbFactory(), new MongoMappingContext());
    converter.setTypeMapper(typeMapper);

    MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory(), converter);
    return mongoTemplate;
}

Esto se conservará _class campo (o lo que quieras nombrar en Construtor) solo para entidades especificadas.

También puedes escribir propio TypeInformationMapper Por ejemplo, basado en anotaciones. Si anotas tu documento por @DocumentType("aliasName") Mantendrá el polimorfismo manteniendo alias de clase.

Lo he explicado brevemente en mi blog, pero aquí hay un código rápido:https://gist.github.com/athlan/6497c74cc515131e1336

Si bien la respuesta de Mkyong todavía funciona, me gustaría agregar mi versión de la solución, ya que pocos bits están en desuso y pueden estar al borde de la limpieza.

Por ejemplo : MappingMongoConverter(mongoDbFactory(), new MongoMappingContext()) está desaprobado en favor de new MappingMongoConverter(dbRefResolver, new MongoMappingContext()); y SimpleMongoDbFactory(new Mongo(), "databasename"); a favor de new SimpleMongoDbFactory(new MongoClient(), database);.

Entonces, mi respuesta de trabajo final sin advertencias de deprecación es:

@Configuration
public class SpringMongoConfig {

    @Value("${spring.data.mongodb.database}")
    private String database;

    @Autowired
    private MongoDbFactory mongoDbFactory;

    public @Bean MongoDbFactory mongoDBFactory() throws Exception {
        return new SimpleMongoDbFactory(new MongoClient(), database);
    }

    public @Bean MongoTemplate mongoTemplate() throws Exception {

        DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory);

        // Remove _class
        MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, new MongoMappingContext());
        converter.setTypeMapper(new DefaultMongoTypeMapper(null));

        return new MongoTemplate(mongoDBFactory(), converter);

    }

}

Espero que esto ayude a las personas a las que les gustaría tener una clase limpia sin advertencias de deprecación.

Esta es mi solución de una línea:

@Bean 
public MongoTemplate mongoTemplateFraud() throws UnknownHostException {

  MongoTemplate mongoTemplate = new MongoTemplate(getMongoClient(), dbName);
  ((MappingMongoConverter)mongoTemplate.getConverter()).setTypeMapper(new DefaultMongoTypeMapper(null));//removes _class
  return mongoTemplate;
}

Luché mucho con este problema. Seguí el enfoque de mkyong Pero cuando presenté un LocalDate atributo (cualquier clase JSR310 de Java 8) Recibí la siguiente excepción:

org.springframework.core.convert.ConverterNotFoundException:
No converter found capable of converting from type [java.time.LocalDate] to type [java.util.Date]

El convertidor correspondiente org.springframework.format.datetime.standard.DateTimeConverters es parte de la primavera 4.1 y se hace referencia en los datos de primavera MongoDB 1.7. Incluso si usaba versiones más nuevas, el convertidor no saltó.

La solución era usar el existente MappingMongoConverter y solo proporciona un nuevo DefaultMongoTypeMapper (El código de Mkyong está en comentario):

@Configuration
@EnableMongoRepositories
class BatchInfrastructureConfig extends AbstractMongoConfiguration
{
    @Override
    protected String getDatabaseName() {
        return "yourdb"
    }

    @Override
    Mongo mongo() throws Exception {
        new Mongo()
    }

    @Bean MongoTemplate mongoTemplate()
    {
        // overwrite type mapper to get rid of the _class column
//      get the converter from the base class instead of creating it
//      def converter = new MappingMongoConverter(mongoDbFactory(), new MongoMappingContext())
        def converter = mappingMongoConverter()
        converter.typeMapper = new DefaultMongoTypeMapper(null)

        // create & return template
        new MongoTemplate(mongoDbFactory(), converter)
    }

Para resumir:

  • extender AbstractMongoConfiguration
  • anotar con EnableMongoRepositories
  • en mongoTemplate Obtener convertidor de la clase base, esto asegura que las clases de conversión de tipo estén registradas

Solo necesita agregar la anotación @Typealias a la definición de clase sobre el cambio del tipo

@Configuration
public class MongoConfig {

    @Value("${spring.data.mongodb.database}")
    private String database;

    @Value("${spring.data.mongodb.host}")
    private String host;

    public @Bean MongoDbFactory mongoDbFactory() throws Exception {
        return new SimpleMongoDbFactory(new MongoClient(host), database);
    }

    public @Bean MongoTemplate mongoTemplate() throws Exception {

        MappingMongoConverter converter = new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory()),
                new MongoMappingContext());
        converter.setTypeMapper(new DefaultMongoTypeMapper(null));

        MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory(), converter);

        return mongoTemplate;

    }

}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top