今天我们学习下DBRef的使用,用过mongodb的都知道mongodb不能做关联查询,关系型数据库中是可以的,当然我们不要用关系型数据库的思想来用nosql。
但是实际应用中也是会有类似的需求的。
我们就以学生和班级的关系来讲解一对一以及一对多的关联操作。
- 一个班级有多个学生,班级对学生是一对多的关系
- 一个学生属于一个班级,学生对班级是一对一的关系
如果用mysql那么就是下面2张表: 班级表: classId className 学生表: studentId studentName classId
查询学生信息带出班级信息的查询也方便
代码语言:javascript复制select * from 班级 inner join 学生 on 班级.classId=学生.classId
用mongodb要如何设计集合呢???
班级集合中嵌套学生信息:
代码语言:javascript复制{
"_id": ObjectId("57fa3f22d4c63d5f5c416ee2"),
"className": "五年级一班",
"students": [
{
"studentId": "1001",
"studentName": "张三"
},
{
"studentId": "1002",
"studentName": "张三2"
}
]
}
上面的嵌套在学生数量有限的情况下是可以的,如果量大超过16M的时候就不适用了,学生有很多信息,我这边只列了简单的。
为了减少文档的大小,那么能不能像mysql一样,之存储id然后做关联呢?
在mongodb中可以使用DBRef来关联
定义要用到的实体类
代码语言:javascript复制@Document
public class Class {
@Id
private String id;
//班级名称
private String className;
//开班时间
private Date openDate;
//引用学生信息
@DBRef
private List<Student> students;
}
@Document
public class Student {
@Id
private String id;
//学生姓名
private String stuName;
//引用班级
@DBRef
private Class classObj;
}
保存数据的时候先保存班级数据,班级有了学生对象中的班级才能引用到,因为引用是通过_id来的。
代码语言:javascript复制Class classObj = new Class();
classObj.setClassName("五年级一班");
classObj.setOpenDate(new Date());
mongoTemplate.save(classObj);
Student student = new Student();
student.setStuName("张学生");
student.setClassObj(classObj);
mongoTemplate.save(student);
我们可以看到保存后的数据在学生集合中有DBRef引用class中的57fa4b99d4c68bb7d044d616
代码语言:javascript复制db.class.find();
{
"_id": ObjectId("57fa4b99d4c68bb7d044d616"),
"className": "五年级一班",
"openDate": ISODate("2016-10-09T13:52:25.678Z")
}
db.student.find();
{
"_id": ObjectId("57fa4b99d4c68bb7d044d617"),
"stuName": "张学生",
"classObj": DBRef("class",
ObjectId("57fa4b99d4c68bb7d044d616"))
}
然后我们查询这个学生的信息就可以自动带出班级的信息了,用过hibernate的一看就知道哈。。
代码语言:javascript复制//自动带出班级信息
Student student = mongoTemplate.findOne(Query.query(Criteria.where("stuName").is("张学生")), Student.class);
System.out.println(student.getStuName() "t" student.getClassObj().getClassName());
上面将的是一对一的操作,一对多的话就比较麻烦了
代码语言:javascript复制Class classObj = new Class();
classObj.setClassName("五年级二班");
classObj.setOpenDate(new Date());
List<Student> students = new ArrayList<>();
Student student = new Student();
student.setStuName("李学生");
student.setClassObj(classObj);
students.add(student);
Student student2 = new Student();
student2.setStuName("王学生");
student2.setClassObj(classObj);
students.add(student2);
classObj.setStudents(students);
mongoTemplate.save(student);
mongoTemplate.save(student2);
mongoTemplate.save(classObj);
我们看上面这段代码,大家觉得这段代码能执行成功吗?不能...
上面也说了,引用一定要引用已经插入到数据的数据。
这边先保存学生信息,学生中引用了班级,班级还没保存
先保存班级信息的话,班级中引用了学生,学生此时还没保存
如果引用没保存的信息就会报错
代码语言:javascript复制Exception in thread "main" org.springframework.data.mapping.model.MappingException: Cannot create a reference to an object with a NULL id.
这样不行,我们就只能曲线救国了
从业务上来说首先肯定是开班级,班级有了再招生
我们就利用上面已经存在的五年级一班来添加学生
代码语言:javascript复制Student student = new Student();
student.setStuName("李学生");
Class classObj = mongoTemplate.findOne(
Query.query(Criteria.where("className").is("五年级一班")),Class.class);
student.setClassObj(classObj);
mongoTemplate.save(student);
List<Student> students = classObj.getStudents();
if (students == null) {
students = new ArrayList<>();
}
students.add(student);
classObj.setStudents(students);
mongoTemplate.save(classObj);
在李学生加入班级后,马上把班级集合中的学生List对象改掉,这样班级中也就存在了对这个学生的引用信息, 这样就会比较麻烦。
代码语言:javascript复制{
"_id": ObjectId("57fa4b99d4c68bb7d044d616"),
"className": "五年级一班",
"openDate": ISODate("2016-10-09T13:52:25.678Z"),
"students": [
DBRef("student",
ObjectId("57fa4f59d4c6731d0c83f933"))
]
}
我们在查询班级的时候就可以关联出这个班级下所有的学生信息了
代码语言:javascript复制Class classObj = mongoTemplate.findOne(
Query.query(Criteria.where("className").is("五年级一班")),Class.class);
classObj.getStudents().forEach(stu -> {
System.err.println(stu.getStuName());
});
搞个一对多这么麻烦,还不如不弄呢,,不要急,条条大路通罗马,这条路不通,还有别的路啊。
我们的需求无非就是想知道某个班级下有多少个学生吗?如果不用关联的话就自己查呗,查的话我们没在学生集合中单独存储班级的id啊,引用里不是有id吗,就用那个查,但是要注意语法classObj.$id
代码语言:javascript复制mongoTemplate.find(
Query.query(Criteria.where("classObj.$id")
.is(new ObjectId("57fa4b99d4c68bb7d044d616"))), Student.class)
.forEach(stu -> {
System.err.println(stu.getStuName());
});
源码地址:https://github.com/yinjihuan/cxytiandi