Announcement Announcement Module
Collapse
No announcement yet.
Spring Data Document M2: Bug with @DBRef Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Spring Data Document M2: Bug with @DBRef

    @DBRef works with Lists but not with properties.

    With:
    @DBRef
    private List<Address> addresses;
    Loading and saving works!

    With:
    @DBRef
    private Address address;
    Only saving works (correct data at mongoDB).
    Loading throws this exception:

    Exception in thread "main" org.springframework.core.convert.ConverterNotFound Exception: No converter found capable of converting from 'com.mongodb.DBRef' to 'persontest.Address'



    Code:
    @Document
    public class User {
        @Id
        private ObjectId id;
    
        @Indexed
        private Integer ssn;
    
        @DBRef
        private List<Address> addresses;
    
        @DBRef
        private Address address;
       ...
    }
    Code:
    @Document
    public class Address {
    
        @Id
        private ObjectId id;
    
        private String street;
        ...
    }
    Code:
    User user = new User();
    user.setSsn(111);
    
    Address address = new Address();
    address.setStreet("Mainstreet");
    mongoOps.insert(address);
    
    user.setAddress(address);
    
    mongoOps.insert(user);
    
    log.info(mongoOps.findOne(new Query(Criteria.where("ssn").is(111)), User.class));

  • #2
    Yikes!

    I pushed a fix for this and created a test case specifically for non-list, referenced properties.

    If you use a snapshot build (after the nightly runs and creates one, that is ), you should get a fix for that.

    Sorry for the bug. It was a two-line fix so should have been caught earlier.

    Comment


    • #3
      I have the same issue @DBRef annotation not working with properties

      org.springframework.core.convert.ConverterNotFound Exception: No converter found capable of converting from 'com.mongodb.DBRef' to 'com.test.Person'
      I am using spring-data-mongodb version 1.0.0.M2. Please advise.

      Comment


      • #4
        Hi a quick correction, I updated to 1.0.0.M3 and i still have the same issue. Can you please tell me on which version did your fix get in?

        Comment


        • #5
          Still an issue?

          I have a case where this is continuing to fail in 1.0.0M4 -- the option to use a List is also not working for me, however. Is it expected that this has been fixed? Is there bug to track it?

          M

          Comment


          • #6
            Found a work around

            This is a total hack, but it does seem to work until a real fix can make it into the Spring code base. The issue is most definitely the storage of the reference, not the loading -- the value on the '$id' field is stored as "3a7blahblahblah" instead of ObjectId("3a7blahblahblah"). So the solution is to use a Mongo event listener to make this happen before the save occurs:

            Code:
            @Component
            public class MyDocReferenceFixer extends AbstractMongoEventListener<MyDoc> {
            
                @Override
                public void onBeforeSave(MyDoc source, DBObject dbo) {
                    super.onBeforeSave(source, dbo);
                    
                    Object o = dbo.get("myReferenceProperty");
                    DBRef dbref = (DBRef)o;
                    
                    dbo.put("myReferenceProperty", new DBRef(dbref.getDB(), dbref.getRef(), new ObjectId((String)dbref.getId())));
                }
                
            }
            Pretty simple -- biggest downfall here (aside from it being a total hack) is that you have to do it for every Document type that has a DBRef, which will get irritating after a while. It does appear to work so far, though -- after putting this in place, my DBRef objects are eagerly loaded when using the MongoTemplate object, and when using a repository.

            M

            Comment


            • #7
              VGuna and I (johannz) discussed this on September 9 in http://forum.springsource.org/showth...9-returns-null

              I created a bug for it: https://jira.springsource.org/browse/DATADOC-275 - DBRef fields and collections are returning nulls. In that bug I researched the cause, and have a proposed fix.

              Copied from the bug:
              Analysis

              I think the problem is in the MappingMongoConverter, and how it is storing DBRefs. Looking at the code for the createDBRef method, it is not doing any type conversion of the ID that it retrieves from the target object, when it should be.

              From the createDBRef method, exception handling removed:

              Code:
              Object id = null;
              	BeanWrapper<MongoPersistentEntity<Object>, Object> wrapper = BeanWrapper.create(target, conversionService);
              	id = wrapper.getProperty(idProperty, Object.class, useFieldAccessOnly);
              
              	String collection = dbref.collection();
              	if ("".equals(collection)) {
              		collection = targetEntity.getCollection();
              	}
              
              	String dbname = dbref.db();
              	DB db = StringUtils.hasText(dbname) ? mongoDbFactory.getDb(dbname) : mongoDbFactory.getDb();
              	return new DBRef(db, collection, id);
              Suggested fix

              I suspect the line "id = wrapper.getProperty(idProperty, Object.class, useFieldAccessOnly);" should be replaced by this code, copied from writeInternal:
              Code:
              Object idObj = null;
              	Class<?>[] targetClasses = new Class<?>[] { ObjectId.class, String.class, Object.class };
              	for (Class<?> targetClass : targetClasses) {
              		try {
              			idObj = wrapper.getProperty(idProperty, targetClass, useFieldAccessOnly);
              			if (null != idObj) {
              				break;
              			}
              		} catch (ConversionException ignored) {
              		} catch (IllegalAccessException e) {
              			throw new MappingException(e.getMessage(), e);
              		} catch (InvocationTargetException e) {
              			throw new MappingException(e.getMessage(), e);
              		}
              	}
              
              	if (null != idObj) {
              		dbo.put("_id", idObj);
              	} else {
              		if (!VALID_ID_TYPES.contains(idProperty.getType())) {
              			throw new MappingException("Invalid data type " + idProperty.getType().getName()
              					+ " for Id property. Should be one of " + VALID_ID_TYPES);
              		}
              	}

              Comment


              • #8
                I did see that defect after my initial post, thanks -- it was one of the pieces of evidence I used to find the workaround. Unfortunately, it doesn't do me any good until the next version of spring-data rolls out with that code (I tried the latest snapshot, which didn't have any effect). I think I certainly was able to prove that the theory that the $id value is being stored incorrectly is true, at least in my instance, and my work around enables me to move forward for now.

                M

                Comment

                Working...
                X