使用@DBRef时默认只会保存当前对象中的@DBRef对象,而不会保存嵌套对象中被@DBRef注解的类,例如:
//用户
@Document(collection = "Persons")
@Data
public class Person {
@DBRef(lazy = true)
private Book book;
}
//图书
@Document(collection = "Books")
@Data
public class Book {
@DBRef(lazy = true)
private Chapter chapter;
}
//章节
@Document(collection = "Chapters")
@Data
public class Chapter {...}
当使用上面的示例保存Person时,会报以下错误,原因是:被引用的对象的id属性为null
org.springframework.data.mapping.MappingException: Cannot create a reference to an object with a NULL id.
出现MappingException
异常的原因:
- 被引用的对象在保存到数据库之前没有生成id。确保在保存被引用对象之前为其生成一个唯一的id。
- 被引用的对象在数据库中的id属性为null。在使用
@DBRef
进行查询时,被引用对象的id属性不能为空,否则无法创建引用。 - 引用关系配置错误。请确保在实体类之间的引用关系配置正确,包括正确使用
@DBRef
注解,以及引用对象的id属性和关联字段的映射关系正确。
AbstractMongoEventListener是什么?
AbstractMongoEventListener是Spring Data MongoDB框架提供的一个抽象类,用于监听MongoDB的事件并执行相应的操作。它提供了一组钩子方法,可以在MongoDB的不同事件发生时进行回调。
AbstractMongoEventListener的主要作用是允许开发人员在MongoDB的不同操作(如插入、更新、删除等)发生时执行自定义的逻辑。通过继承AbstractMongoEventListener并覆盖相应的方法,我们可以在特定操作之前、之后或在特定的操作上执行自定义操作。具体来说,AbstractMongoEventListener提供了以下一些方法:
-
onBeforeConvert
: 当将领域对象转换为持久化文档之前,调用此方法。 -
onBeforeSave
: 当将领域对象保存到数据库之前,调用此方法。 -
onAfterSave
: 在将领域对象保存到数据库之后,调用此方法。 -
onAfterLoad
: 在从数据库加载领域对象后,调用此方法。 -
onBeforeDelete
: 在删除领域对象之前,调用此方法。 -
onAfterDelete
: 在删除领域对象之后,调用此方法。
通过覆盖这些方法,可以实现一些额外的业务逻辑,如在保存对象之前进行数据验证、在对象保存之后执行一些后处理操作等。此外,AbstractMongoEventListener还可以扩展为自定义事件监听器,监听其他MongoDB事件(如索引创建、索引删除等)。
需要注意的是,为了使用AbstractMongoEventListener,我们需要将其注册为Spring的Bean,并将其与MongoTemplate或MongoRepository关联起来,以便框架在适当的时机调用监听器的方法。
自定义监听onBeforeConvert,实现级联存储
import com.sun.istack.internal.NotNull;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener;
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.List;
/**
* mongoDB实现级联存储,支持List列表级联存储
*/
@Component
public class CascadingMongoEventListener extends AbstractMongoEventListener {
@Resource
private MongoOperations mongoOperations;
@Override
public void onBeforeConvert(BeforeConvertEvent event) {
Object document = event.getSource();
ReflectionUtils.doWithFields(document.getClass(), new CascadeCallback(document, mongoOperations));
}
private static class CascadeCallback implements ReflectionUtils.FieldCallback {
private final Object source;
private final MongoOperations mongoOperations;
public CascadeCallback(Object source, MongoOperations mongoOperations) {
this.source = source;
this.mongoOperations = mongoOperations;
}
@Override
public void doWith(@NotNull Field field) throws IllegalArgumentException, IllegalAccessException {
ReflectionUtils.makeAccessible(field);
if (!field.isAnnotationPresent(DBRef.class)) {
return;
}
Object fieldValue = field.get(source);
if(fieldValue == null) {
return;
}
if(fieldValue instanceof List) {
List<?> list = (List<?>) fieldValue;
list.forEach(mongoOperations::save);
} else {
mongoOperations.save(fieldValue);
}
}
}
}
使用
创建 Person
、Book
、Chapter
@Document(collection = "Persons")
@Data
public class Person {
@JsonSerialize(using = ObjectIdSerializer.class)
@JsonDeserialize(using = ObjectIdDeserializer.class)
@Id
private ObjectId id;
private String name;
@DBRef(lazy = true)
private Book book;//支持List<Book>
}
@Document(collection = "Books")
@Data
public class Book {
@JsonSerialize(using = ObjectIdSerializer.class)
@JsonDeserialize(using = ObjectIdDeserializer.class)
@Id
private ObjectId id;
private String name;
private Double price;
@DBRef(lazy = true)
private Chapter chapter;//支持List<Chapter>
}
@Document(collection = "Chapters")
@Data
public class Chapter {
@JsonSerialize(using = ObjectIdSerializer.class)
@JsonDeserialize(using = ObjectIdDeserializer.class)
@Id
private ObjectId id;
private String name;
private String content;
}
验证
curl --location 'http://localhost:8080/person' \
--header 'Content-Type: application/json' \
--data '{
"name":"小明",
"book":{
"name":"Go语言入门",
"price":39.9,
"chapter":{
"name":"第一章",
"content":"认识GO初级语法"
}
}
}'
总结文章来源:https://www.toymoban.com/news/detail-527902.html
使用@DBRef进行级联存储的方式就是通过继承AbstractMongoEventListener
并重写onBeforeConvert
方法在保存文档之前先保存子对象,就可以实现级联存储,需要注意,使用当前配置会全局生效,如果想要自定义级联存储可以使用自定义注解实现,扫描指定注解后进行级联存储。文章来源地址https://www.toymoban.com/news/detail-527902.html
到了这里,关于Spring Data MongoDB实现@DBRef级联存储的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!