Skip to content

Commit 00e48cc

Browse files
christophstroblThomas Darimont
authored andcommitted
DATAMONGO-1050 - Explicitly annotated Field should not be considered Id.
We changed the id resolution to skip properties having an explicit name set via @field unless they are marked with @id. This means that @field(“id”) String id; will be stored as “id” within mongodb. Prior to this change the fieldname would have been changed to “_id”. Added tests to ensure proper field mapping for various "id" field variants. Original pull request: spring-projects#225.
1 parent f845382 commit 00e48cc

File tree

4 files changed

+204
-21
lines changed

4 files changed

+204
-21
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ public boolean isIdProperty() {
100100
}
101101

102102
// We need to support a wider range of ID types than just the ones that can be converted to an ObjectId
103-
return SUPPORTED_ID_PROPERTY_NAMES.contains(getName());
103+
// but still we need to check if there happens to be an explicit name set
104+
return SUPPORTED_ID_PROPERTY_NAMES.contains(getName()) && !hasExplicitFieldName();
104105
}
105106

106107
/*
@@ -134,10 +135,8 @@ public String getFieldName() {
134135
}
135136
}
136137

137-
org.springframework.data.mongodb.core.mapping.Field annotation = findAnnotation(org.springframework.data.mongodb.core.mapping.Field.class);
138-
139-
if (annotation != null && StringUtils.hasText(annotation.value())) {
140-
return annotation.value();
138+
if (hasExplicitFieldName()) {
139+
return getAnnotatedFieldName();
141140
}
142141

143142
String fieldName = fieldNamingStrategy.getFieldName(this);
@@ -150,6 +149,26 @@ public String getFieldName() {
150149
return fieldName;
151150
}
152151

152+
/**
153+
* @return true if {@link org.springframework.data.mongodb.core.mapping.Field} having non blank
154+
* {@link org.springframework.data.mongodb.core.mapping.Field#value()} present.
155+
* @since 1.7
156+
*/
157+
protected boolean hasExplicitFieldName() {
158+
return StringUtils.hasText(getAnnotatedFieldName());
159+
}
160+
161+
private String getAnnotatedFieldName() {
162+
163+
org.springframework.data.mongodb.core.mapping.Field annotation = findAnnotation(org.springframework.data.mongodb.core.mapping.Field.class);
164+
165+
if (annotation != null && StringUtils.hasText(annotation.value())) {
166+
return annotation.value();
167+
}
168+
169+
return null;
170+
}
171+
153172
/*
154173
* (non-Javadoc)
155174
* @see org.springframework.data.mongodb.core.mapping.MongoPersistentProperty#getFieldOrder()

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383

8484
import com.mongodb.BasicDBList;
8585
import com.mongodb.BasicDBObject;
86+
import com.mongodb.BasicDBObjectBuilder;
8687
import com.mongodb.DB;
8788
import com.mongodb.DBObject;
8889
import com.mongodb.DBRef;
@@ -1869,6 +1870,81 @@ public void readShouldRespectExplicitFieldNameForDbRef() {
18691870
Mockito.any(DbRefResolverCallback.class), Mockito.any(DbRefProxyHandler.class));
18701871
}
18711872

1873+
/**
1874+
* @see DATAMONGO-1050
1875+
*/
1876+
@Test
1877+
public void writeShouldUseExplicitFieldnameForIdPropertyWhenAnnotated() {
1878+
1879+
RootForClassWithExplicitlyRenamedIdField source = new RootForClassWithExplicitlyRenamedIdField();
1880+
source.id = "rootId";
1881+
source.nested = new ClassWithExplicitlyRenamedField();
1882+
source.nested.id = "nestedId";
1883+
1884+
DBObject sink = new BasicDBObject();
1885+
converter.write(source, sink);
1886+
1887+
assertThat((String) sink.get("_id"), is("rootId"));
1888+
assertThat((DBObject) sink.get("nested"), is(new BasicDBObjectBuilder().add("id", "nestedId").get()));
1889+
}
1890+
1891+
/**
1892+
* @see DATAMONGO-1050
1893+
*/
1894+
@Test
1895+
public void readShouldUseExplicitFieldnameForIdPropertyWhenAnnotated() {
1896+
1897+
DBObject source = new BasicDBObjectBuilder().add("_id", "rootId")
1898+
.add("nested", new BasicDBObject("id", "nestedId")).get();
1899+
1900+
RootForClassWithExplicitlyRenamedIdField sink = converter.read(RootForClassWithExplicitlyRenamedIdField.class,
1901+
source);
1902+
1903+
assertThat(sink.id, is("rootId"));
1904+
assertThat(sink.nested, notNullValue());
1905+
assertThat(sink.nested.id, is("nestedId"));
1906+
}
1907+
1908+
/**
1909+
* @see DATAMONGO-1050
1910+
*/
1911+
@Test
1912+
public void namedIdFieldShouldExtractValueFromUnderscoreIdField() {
1913+
1914+
DBObject dbo = new BasicDBObjectBuilder().add("_id", "A").add("id", "B").get();
1915+
1916+
ClassWithNamedIdField withNamedIdField = converter.read(ClassWithNamedIdField.class, dbo);
1917+
1918+
assertThat(withNamedIdField.id, is("A"));
1919+
}
1920+
1921+
/**
1922+
* @see DATAMONGO-1050
1923+
*/
1924+
@Test
1925+
public void explicitlyRenamedIfFieldShouldExtractValueFromIdField() {
1926+
1927+
DBObject dbo = new BasicDBObjectBuilder().add("_id", "A").add("id", "B").get();
1928+
1929+
ClassWithExplicitlyRenamedField withExplicitlyRenamedField = converter.read(ClassWithExplicitlyRenamedField.class,
1930+
dbo);
1931+
1932+
assertThat(withExplicitlyRenamedField.id, is("B"));
1933+
}
1934+
1935+
/**
1936+
* @see DATAMONGO-1050
1937+
*/
1938+
@Test
1939+
public void annotatedIdFieldShouldExtractValueFromUnderscoreIdField() {
1940+
1941+
DBObject dbo = new BasicDBObjectBuilder().add("_id", "A").add("id", "B").get();
1942+
1943+
ClassWithAnnotatedIdField withAnnotatedIdField = converter.read(ClassWithAnnotatedIdField.class, dbo);
1944+
1945+
assertThat(withAnnotatedIdField.key, is("A"));
1946+
}
1947+
18721948
static class GenericType<T> {
18731949
T content;
18741950
}
@@ -2128,6 +2204,32 @@ class ClassWithExplicitlyNamedDBRefProperty {
21282204
public ClassWithIntId getDbRefProperty() {
21292205
return dbRefProperty;
21302206
}
2207+
}
2208+
2209+
static class RootForClassWithExplicitlyRenamedIdField {
2210+
2211+
@Id String id;
2212+
ClassWithExplicitlyRenamedField nested;
2213+
}
2214+
2215+
static class ClassWithExplicitlyRenamedField {
2216+
2217+
@Field("id") String id;
2218+
}
2219+
2220+
static class RootForClassWithNamedIdField {
2221+
2222+
String id;
2223+
ClassWithNamedIdField nested;
2224+
}
2225+
2226+
static class ClassWithNamedIdField {
2227+
2228+
String id;
2229+
}
2230+
2231+
static class ClassWithAnnotatedIdField {
21312232

2233+
@Id String key;
21322234
}
21332235
}

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,34 @@ public void mapsIdReferenceToDBRefCorrectly() {
674674
assertThat(reference.getId(), is(instanceOf(ObjectId.class)));
675675
}
676676

