DATADOC-172 - Allow defining property order for document mapping.
Entities can now use @Field annotation to define the order using the order attribute. We will simply start with the lower values and proceed to the higher ones. Unannotated properties are ordered behind all annotated (and order declared) ones. One might not assume any particular order for fields where the order is not manually defined. Removed @FieldName as it is superseeded by @Field.
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.document.mongodb.geo;
|
||||
|
||||
import org.springframework.data.document.mongodb.mapping.Field;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -25,7 +26,9 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class Box {
|
||||
|
||||
@Field(order = 10)
|
||||
private final Point first;
|
||||
@Field(order = 20)
|
||||
private final Point second;
|
||||
|
||||
public Box(Point lowerLeft, Point upperRight) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.document.mongodb.geo;
|
||||
|
||||
import org.springframework.data.annotation.PersistenceConstructor;
|
||||
import org.springframework.data.document.mongodb.mapping.Field;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -26,7 +27,9 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class Point {
|
||||
|
||||
@Field(order = 10)
|
||||
private final double x;
|
||||
@Field(order = 20)
|
||||
private final double y;
|
||||
|
||||
@PersistenceConstructor
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.data.document.mongodb.mapping;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import org.springframework.data.document.mongodb.MongoCollectionUtils;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.model.BasicPersistentEntity;
|
||||
@@ -44,7 +46,7 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
|
||||
*/
|
||||
public BasicMongoPersistentEntity(TypeInformation<T> typeInformation) {
|
||||
|
||||
super(typeInformation);
|
||||
super(typeInformation, MongoPersistentPropertyComparator.INSTANCE);
|
||||
|
||||
Class<?> rawType = typeInformation.getType();
|
||||
String fallback = MongoCollectionUtils.getPreferredCollectionName(rawType);
|
||||
@@ -73,8 +75,35 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
|
||||
*/
|
||||
@Override
|
||||
public void verify() {
|
||||
if (isRootEntity && idProperty == null) {
|
||||
if (isRootEntity && getIdProperty() == null) {
|
||||
throw new MappingException(String.format("Root entity %s has to have an id property!", getType().getName()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Comparator} implementation inspecting the {@link MongoPersistentProperty}'s order.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
static enum MongoPersistentPropertyComparator implements Comparator<MongoPersistentProperty> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
public int compare(MongoPersistentProperty o1, MongoPersistentProperty o2) {
|
||||
|
||||
if (o1.getFieldOrder() == Integer.MAX_VALUE) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (o2.getFieldOrder() == Integer.MAX_VALUE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return o1.getFieldOrder() - o2.getFieldOrder();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,17 +21,19 @@ import java.math.BigInteger;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
|
||||
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Mongo specific {@link org.springframework.data.mapping.PersistentProperty} implementation.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class BasicMongoPersistentProperty extends AnnotationBasedPersistentProperty<MongoPersistentProperty> implements
|
||||
@@ -39,6 +41,7 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(BasicMongoPersistentProperty.class);
|
||||
|
||||
private static final String ID_FIELD_NAME = "_id";
|
||||
private static final Set<Class<?>> SUPPORTED_ID_TYPES = new HashSet<Class<?>>();
|
||||
private static final Set<String> SUPPORTED_ID_PROPERTY_NAMES = new HashSet<String>();
|
||||
|
||||
@@ -53,7 +56,7 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
|
||||
|
||||
/**
|
||||
* Creates a new {@link BasicMongoPersistentProperty}.
|
||||
*
|
||||
*
|
||||
* @param field
|
||||
* @param propertyDescriptor
|
||||
* @param owner
|
||||
@@ -63,8 +66,8 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
|
||||
MongoPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
|
||||
super(field, propertyDescriptor, owner, simpleTypeHolder);
|
||||
|
||||
if (isIdProperty() && field.isAnnotationPresent(FieldName.class)) {
|
||||
LOG.warn(String.format("Invalid usage of %s on id property. Field name will not be considered!", FieldName.class));
|
||||
if (isIdProperty() && getFieldName() != ID_FIELD_NAME) {
|
||||
LOG.warn("Customizing field name for id property not allowed! Custom name will not be considered!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +81,7 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
|
||||
|
||||
/**
|
||||
* Also considers fields as id that are of supported id type and name.
|
||||
*
|
||||
*
|
||||
* @see #SUPPORTED_ID_PROPERTY_NAMES
|
||||
* @see #SUPPORTED_ID_TYPES
|
||||
*/
|
||||
@@ -90,22 +93,31 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
|
||||
|
||||
// We need to support a wider range of ID types than just the ones that can be converted to an ObjectId
|
||||
return SUPPORTED_ID_PROPERTY_NAMES.contains(field.getName());
|
||||
//return SUPPORTED_ID_TYPES.contains(field.getType()) && SUPPORTED_ID_PROPERTY_NAMES.contains(field.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key to be used to store the value of the property inside a Mongo {@link DBObject}.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getFieldName() {
|
||||
|
||||
if (isIdProperty()) {
|
||||
return "_id";
|
||||
return ID_FIELD_NAME;
|
||||
}
|
||||
|
||||
FieldName annotation = getField().getAnnotation(FieldName.class);
|
||||
return annotation != null ? annotation.value() : getName();
|
||||
org.springframework.data.document.mongodb.mapping.Field annotation = getField().getAnnotation(
|
||||
org.springframework.data.document.mongodb.mapping.Field.class);
|
||||
return annotation != null && StringUtils.hasText(annotation.value()) ? annotation.value() : field.getName();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.document.mongodb.mapping.MongoPersistentProperty#getFieldOrder()
|
||||
*/
|
||||
public int getFieldOrder() {
|
||||
org.springframework.data.document.mongodb.mapping.Field annotation = getField().getAnnotation(
|
||||
org.springframework.data.document.mongodb.mapping.Field.class);
|
||||
return annotation != null ? annotation.order() : Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.springframework.data.document.mongodb.mapping;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Annotation to define custom metadata for document fields.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Field {
|
||||
|
||||
/**
|
||||
* The key to be used to store the field inside the document.
|
||||
* @return
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* The order in which various fields shall be stored. Has to be a positive integer.
|
||||
* @return the order the field shall have in the document or -1 if undefined.
|
||||
*/
|
||||
int order() default Integer.MAX_VALUE;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.document.mongodb.mapping;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation to allow defining the name of the field a property should use in a Mongo document. This will cause the
|
||||
* property annotated being persisted to a field with the configured name as wells as being read from it.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.FIELD })
|
||||
@Documented
|
||||
public @interface FieldName {
|
||||
|
||||
String value();
|
||||
}
|
||||
@@ -30,6 +30,13 @@ public interface MongoPersistentProperty extends PersistentProperty<MongoPersist
|
||||
* @return
|
||||
*/
|
||||
String getFieldName();
|
||||
|
||||
/**
|
||||
* Returns the order of the field if defined. Will return -1 if undefined.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
int getFieldOrder();
|
||||
|
||||
/**
|
||||
* Returns whether the propert is a {@link com.mongodb.DBRef}. If this returns {@literal true} you can expect
|
||||
|
||||
@@ -81,6 +81,14 @@ public class SimpleMongoMappingContext extends
|
||||
public String getFieldName() {
|
||||
return isIdProperty() ? "_id" : getName();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.document.mongodb.mapping.MongoPersistentProperty#getFieldOrder()
|
||||
*/
|
||||
public int getFieldOrder() {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.AbstractPersistentProperty#createAssociation()
|
||||
|
||||
@@ -62,6 +62,11 @@ public class BasicMongoPersistentPropertyUnitTests {
|
||||
assertThat(getPropertyFor(field).getFieldName(), is("lastname"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preventsNegativeOrder() {
|
||||
getPropertyFor(ReflectionUtils.findField(Person.class, "ssn"));
|
||||
}
|
||||
|
||||
private MongoPersistentProperty getPropertyFor(Field field) {
|
||||
return new BasicMongoPersistentProperty(field, null, entity, new SimpleTypeHolder());
|
||||
}
|
||||
@@ -71,8 +76,11 @@ public class BasicMongoPersistentPropertyUnitTests {
|
||||
@Id
|
||||
String id;
|
||||
|
||||
@FieldName("foo")
|
||||
@org.springframework.data.document.mongodb.mapping.Field("foo")
|
||||
String firstname;
|
||||
String lastname;
|
||||
|
||||
@org.springframework.data.document.mongodb.mapping.Field(order = -20)
|
||||
String ssn;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,7 +382,7 @@ public class MappingMongoConverterUnitTests {
|
||||
class Person implements Contact {
|
||||
LocalDate birthDate;
|
||||
|
||||
@FieldName("foo")
|
||||
@Field("foo")
|
||||
String firstname;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package org.springframework.data.document.mongodb.mapping;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.document.mongodb.mapping.BasicMongoPersistentEntity.MongoPersistentPropertyComparator;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link MongoPersistentPropertyComparator}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class MongoPersistentPropertyComparatorUnitTests {
|
||||
|
||||
@Mock
|
||||
MongoPersistentProperty firstName;
|
||||
|
||||
@Mock
|
||||
MongoPersistentProperty lastName;
|
||||
|
||||
@Mock
|
||||
MongoPersistentProperty ssn;
|
||||
|
||||
@Test
|
||||
public void ordersPropertiesCorrectly() {
|
||||
|
||||
when(ssn.getFieldOrder()).thenReturn(10);
|
||||
when(firstName.getFieldOrder()).thenReturn(20);
|
||||
when(lastName.getFieldOrder()).thenReturn(Integer.MAX_VALUE);
|
||||
|
||||
List<MongoPersistentProperty> properties = Arrays.asList(firstName, lastName, ssn);
|
||||
Collections.sort(properties, MongoPersistentPropertyComparator.INSTANCE);
|
||||
|
||||
assertThat(properties.get(0), is(ssn));
|
||||
assertThat(properties.get(1), is(firstName));
|
||||
assertThat(properties.get(2), is(lastName));
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -220,7 +219,6 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void findsPeopleByLocationWithinBox() {
|
||||
Point point = new Point(-73.99171, 40.738868);
|
||||
dave.setLocation(point);
|
||||
|
||||
Reference in New Issue
Block a user