5

Staram się utrzymywać następujący obiekt z spring-data-mongodb wersji 1.1.1.RELEASE:PersistenceConstructor Argument nazwa zmiennej nie pasuje instancji nazwę zmiennej

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

} 

ja dostać MappingException gdy próbuję odczytać obiekt z powrotem z bazy danych (zobacz pełny opis stosu poniżej)

Konwencja nazewnictwa, której używa moja grupa, wymaga podania nazw zmiennych argumentów przed prefiksem a_ i nazw zmiennych instancji, które mają być poprzedzone przez m_. Wygląda na to, że spring-data-mongodb przyjmuje założenie, że nazwy zmiennych argumentów konstruktora muszą być zgodne z nazwami instancji obiektu.

  • Dlaczego nie spring-data-mongodb użyć argumentu konstruktora instancji zmiennej mapowania że zdefiniowania w konstruktorze?
  • Czy istnieje inny sposób zdefiniowania tego odwzorowania w taki sposób, aby spring-data-mongodb poprawnie zbudował mój obiekt, czy też jest to moja jedyna opcja złamania konwencji nazewnictwa?

.

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) 
+0

To naprawdę marna konwencja nazewnictwa, która nie ma żadnego sensu. –

Odpowiedz

16

tl; dr

Musimy polegać na nazw argumentów konstruktora, aby dopasować nazwy pól, aby dowiedzieć się, które pola dokumentu ciągnąć się, jeśli chcesz, aby dostosować ten korzystanie @Value("#root.field_name") na argument konstruktora..

Długa historia

Jeśli używasz konstruktora z argumentami pozwolić Wiosna danych instancji danej klasy przy użyciu tego konstruktora mamy przekazać parametry do konstruktora po wywołaniu. Aby dowiedzieć się, które pole dokumentu musimy przekazać, musimy sprawdzić właściwość dopasowywania, aby umożliwić dostosowanie nazwy pola. Zobacz poniższy przykład:

@Document 
class MyEntity { 

    @Field("foo") 
    private String myField; 

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

W tym przypadku musimy rurze pole foo do konstruktora i nie ma sposobu, aby dowiedzieć się o tym, czy nie można jakoś uzyskać referencję do obiektu. Jeśli nazwa parametru konstruktora była inna, jak powinniśmy rzetelnie dowiedzieć się, która wartość pola powinna być rzeczywiście używana jako argument?Przykład, który pokazałeś w swoim pytaniu, może być gotowy do pracy po wyjęciu z pudełka, ponieważ twój dokument zawierałby pole m_property i nie ma absolutnie żadnego sposobu, aby dowiedzieć się, czy rzeczywiście chcesz go wstrzyknąć, z wyjątkiem dodania bardziej jawnej konfiguracji.

Aby dostosować to zachowanie, można użyć adnotacji Spring @Value i wprowadzić niestandardowe pole dokumentu do konstruktora. Sam dokument jest dostępny za pośrednictwem zmiennej #root. Więc można łatwo zmienić moje próbki wyżej:

@Document 
class MyEntity { 

    @Field("foo") 
    private String myField; 

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

bym zalecamy dodać niestandardowe pole do nazw swoich właściwości, jak nie chcesz, aby odsłonić swoje konwencje nazewnictwa nieruchomość do bazy danych. Sposób użycia pf @Value został krótko wymieniony w dokumencie reference docs, ale utworzyłem a ticket, aby poprawić dokumentację i uczynić to bardziej oczywistym.

+0

Jedno kolejne pytanie dla Ciebie - jeśli zrobię to, co sugerujesz, wydaje się działać dla obiektów, które zawierają tylko prymitywy, ale otrzymuję wyjątek, gdy próbuję użyć obiektów, które zawierają inne obiekty. Czy wiesz, co się tutaj dzieje? Podałem szczegóły w [Pytanie 13854753] (http://stackoverflow.com/questions/13854753) –

+0

FYI - Stworzyłem [DATAMONGO-592] (https://jira.springsource.org/browse/DATAMONGO -592), aby śledzić kwestię braku możliwości używania obiektów zawierających inne obiekty. Dzięki za pomoc!! –

+0

Och, jak ja kocham stackoverflow i tych, którzy w nim uczestniczą! Mogłem spędzić kilka dni próbując dowiedzieć się, co jest nie tak, po prostu uratowałeś mnie przez cały ten czas i frustrację. Dziękuję Ci! – TheZuck

0

można korzystać z niektórych niestandardowych konwerterów (i usunąć @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(); 
    } 
} 

Nie zapomnij zadeklarować te (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> 

widzieć this reference

Notatka: to obejść, nie sądzę nazewnictwo mają być tak ciasne, że musisz się obejść. Być może nadszedł czas, aby twoja grupa "przemyślała" te konwencje nazewnictwa (w tym przypadku ze względu na produktywność).