677+
/**
678+
* @see DATAMONGO-1050
679+
*/
680+
@Test
681+
public void shouldUseExplicitlySetFieldnameForIdPropertyCandidates() {
682+
683+
Query query = query(where("nested.id").is("bar"));
684+
685+
DBObject dbo = mapper.getMappedObject(query.getQueryObject(),
686+
context.getPersistentEntity(RootForClassWithExplicitlyRenamedIdField.class));
687+
688+
assertThat(dbo, equalTo(new BasicDBObjectBuilder().add("nested.id", "bar").get()));
689+
}
690+
691+
/**
692+
* @see DATAMONGO-1050
693+
*/
694+
@Test
695+
public void shouldUseExplicitlySetFieldnameForIdPropertyCandidatesUsedInSortClause() {
696+
697+
Query query = new Query().with(new Sort("nested.id"));
698+
699+
DBObject dbo = mapper.getMappedSort(query.getSortObject(),
700+
context.getPersistentEntity(RootForClassWithExplicitlyRenamedIdField.class));
701+
702+
assertThat(dbo, equalTo(new BasicDBObjectBuilder().add("nested.id", 1).get()));
703+
}
704+
677705
@Document
678706
public class Foo {
679707
@Id private ObjectId id;
@@ -755,4 +783,15 @@ class WithTextScoreProperty {
755783
@Id String id;
756784
@TextScore @Field("score") Float textScore;
757785
}
786+
787+
static class RootForClassWithExplicitlyRenamedIdField {
788+
789+
@Id String id;
790+
ClassWithExplicitlyRenamedField nested;
791+
}
792+
793+
static class ClassWithExplicitlyRenamedField {
794+
795+
@Field("id") String id;
796+
}
758797
}

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,7 @@ public void rejectsInvalidValueReturnedByFieldNamingStrategy() {
130130
@Test
131131
public void shouldDetectAnnotatedLanguagePropertyCorrectly() {
132132

133-
BasicMongoPersistentEntity<DocumentWithLanguageProperty> persistentEntity = new BasicMongoPersistentEntity<DocumentWithLanguageProperty>(
134-
ClassTypeInformation.from(DocumentWithLanguageProperty.class));
135-
136-
MongoPersistentProperty property = getPropertyFor(persistentEntity, "lang");
133+
MongoPersistentProperty property = getPropertyFor(DocumentWithLanguageProperty.class, "lang");
137134
assertThat(property.isLanguageProperty(), is(true));
138135
}
139136

@@ -143,10 +140,7 @@ public void shouldDetectAnnotatedLanguagePropertyCorrectly() {
143140
@Test
144141
public void shouldDetectIplicitLanguagePropertyCorrectly() {
145142

146-
BasicMongoPersistentEntity<DocumentWithImplicitLanguageProperty> persistentEntity = new BasicMongoPersistentEntity<DocumentWithImplicitLanguageProperty>(
147-
ClassTypeInformation.from(DocumentWithImplicitLanguageProperty.class));
148-
149-
MongoPersistentProperty property = getPropertyFor(persistentEntity, "language");
143+
MongoPersistentProperty property = getPropertyFor(DocumentWithImplicitLanguageProperty.class, "language");
150144
assertThat(property.isLanguageProperty(), is(true));
151145
}
152146

@@ -156,10 +150,7 @@ public void shouldDetectIplicitLanguagePropertyCorrectly() {
156150
@Test
157151
public void shouldDetectTextScorePropertyCorrectly() {
158152

159-
BasicMongoPersistentEntity<DocumentWithTextScoreProperty> persistentEntity = new BasicMongoPersistentEntity<DocumentWithTextScoreProperty>(
160-
ClassTypeInformation.from(DocumentWithTextScoreProperty.class));
161-
162-
MongoPersistentProperty property = getPropertyFor(persistentEntity, "score");
153+
MongoPersistentProperty property = getPropertyFor(DocumentWithTextScoreProperty.class, "score");
163154
assertThat(property.isTextScoreProperty(), is(true));
164155
}
165156

@@ -169,17 +160,39 @@ public void shouldDetectTextScorePropertyCorrectly() {
169160
@Test
170161
public void shouldDetectTextScoreAsReadOnlyProperty() {
171162

172-
BasicMongoPersistentEntity<DocumentWithTextScoreProperty> persistentEntity = new BasicMongoPersistentEntity<DocumentWithTextScoreProperty>(
173-
ClassTypeInformation.from(DocumentWithTextScoreProperty.class));
174-
175-
MongoPersistentProperty property = getPropertyFor(persistentEntity, "score");
163+
MongoPersistentProperty property = getPropertyFor(DocumentWithTextScoreProperty.class, "score");
176164
assertThat(property.isWritable(), is(false));
177165
}
178166

167+
/**
168+
* @see DATAMONGO-1050
169+
*/
170+
@Test
171+
public void shouldNotConsiderExplicitlyNameFieldAsIdProperty() {
172+
173+
MongoPersistentProperty property = getPropertyFor(DocumentWithExplicitlyRenamedIdProperty.class, "id");
174+
assertThat(property.isIdProperty(), is(false));
175+
}
176+
177+
/**
178+
* @see DATAMONGO-1050
179+
*/
180+
@Test
181+
public void shouldConsiderPropertyAsIdWhenExplicitlyAnnotatedWithIdEvenWhenExplicitlyNamePresent() {
182+
183+
MongoPersistentProperty property = getPropertyFor(DocumentWithExplicitlyRenamedIdPropertyHavingIdAnnotation.class,
184+
"id");
185+
assertThat(property.isIdProperty(), is(true));
186+
}
187+
179188
private MongoPersistentProperty getPropertyFor(Field field) {
180189
return getPropertyFor(entity, field);
181190
}
182191

192+
private <T> MongoPersistentProperty getPropertyFor(Class<T> type, String fieldname) {
193+
return getPropertyFor(new BasicMongoPersistentEntity<T>(ClassTypeInformation.from(type)), fieldname);
194+
}
195+
183196
private MongoPersistentProperty getPropertyFor(MongoPersistentEntity<?> persistentEntity, String fieldname) {
184197
return getPropertyFor(persistentEntity, ReflectionUtils.findField(persistentEntity.getType(), fieldname));
185198
}
@@ -230,4 +243,14 @@ static class DocumentWithImplicitLanguageProperty {
230243
static class DocumentWithTextScoreProperty {
231244
@TextScore Float score;
232245
}
246+
247+
static class DocumentWithExplicitlyRenamedIdProperty {
248+
249+
@org.springframework.data.mongodb.core.mapping.Field("id") String id;
250+
}
251+
252+
static class DocumentWithExplicitlyRenamedIdPropertyHavingIdAnnotation {
253+
254+
@Id @org.springframework.data.mongodb.core.mapping.Field("id") String id;
255+
}
233256
}

0 commit comments

Comments
 (0)