在spring数据和mongodb中实现级联保存

时间:2016-03-24 07:39:52

标签: mongodb spring-data spring-data-mongodb

我实现了基于spring数据和mongodb的应用程序。

QATemplate.java

@Document(collection = "QATemplate")
public class QATemplate {

    @Id
    private String titleId;

    public String getTitleId() {
        return titleId;
    }

    public void setTitleId(String titleId) {
        this.titleId = titleId;
    }

    @Field("title")
    private String title;

    @Field("tags")
    private Object tags;

    @Field("answer_type")
    private String answerType;

    @Field("answer")
    @DBRef
    @CascadeSave
    private Object answer;

我有一个可以是简单文本的字段,也可以是嵌入文档。 所以我创建了该字段作为对象类型。

    @Field("answer")
    private Object answer;

工作得很好。 但我需要用自己的id保存嵌入式文档,并将其id作为参考传递给本文档,所以我必须像这样写我的代码

    @Field("answer")
    @DBRef
    @CascadeSave
    private Object answer;

用于实现我使用的级联保存

CasecadeSave.java

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface CascadeSave {

}

CasecadeCallback.java

public class CascadeCallback implements ReflectionUtils.FieldCallback {

    private Object source;
    private MongoOperations mongoOperations;

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

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

        if (field.isAnnotationPresent(DBRef.class) && field.isAnnotationPresent(CascadeSave.class)) {
            final Object fieldValue = field.get(getSource());

            boolean insta = !(fieldValue instanceof String);
            boolean instanull = fieldValue != null;

            if ( instanull && insta ) {
                final FieldCallback callback = new FieldCallback();
                ReflectionUtils.doWithFields(fieldValue.getClass(), callback);
                getMongoOperations().save(fieldValue);
            }
        }
    }

    public Object getSource() {
        return source;
    }

    public void setSource(final Object source) {
        this.source = source;
    }

    public MongoOperations getMongoOperations() {
        return mongoOperations;
    }

    public void setMongoOperations(final MongoOperations mongoOperations) {
        this.mongoOperations = mongoOperations;
    }
}

CascadeSaveMongoEventListener.java

public class CascadeSaveMongoEventListener extends AbstractMongoEventListener<Object> {

    @Autowired
    private MongoOperations mongoOperations;

    @Override
    public void onBeforeConvert(final Object source) {
        ReflectionUtils.doWithFields(source.getClass(), new CascadeCallback(source, mongoOperations));
    }
  }

FieldCallback.java

public class FieldCallback implements ReflectionUtils.FieldCallback {
    private boolean idFound;

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

        if (field.isAnnotationPresent(Id.class)) {
            idFound = true;
        }
    }

    public boolean isIdFound() {
        return idFound;
    }
}

但在应用此代码后如果我插入像这样的json数据

{
    "title":"Indiaa",
    "answerType":"text",
    "answer":[
            {
                 "title":"Indiaana jones",
                 "answerType":"text",
                 "answer":"testing vvv"
            }
        ]
}

我收到了一个错误:

threw exception [Request processing failed; 
nested exception is java.lang.ClassCastException: com.mongodb.BasicDBObject cannot be cast to com.mongodb.BasicDBList] with root cause
java.lang.ClassCastException: com.mongodb.BasicDBObject cannot be cast to com.mongodb.BasicDBList

如果我尝试插入这样的json数据:

{
    "title":"Indiava",
    "answerType":"text",
    "answer":"vv"
}

我收到了一个错误:

HTTP Status 500 - Request processing failed; 
nested exception is org.springframework.data.mapping.model.MappingException: No mapping metadata found for class java.lang.String

1 个答案:

答案 0 :(得分:1)

使用DBRef时,不是将answer对象保存在QATemplate内,而是仅保存对它的引用,但它位于另一个集合中:

{ "$ref" : "COLLECTION", "$id" : ObjectId("someobjectID") }

此外,你说你想要一个答案“文档”,但你实际上先传递一个数组,然后传递一个简单的字符串。

要使其运行,请创建一个包含答案文档和字符串答案的类型。这样Spring Data MongoDB将知道在哪个集合中放置它。首先尝试使用一个文档,如下所示:

{
    "title":"Where is India",
    "answerType":"text",
    "answer":{
        "text":"Asia"
    }
}

answer的位置:

@Field("answer")
@DBRef
@CascadeSave
private Answer answer;

Answer是:

@Document(collection = "Answers")
public class Answer {
    @Id
    private String id;

    String text;
}

运行此功能后,将Answer转换为界面,然后设置TextAnswer(简单文本)或FullAnswer(整个答案文档)。 Spring Data MongoDB支持多态性。