PersistenceConstructor参数变量名不匹配实例变量名。

时间:2022-09-11 16:52:17

I'm trying to persist the following object with spring-data-mongodb version 1.1.1.RELEASE:

我试图用spring-data-mongodb版本1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.

@Document
public static class TestObject {

    private final int m_property;

    @PersistenceConstructor
    public TestObject(int a_property) {
        m_property = a_property;
    }

    public int property() {
        return m_property;
    }

}

I get a MappingException when I try to read the object back from the database (see full stacktrace below)

当我试图从数据库中读取对象时,我得到了一个MappingException(请参阅下面的完整堆栈跟踪)

The naming convention my group uses requires argument variable names to be prefaced by a_ and instance variable names to be prefaced by m_. It seems like spring-data-mongodb is making the assumption that the constructor argument variable names must match the object instance variable names.

我的组使用的命名约定要求参数变量名被a_和实例变量名预先设置为m_。这似乎是spring-data-mongodb的假设:构造函数参数变量名称必须与对象实例变量名相匹配。

  • Why doesn't spring-data-mongodb use the constructor argument to instance variable mapping that I define within the constructor?
  • 为什么spring-data-mongodb不使用构造函数参数来定义构造函数中定义的变量映射?
  • Is there another way to define this mapping such that spring-data-mongodb will properly construct my object, or is my only option to break the naming convention?
  • 是否有另一种方法来定义这种映射,例如spring-data-mongodb将正确地构造我的对象,或者是我唯一的选择来打破命名约定?

.

Exception in thread "main" org.springframework.data.mapping.model.MappingException: No property a_property found on entity class com.recorder.TestRecorder$TestObject to bind constructor parameter to!
    at org.springframework.data.mapping.model.PersistentEntityParameterValueProvider.getParameterValue(PersistentEntityParameterValueProvider.java:90)
    at org.springframework.data.convert.ReflectionEntityInstantiator.createInstance(ReflectionEntityInstantiator.java:70)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:229)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:209)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:173)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:169)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:72)
    at org.springframework.data.mongodb.core.MongoTemplate$ReadDbObjectCallback.doWith(MongoTemplate.java:1820)
    at org.springframework.data.mongodb.core.MongoTemplate.executeFindMultiInternal(MongoTemplate.java:1542)
    at org.springframework.data.mongodb.core.MongoTemplate.findAll(MongoTemplate.java:1064)
    at com.recorder.TestRecorder.main(TestRecorder.java:43)

2 个解决方案

#1


16  

tl;dr

博士tl;

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.

我们需要依赖构造函数参数名来匹配字段名,以找出要引入文档的哪个字段。如果您想在构造函数参数上定制这个使用@Value(“#root.field_name”)。

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:

如果您使用带有参数的构造函数来让Spring数据实例化给定的类,那么我们必须在调用时将参数传递给构造函数。为了找出我们需要提交的文档字段,我们需要检查匹配的属性以进行潜在的字段名定制。看下面的例子:

@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.

在这种情况下,我们需要将字段foo输入到构造函数中,如果我们不能获得对该属性的引用,就没有办法知道这一点。如果构造函数参数的名称是不同的,那么我们应该如何可靠地找到应该使用哪个字段值作为参数呢?在你的问题中,你所展示的例子永远不会跳出这个盒子,因为你的文档中包含一个m_property字段,而且绝对没有办法找到你想要注入的内容,除非添加更明确的配置。

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:

要定制此行为,您可以使用Spring的@Value注释并将自定义文档字段注入到构造函数中。文档本身通过#root变量可用。所以你可以很容易地将我的样本修改为:

@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.

我强烈建议您在属性中添加自定义字段名,同时也不希望将属性命名约定公开给数据库。在参考文档中简要地提到了使用pf @Value,但我已经创建了一张罚单来改进文档并使其更加明显。

#2


0  

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

您可以使用一些自定义转换器(并删除@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):

不要忘记声明那些(xml配置):

<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).

附注:这是一项工作,我不认为命名约定是如此紧密以至于你需要在周围工作。也许是时候让您的团队“重新考虑”那些命名约定了(在这种情况下,为了提高生产率)。

#1


16  

tl;dr

博士tl;

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.

我们需要依赖构造函数参数名来匹配字段名,以找出要引入文档的哪个字段。如果您想在构造函数参数上定制这个使用@Value(“#root.field_name”)。

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:

如果您使用带有参数的构造函数来让Spring数据实例化给定的类,那么我们必须在调用时将参数传递给构造函数。为了找出我们需要提交的文档字段,我们需要检查匹配的属性以进行潜在的字段名定制。看下面的例子:

@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.

在这种情况下,我们需要将字段foo输入到构造函数中,如果我们不能获得对该属性的引用,就没有办法知道这一点。如果构造函数参数的名称是不同的,那么我们应该如何可靠地找到应该使用哪个字段值作为参数呢?在你的问题中,你所展示的例子永远不会跳出这个盒子,因为你的文档中包含一个m_property字段,而且绝对没有办法找到你想要注入的内容,除非添加更明确的配置。

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:

要定制此行为,您可以使用Spring的@Value注释并将自定义文档字段注入到构造函数中。文档本身通过#root变量可用。所以你可以很容易地将我的样本修改为:

@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.

我强烈建议您在属性中添加自定义字段名,同时也不希望将属性命名约定公开给数据库。在参考文档中简要地提到了使用pf @Value,但我已经创建了一张罚单来改进文档并使其更加明显。

#2


0  

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

您可以使用一些自定义转换器(并删除@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):

不要忘记声明那些(xml配置):

<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).

附注:这是一项工作,我不认为命名约定是如此紧密以至于你需要在周围工作。也许是时候让您的团队“重新考虑”那些命名约定了(在这种情况下,为了提高生产率)。