使用@OrderBy对Spring Data MongoDB集合进行排序

这是关于调整和增强Spring Data MongoDB功能的第三篇文章。 这次,我发现我错过了一个JPA功能– @OrderBy批注。 @OrderBy指定在检索关联值时集合值关联的元素的顺序。

在本文中,我将展示如何使用Spring Data MongoDB使用@OrderBy批注实现排序 

用例

对于那些以前没有使用过JPA @OrderBy的人来说,这只是一个简短的例子。 我们这里有两个类和一对多关系:

package pl.maciejwalkowiak.springdata.mongodb.domain;

import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document
public class Backpack {
 @Id
 private ObjectId id;

 private List items;

 ...
}
public class Item {
 private String name;
 private int price;

 ...
}

背包在这里是主要类别,其中包含嵌入式物品清单。 从数据库中加载背包时,其项目将按接近插入顺序的顺序加载。 如果我们想更改它并按其字段之一订购商品怎么办? 我们需要自己实现排序,然后再次扩展AbstractMongoEventListener 。

排序详细信息:介绍@OrderBy

与JPA相反,在这种情况下,排序不能在数据库级别进行。 我们需要在应用程序端注意这一点–可以在两个地方完成:

  • 在将对象转换成MongoDB数据结构之前–如果我们要确保在MongoDB集合中正确对对象进行排序
  • 将对象从MongoDB数据结构转换为Java对象之后–如果我们只想确保应用程序内部的List正确排序

为了指定应该在哪个位置进行排序,我创建了SortPhase枚举:

public enum SortPhase {
 AFTER_CONVERT,
 BEFORE_CONVERT;
}

最后– @OrderBy批注将包含三个几乎自我描述的属性:

package pl.maciejwalkowiak.springdata.mongodb;

import org.springframework.data.mongodb.core.query.Order;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
public @interface OrderBy {
 /**
  * Field name
  */
 String value();
 Order order() default Order.ASCENDING;
 SortPhase[] phase() default SortPhase.AFTER_CONVERT;
}

实现SortingMongoEventListener

声明式排序必须使用反射。 为了保持代码可读性,我使用了commons-beanutils,但可以不使用它而手动完成。 在项目中添加以下依赖项:


 commons-beanutils
 commons-beanutils
 1.8.3



 commons-collections
 commons-collections
 3.2.1

最后一部分是SortingMongoEventListener实现:

package pl.maciejwalkowiak.springdata.mongodb;

import com.mongodb.DBObject;
import org.apache.commons.beanutils.BeanComparator;
import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener;
import org.springframework.data.mongodb.core.query.Order;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * MongoEventListener that intercepts object before its converted to BasicDBObject (before it is saved into MongoDB)
 * and after its loaded from MongoDB.
 *
 * @author Maciej Walkowiak
 */
public class SortingMongoEventListener extends AbstractMongoEventListener {
 @Override
 public void onAfterConvert(DBObject dbo, final Object source) {
  ReflectionUtils.doWithFields(source.getClass(), new SortingFieldCallback(source, SortPhase.AFTER_CONVERT));
 }

 @Override
 public void onBeforeConvert(Object source) {
  ReflectionUtils.doWithFields(source.getClass(), new SortingFieldCallback(source, SortPhase.BEFORE_CONVERT));
 }

 /**
  * Performs sorting with field if:
  * 
    *
  • field is an instance of list
  • *
  • is annotated with OrderBy annotation
  • *
  • OrderBy annotation is set to run in same phase as SortingFieldCallback
  • *
*/ private static class SortingFieldCallback implements ReflectionUtils.FieldCallback { private Object source; private SortPhase phase; private SortingFieldCallback(Object source, SortPhase phase) { this.source = source; this.phase = phase; } public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { if (field.isAnnotationPresent(OrderBy.class)) { OrderBy orderBy = field.getAnnotation(OrderBy.class); if (Arrays.asList(orderBy.phase()).contains(phase)) { ReflectionUtils.makeAccessible(field); Object fieldValue = field.get(source); sort(fieldValue, orderBy); } } } private void sort(Object fieldValue, OrderBy orderBy) { if (ClassUtils.isAssignable(List.class, fieldValue.getClass())) { final List list = (List) fieldValue; if (orderBy.order() == Order.ASCENDING) { Collections.sort(list, new BeanComparator(orderBy.value())); } else { Collections.sort(list, new BeanComparator(orderBy.value(), Collections.reverseOrder())); } } } } }

为了使用它,您只需要在应用程序上下文中将该类声明为Spring Bean:

现在是从这篇文章的开头将创建的OrderBy注释添加到Backpack类的时候了。 假设我们要按价格降序订购商品:

@Document
public class Backpack {
 @Id
 private ObjectId id;

 @OrderBy(value = "price", order = Order.DESCENDING)
 private List items;

 ...
}

而已。 现在,每次加载背包对象时(无论它的findAll,findOne还是您的自定义方法都没有关系),背包中的物品将被订购。  

摘要

SortingMongoEventListener是Spring Data MongoDB事件系统功能强大的另一个示例。 欢迎您发表评论,如果您认为此功能可能是Spring Data MongoDB的一部分,请告诉我。

参考: Software Development Journey博客上的JCG合作伙伴 Maciej Walkowiak 使用@OrderBy对Spring Data MongoDB集合进行排序 。


翻译自: https://www.javacodegeeks.com/2012/07/sorting-spring-data-mongodb-collections.html

你可能感兴趣的:(数据库,java,mysql,mongodb,spring)