PersistenceConstructor argument variable name doesn't match instance variable name

梦想的初衷 提交于 2019-12-01 06:03:58

tl;dr

We need to rely on constructor argument names to match field names to find out which field of the document to pull in. If you want to customize this use @Value("#root.field_name") on the constructor argument.

Long story

If you're using a constructor with arguments to let Spring Data instantiate the given class using this constructor we have to hand parameters to the constructor upon invocation. To find out which document field we have to hand in, we need to inspect the matching property for potential field name customization. See the following example:

@Document
class MyEntity {

  @Field("foo")
  private String myField;

  public MyEntity(String myField) {
    this.myField = myField;
  }
}

In this case we need to pipe the field foo into the constructor and there's no way to find out about this if we don't somehow can obtain a reference to the property. If the constructor parameter name was something different, how should we reliably find out which field value should actually be used as argument? The example you've shown in your question can never work out of the box, as your document would contain a m_property field and there's absolutely no way to find out you actually want that to be injected, except adding more explicit configuration.

To customize this behavior you can use Spring's @Value annotation and inject a custom document field into the constructor. The document itself is available through the #root variable. So you could easily alter my sample above to:

@Document
class MyEntity {

  @Field("foo")
  private String myField;

  public MyEntity(@Value("#root.foo") String somethingDifferent) {
    this.myField = somethingDifferent;
  }
}

I'd strongly recommend that you add custom field names to your properties as well as you don't want to expose your property naming conventions to the database. The usage pf @Value is briefly mentioned in the reference docs but I've created a ticket to improve the docs and make this more obvious.

You can use some custom converters (and remove @PersistenceConstructor):

// DB => Java
package com.recorder.converters;

public class TestObjectReadConverter implements Converter<DBObject, TestObject> 
{
   public TestObject convert(final DBObject source) {
       return new TestObject((Integer) source.get("m_property"));
   }
}

.

// JAVA => DB
package com.recorder.converters;

public class TestObjectWriteConverter implements Converter<TestObject, DBObject> 
{
    public DBObject convert(final TestObject source) {
        return new BasicDBObjectBuilder("m_property", source.property()).get();
    }
}

Don't forget to declare those (xml config):

<mongo:mapping-converter base-package="com.recorder">
    <mongo:custom-converters>
        <mongo:converter>
             <bean class="com.recorder.converters.TestObjectReadConverter" />
        </mongo:converter>
        <mongo:converter>
             <bean class="com.recorder.converters.TestObjectWriteConverter"/>
        </mongo:converter>
    </mongo:custom-converters>
</mongo:mapping-converter>

see this reference

Side note: this is a work around, I don't think naming convention are meant to be so tight that you need to work around. Perhaps it's time for your group to "rethink" those naming convention (for productivity sake in that case).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!