2011-07-24 21 views
26

El valor predeterminado MappingMongoConverter agrega una clave de tipo personalizada ("_class") a cada objeto en la base de datos. Por lo tanto, si creo una persona:Datos de muelle MongoDb: MappingMongoConverter eliminar _class

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

y guardarlo en el PP:

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

el objeto resultante en el mongo habrá:

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

Preguntas:

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

  2. ¿Es posible no contaminar el objeto con la tecla "_clase"; sin escribir un convertidor único solo para la clase Persona?

+0

Entonces, ¿cuál es la historia con esto? ¿No hay forma de evitar que el campo "_clase" se guarde en MongoDB? – hodgesz

Respuesta

20

Así que aquí está la historia: agregamos el tipo por defecto como algún tipo de sugerencia de qué clase crear una instancia en realidad. Como usted tiene que canalizar en un tipo de leer el documento en MongoTemplate a través de todos modos hay dos opciones posibles:

  1. Usted mano en un tipo del tipo almacenado real puede ser asignado a. En ese caso, consideramos el tipo almacenado, utilízalo para la creación de objetos. El ejemplo clásico aquí es hacer consultas polimórficas. Supongamos que tiene una clase abstracta Contact y su Person. A continuación, puede consultar Contact sy esencialmente tiene que determinar un tipo para crear instancias.
  2. Si, por otro lado, pasas un tipo completamente diferente, simplemente ingresaremos en ese tipo de letra, no en el almacenado en el documento en realidad. Eso cubriría su pregunta de qué pasa si mueve el tipo.

Quizás le interese ver this ticket que cubre algún tipo de estrategia de mapeo de tipo enchufable para convertir la información del tipo en un tipo real. Esto puede servir simplemente para ahorrar espacio, ya que es posible que desee 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 podría encontrar claves de tipo completamente arbitrarias producidas por otro cliente de almacenamiento de datos y vincularlas a tipos de Java.

+0

gracias por responder. ¿Tendría sentido extraer el tipo a una configuración? en lugar de mantenerlo con el objeto? Por ejemplo, para proporcionar la asignación en el código: ('converter.configure (Contact.class, Person.class)'). –

+0

Oliver, ¿hay una forma simple de eliminar _class en 1.0GA? [Este] (http://forum.springsource.org/showthread.php?112505-Spring-data-MongoDb-MappingMongoConverter-remove-_class) no funciona ahora. La forma más simple que parece es: '((MappingMongoConverter) template.getConverter()). SetTypeMapper (new DefaultMongoTypeMapper (null));'. Pero es feo e incorrecto ... – Shcheklein

+1

¿Qué quieres decir con 'no funciona'? No es necesario hacer el trabajo de conversión si configura el 'MappingMongoConverter' correctamente por adelantado mediante el código o la configuración XML. –

14

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; 

    } 

} 
+0

Aquí hay una advertencia: este código elimina todos los códigos de conversión de tipo. Por ejemplo, Spring Data ya no puede convertir (y almacenar) atributos LocalDate. – ChrLipp

+0

@mkyong, algunos fragmentos de su código están en desuso. Han agregado la respuesta de trabajo eliminando las advertencias de desaprobación. – harshavmb

4
<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> 
3

Si desea desactivar _class atributo por defecto, pero preservar polymorfism para las clases especificadas, se puede definir explicitamente el tipo de campo _class (opcional) por configuing:

@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; 
} 

Este preservará el campo _class (o lo que quieras nombrar en construtor) solo para las entidades especificadas.

También puede escribir el propio TypeInformationMapper basado en anotaciones, por ejemplo.Si anota su documento al @DocumentType("aliasName"), conservará el polimorfismo manteniendo un alias de clase.

I have explained briefly it on my blog, pero aquí es alguna pieza de código rápido: https://gist.github.com/athlan/6497c74cc515131e1336

1

luché mucho tiempo con este problema. I seguido el enfoque de mkyong pero cuando introduje un atributo LocalDate (cualquier clase JSR310 de Java 8) he recibido 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 la primavera de datos MongoDB 1.7. Incluso si utiliza versiones más recientes del convertidor no saltó en

La solución fue usar el MappingMongoConverter existente y sólo proporcionan una nueva DefaultMongoTypeMapper (el código de mkyong está bajo 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 Resumiendo:

  • extienden AbstractMongoConfiguration
  • annotate con EnableMongoRepositories
  • en mongoTemplate convertidor de obtener de la clase base, esto garantiza que las clases de conversión de tipo se registran
1

Ésta es mi solución 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; 
} 
1

Si bien, la respuesta de Mkyong todavía funciona, me gustaría añadir mi versión de solución ya que algunos bits están en desuso y pueden estar a punto de limpiarse.

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

Por lo tanto, mi respuesta final de trabajo sin avisos de obsolescencia 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); 

    } 

} 

Esperanza esto ayuda a las personas a quienes les gustaría tener una clase limpio, sin avisos de obsolescencia.

Cuestiones relacionadas