这篇文章在 spring data mongodb基础篇 的基础上做的扩展
思路:实现org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener<Object>中的onBeforeConvert()方法,mongodb在保存业务对象之前,会先把业务对象转化为DBObject,通过日志可以发现 onBeforeConvert() 在onBeforeSave()之前调用:所以我们可以在保存对象之前处理关联对象,覆盖 onBeforeConvert() 方法。
更通用的做法是覆盖onApplicationEvent(),这个方法在所有的事件之前都会执行,可以根据不同的事件类型做不同的操作,我们甚至还可以实现级联删除操作。
com.wss.lsl.pay.demo.model.User 实体对象加
@DBRef // mongodb的注解,文档之间建立关联关系,可以认为是关系型数据库中的外键
@CascadeSave // 自定义的注解
@Field("card")
private Card card;
// getter/setter
com.wss.lsl.pay.demo.model.Card实体对象:
@Document(collection = "card")
public class Card {
@Id
private String id; // mongodb的主键
private String cardNo; // 卡号
// getter/setter
}
自定义的注解com.wss.lsl.pay.demo.common.annotation.CascadeSave
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 CascadeSave {
}
com.wss.lsl.pay.demo.listener.CascadeSaveMongoEventListener实现了 AbstractMongoEventListener
package com.wss.lsl.pay.demo.listener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener;
import org.springframework.util.ReflectionUtils;
import com.mongodb.DBObject;
import com.wss.lsl.pay.demo.common.callback.CascadeSaveCallback;
public class CascadeSaveMongoEventListener extends AbstractMongoEventListener<Object> {
@Autowired
private MongoTemplate mongoTemplate;
@Override
public void onBeforeConvert(Object source) {
ReflectionUtils.doWithFields(source.getClass(), new CascadeSaveCallback(source, mongoTemplate));
super.onBeforeConvert(source);
}
}
com.wss.lsl.pay.demo.common.callback.CascadeSaveCallback
package com.wss.lsl.pay.demo.common.callback;
import java.lang.reflect.Field;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.util.ReflectionUtils;
import com.wss.lsl.pay.demo.common.annotation.CascadeSave;
public class CascadeSaveCallback implements ReflectionUtils.FieldCallback {
private Object source;
private MongoTemplate mongoTemplate;
public CascadeSaveCallback(Object source, MongoTemplate mongoTemplate) {
super();
this.source = source;
this.mongoTemplate = mongoTemplate;
}
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
ReflectionUtils.makeAccessible(field);
if(field.isAnnotationPresent(DBRef.class) && field.isAnnotationPresent(CascadeSave.class)){
final Object fieldValue = field.get(source);
if(fieldValue != null){
FieldCallback fc = new FieldCallback();
ReflectionUtils.doWithFields(fieldValue.getClass(), fc);
mongoTemplate.save(fieldValue);
}
}
}
}
com.wss.lsl.pay.demo.common.callback.FieldCallback
package com.wss.lsl.pay.demo.common.callback;
import java.lang.reflect.Field;
import org.springframework.data.annotation.Id;
import org.springframework.util.ReflectionUtils;
public class FieldCallback implements ReflectionUtils.FieldCallback {
private boolean isFound;
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
ReflectionUtils.makeAccessible(field);
if (field.isAnnotationPresent(Id.class)) {
isFound = true;
}
}
public boolean isFound() {
return isFound;
}
}
spring的配置文件applicationContext.xml加
<!-- cascade operation -->
<bean class="com.wss.lsl.pay.demo.listener.CascadeSaveMongoEventListener" />
单元测试:UserRepositoryTest中添加测试方法
// 测试级联操作
@Test
public void testCascade() {
User user = new User("有会员卡的用户", 20);
Card card = new Card("1323232");
user.setCard(card);
// 我们只保存了user
userRepository.save(user);
// 校验card确实保存进去了:card产生了id
Assert.assertNotNull(card.getId());
}