Announcement Announcement Module
Collapse
No announcement yet.
Registering custom mapping converters for MongoDB Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Registering custom mapping converters for MongoDB

    I have the following mappings

    Code:
    class Parent {
      @Id
      String id;
      String name;
    
    @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name="STATE_CD", insertable = false, updatable = false)
      State state;
    }
    
    class State {
      @Id
      String id;
      String name;
    }
    When I try to insert into mongo instance using mongoTemplate,

    Code:
    List<Person> list = em.createQuery("select p from Person p").setMaxResults(10).getResultList();
    for(Person p: list){
      mongoTemplate.insert(p);
    }
    I get this..

    Code:
    java.lang.ClassCastException: org.hibernate.engine.EntityKey cannot be cast to java.lang.String
    	at org.springframework.data.document.mongodb.convert.SimpleMongoConverter.writeMap(SimpleMongoConverter.java:296)
    	at org.springframework.data.document.mongodb.convert.SimpleMongoConverter.writeCompoundValue(SimpleMongoConverter.java:231)
    Both objects have a String as their PK, so why this error?

    Also, I do have some other objects in my model that have a composite key. I see SimpleMongoConverter might be limited in mapping correctly. Thus, I have written a custom converter, but how can I register it? The documentation on how to do this is not too clear (http://static.springsource.org/sprin.../html/#d0e3177). Specifically, how to I get a hold of the SimpleMongoConverter or MappingMongoConverter to call setConverters on it?

    Here is my converter:

    Code:
    abstract public class ExtendedObjectIdCoverters {
    
        private ExtendedObjectIdCoverters() {
        }
    
        public static enum ObjectIdToLongConverter implements Converter<ObjectId, Long> {
            INSTANCE;
    
            public Long convert(ObjectId source) {
                return new Long(source.toString());
            }
        }
    
        public static enum LongToObjectIdConverter implements Converter<Long, ObjectId> {
            INSTANCE;
    
            public ObjectId convert(Long source) {
                return new ObjectId(source.toString());
            }
        }
    }
    Thanks in advance

  • #2
    Are you using MongoDB's @Id? or Hibernate's? I also believe that @ManyToOne is from Hibernate.

    I don't think you can mix both: Hibernate and MongoDB. Hibernate works only for relational databases. MongoDB is not one of them.

    If you want to store Parents and States in different collections (tables) try something like this:

    Code:
    import org.springframework.data.document.mongodb.mapping.Document;
    import org.springframework.data.document.mongodb.mapping.DBRef;
    import org.springframework.data.annotation.Id;
    
    @Document
    class Parent {
      @Id
      String id;
      String name;
    
     @DBRef
      State state;
    }
    
    @Document
    class State {
      @Id
      String id;
      String name;
    }

    Comment


    • #3
      Thanks Chiwi. My goal is to move some data from my current relational store to mongo. I figured out what the issue was. It was due to bidirectional relationship mappings on entities. That was somehow causing an infinite/stack overflow error when the MongoTemplate was trying to build the DBObject. The error however is not clear and doesn't indicate that. Probably someday, bidirectional relationships might be fully supported via the MongoTemplate.

      So you can use both Hibernate and Mongo together if you choose.

      I still haven't found the answer to the other half of my question, registering custom mapping coverters. Any help there will be greatly appreciated.

      Comment


      • #4
        We've done some refinements regarding the registration of custom Converters after M2 already. In the latest snapshots you should find a setConverters(…) method on MappingMongoConverter now.

        Note that Mongo requires ObjectId's to be used as document key. However we support String and BigInteger as ids as ObjectId is pretty much a hex value. Long won't work as the scale of Longs is to small to take ObjectIds. Simply try to convert an ObjectId to a Long and back to see it will cause invalid results.

        We're currently not supporting using Hibernate and Mongo together correctly. The reason is, that there's no straight forward mapping as documents might use embedded documents to implement many to one relationships. This on the other hand causes the embedded documents not being able to be retrieved as standalone entities but rather in the context of their parent documents. DBRefs can be a solution in that case but they are eagerly loaded for now (this might have caused the infinite loop you experienced).

        I am not entirely sure how you got to the ClassCastException for the Hibernate type though. We recommend using MappingMongoConverter for now as is much more sophisticated and actually the one using the annotation metadata. So using SimpleMongoConverter would not consider those at all…

        Cheers,
        Ollie

        Comment


        • #5
          Thanks Oliver. As for the using Long for PK, how about just letting Mongo generate it's own internal unique id for _id? That is, when I load my entities from the relational store, they have an PK of Long, but when I push it into Mongo via the template, can't I specify that PK not be uses as a PK for the mongo document, forcing Mongo to auto-generate one? (hopefully I explained this correctly)

          As far as @DBRef, I did a quick test with that, but so far, it still seems the child records are embedded in parent.

          Here is a sample of my test case:

          Code:
          @Document
          public class Album {
          
              @org.springframework.data.annotation.Id
              private String id;
          
              private String title;
          
              @DBRef
              private Set<Song> songs = new HashSet<Song>();
          }
          
          @Document
          public class Song {
              @org.springframework.data.annotation.Id
              private String id;
          
              private String name;
          }
          When stored in mongo, what I see is this, when I issue db.col.find()

          Code:
          { "_id" : ObjectId("4dc410af2f96719eb511e3eb"), "songs" : [
          	{
          		"name" : "Shine Like The Moon"
          	},
          	{
          		"name" : "Take You Out"
          	},
          	{
          		"name" : "What About Us?"
          	},
          	{
          		"name" : "The Rising"
          	}
          ], "title" : "The Rising" }
          The doc says DBRef's will be stored, so I was expecting to see an array of DBRef objects instead. Or is that happening behind the scene and rendering like this only to show the entire document (i.e. eagerly fetched the DBRefs)

          Comment

          Working...
          X