转载

Spring Data MongoDB中的自定义级联

在使用Spring Data操作MongoDB中:

  • 在保存一个实体的时候,如果被@DBRef标识的类只传入Id,保存后返回的结果并没有全部的引用类内容,只有Id。
  • 保存实体,不能保存引用实体。

例如:我们有一个实体 Person ,有一个实体 EmailAddress

@Document(collection = "test_person")
public class Person{

    private String name;

    @DBRef
    private EmailAddress emailAddress;

    ... getter setter 方法
}
@Document(collection = "test_email")
public class EmailAddress{

    @Id
    private String id;

    private String value;

   ... getter setter 方法

}

当我们调用保存方法的时候:

public Person test(){
    Person person = new Person();
    person.setName("test");

    EmailAddress emailAddress = new EmailAddress();
    emailAddress.setId("5a05108d4dcc5dece03c9e69");

    person.setEmailAddress(emailAddress);

    testRepository.save(person);
    return person;
}

上述的代码中,返回的person只有id,没有emailAddress的其他值。

public Person test(){
    Person person = new Person();
    person.setName("test");

    EmailAddress emailAddress = new EmailAddress();
    emailAddress.setName("afafa");

    person.setEmailAddress(emailAddress);

    testRepository.save(person);
    return person;
}

上述的代码中,emailAddress不能被保存。

解决

生命周期事件

Spring Data MongoDB中存在一些生命周期事件,如:onBeforeConvert, onBeforeSave, onAfterSave, onAfterLoad and onAfterConvert等。我们可以继承 AbstractMappingEventListener ,然后重写这些方法,即可以实现。

代码

/**
 * MongoDB级联控制
 * Created by guanzhenxing on 2017/11/9.
 */
public class CascadeControlMongoEventListenerextends AbstractMongoEventListener<Object>{

    @Autowired
    private MongoOperations mongoOperations;

    @Override
    public void onAfterSave(AfterSaveEvent<Object> event){
        super.onAfterSave(event);
        Object source = event.getSource();
        ReflectionUtils.doWithFields(source.getClass(), new CascadeAfterSaveCallback(source, mongoOperations));
    }

    @Override
    public void onBeforeConvert(BeforeConvertEvent<Object> event){
        super.onBeforeConvert(event);
        Object source = event.getSource();
        ReflectionUtils.doWithFields(source.getClass(), new CascadeBeforeConvertCallback(source, mongoOperations));
    }
}
/**
 * 级联控制的回调
 * Created by guanzhenxing on 2017/11/10.
 */
public class CascadeAfterSaveCallbackimplements ReflectionUtils.FieldCallback{

    private Object source;
    private MongoOperations mongoOperations;

    public CascadeAfterSaveCallback(final Object source, final MongoOperations mongoOperations){
        this.source = source;
        this.mongoOperations = mongoOperations;
    }

    @Override
    public void doWith(final Field field)throws IllegalArgumentException, IllegalAccessException {
        ReflectionUtils.makeAccessible(field);

        if (field.isAnnotationPresent(DBRef.class)) {
            final Object fieldValue = field.get(source);   //获得值
            if (fieldValue != null) {
                doCascadeLoad(field);
            }
        }
    }

    /**
     * 级联查询
     *
     * @param field
     */
    private void doCascadeLoad(Field field)throws IllegalAccessException {
        Object fieldValue = field.get(source);
        List<Field> idFields = ReflectionUtil.getAnnotationField(fieldValue, Id.class); //该方法是为了获得所有的被@Id注解的属性
        if (idFields.size() == 1) { //只处理一个Id
            Object idValue = ReflectionUtil.getFieldValue(fieldValue, idFields.get(0).getName());
            Object value = mongoOperations.findById(idValue, fieldValue.getClass());    //查询获得值
            ReflectionUtil.setFieldValue(source, field.getName(), value);
        }
    }
}
public class CascadeBeforeConvertCallbackimplements ReflectionUtils.FieldCallback{


    private Object source;
    private MongoOperations mongoOperations;

    public CascadeBeforeConvertCallback(Object source, MongoOperations mongoOperations){
        this.source = source;
        this.mongoOperations = mongoOperations;
    }

    @Override
    public void doWith(Field field)throws IllegalArgumentException, IllegalAccessException {
        ReflectionUtils.makeAccessible(field);

        if (field.isAnnotationPresent(DBRef.class)) {
            final Object fieldValue = field.get(source);   //获得值
            if (fieldValue != null) {
                doCascadeSave(field);
            }
        }
    }

    /**
     * 级联保存
     *
     * @param field
     * @throws IllegalAccessException
     */
    private void doCascadeSave(Field field)throws IllegalAccessException {

        if (field.isAnnotationPresent(CascadeSave.class)) { //如果有标识@CascadeSave注解
            Object fieldValue = field.get(source);
            List<Field> idFields = ReflectionUtil.getAnnotationField(fieldValue, Id.class);
            if (idFields.size() == 1) {
                mongoOperations.save(fieldValue);
            }
        }

    }
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface CascadeSave {
}
@Configuration
public class MongoConfig{


    @Bean
    public CascadeControlMongoEventListener userCascadingMongoEventListener(){
        return new CascadeControlMongoEventListener();
    }

}

以上是核心代码。至此,我们就可以解决上述的问题了。

参考: http://www.baeldung.com/cascading-with-dbref-and-lifecycle-events-in-spring-data-mongodb
原文  http://webfuse.cn/2017/11/10/Spring Data MongoDB中的自定义级联/
正文到此结束
Loading...