首先,我们回忆一下,MySQL多表关联查询的语句:
student表:

CALSS表:

通过student的classId关联进行查询学生名称,班级的数据:

SELECT student.name,student.age,class.name FROM student,class WHERE student.classId = class.id

这个是典型的一对多,学生多方增加一个外键指向班级,hibernate、mybaties等java框架都支持这种多表查询,查询student实体就可以一块封装班级实体

Mongodb作为Nosql代表,其本身特性不建议对多Collection关联处理,不过对于有些需要对多表关联处理的需求,Mongodb也可以实现。主要分为两种方式:简单手工关联和DBRef方式关联

1.简单手工关联

下图表示帖子和用户两个Collection的ER图:

这种先把帖子对象查出来获得author_name字段,再根据该字段查询用户表;还有一种实现方式,手动创建id 让用户表和帖子表id一样,不过这样也要查询两次;手动这种方式对于一对一还可以使用,但一对多,多对多就比较吃力了,要查询很多次

2.DBRef方式关联

DBRefs具有以下字段:

$ref

         该$ref字段包含引用文档所在的集合的名称。

$id

        该$id字段包含_id引用文档中字段的值。

$db

      可选的。包含引用文档所在的数据库的名称。只有一些驱动程序支持$db引用,该字段说明可以跨集合关联

注意

DBRef中字段的顺序很重要,在使用DBRef时必须使用上面的顺序。

命令的方式进行操作:

我们先建立A collection。

> var a={value:"1"}  

> var b={value:"2"}  

> var c={value:"9"}  

> var d={value:"10"}  

> db.A.save(a)  

> db.A.save(b)        

> db.A.save(c)   

> db.A.save(d)  

> db.A.find()                                                                                                 

{ "_id" : ObjectId("4e3f33ab6266b5845052c02b"), "value" : "1" }  

{ "_id" : ObjectId("4e3f33de6266b5845052c02c"), "value" : "2" }  

{ "_id" : ObjectId("4e3f33e06266b5845052c02d"), "value" : "9" }  

{ "_id" : ObjectId("4e3f33e26266b5845052c02e"), "value" : "10" }  


B collection以A collection的  _id为 ObjectId("4e3f33de6266b5845052c02c")作为Apid

Apid是集合的B的一个字段,类型为内嵌数组类型,里面存储了 new DBRef('A',ObjectId("4e3f33de6266b5845052c02c"))

'A' 代表集合名字,后边是id  ,既然是数组当然可以存储多个new DBRef()  那就是一对多了

所以:

源码打印 

> var Ba={Apid:[new DBRef('A',ObjectId("4e3f33de6266b5845052c02c"))],value:3}                        

> db.B.save(Ba)  

> var Ba={Apid:[new DBRef('A',ObjectId("4e3f33de6266b5845052c02c"))],value:4}  

> db.B.insert(Ba)                                                              

> var Ba={Apid:[new DBRef('A',ObjectId("4e3f33de6266b5845052c02c"))],value:7}  

> db.B.insert(Ba)                                                              

> var Ba={Apid:[new DBRef('A',ObjectId("4e3f33de6266b5845052c02c"))],value:8}  

> db.B.insert(Ba)                                                              

> db.B.find()  

{ "_id" : ObjectId("4e3f3dd96266b5845052c035"), "Apid" : [ { "$ref" : "A", "$id" : ObjectId("4e3f33de6266b5845052c02c") } ], "value" : 3 }  

{ "_id" : ObjectId("4e3f3de16266b5845052c036"), "Apid" : [ { "$ref" : "A", "$id" : ObjectId("4e3f33de6266b5845052c02c") } ], "value" : 4 }  

{ "_id" : ObjectId("4e3f3dec6266b5845052c037"), "Apid" : [ { "$ref" : "A", "$id" : ObjectId("4e3f33de6266b5845052c02c") } ], "value" : 7 }  

{ "_id" : ObjectId("4e3f3df06266b5845052c038"), "Apid" : [ { "$ref" : "A", "$id" : ObjectId("4e3f33de6266b5845052c02c") } ], "value" : 8 } 

C collection以B collection的  _id为 ObjectId("4e3f3de16266b5845052c036") 作为Apid

 

源码打印

> var Ca={Bpid:[new DBRef('B',ObjectId("4e3f3de16266b5845052c036"))],value:5}                        

> db.C.save(Ca)                                                                

> var Ca={Bpid:[new DBRef('B',ObjectId("4e3f3de16266b5845052c036"))],value:6}  

> db.C.save(Ca)                                                                

> db.C.find()  

{ "_id" : ObjectId("4e3f42f36266b5845052c03d"), "Bpid" : [ { "$ref" : "B", "$id" : ObjectId("4e3f3de16266b5845052c036") } ], "value" : 5 }  

{ "_id" : ObjectId("4e3f42f96266b5845052c03e"), "Bpid" : [ { "$ref" : "B", "$id" : ObjectId("4e3f3de16266b5845052c036") } ], "value" : 6 }  

 

目前为止3个collection 的关系已经建成。

查询

var a = db.B.findOne({"value":4})                                                       

> a.Apid.forEach(

     function(ref){

                           printjson(db[ref.$ref].findOne({"_id":ref.$id}));

                     })  

结果 :{ "_id" : ObjectId("4e3f33de6266b5845052c02c"), "value" : "2" }

db.B.findOne({"name": "CSL"}).Apid[0].fetch();//这两种命令的查询方式也行

db.B.find({"name": "CSL"})[0].Apid[0].fetch();

 

> db.A.findOne({"_id":db.B.findOne().Apid[0].$id})  

结果:{ "_id" : ObjectId("4e3f33de6266b5845052c02c"), "value" : "2" }  

mongo-java方式操作:

 保存的时候,一对多就用 List<DBRef>    一对一就用new DBRef ()  ,都要知道对方文档所在的集合名字和文档的id

              List<DBRef> listRef = new ArrayList<DBRef>();
	    	DBRef refB = new DBRef(db,"transations", obj.get("_id"));
	    	listRef.add(refB);
	    	
	    	DBObject subObj = new BasicDBObject();
	    	subObj.put("brand", refB);
	    	subObj.put("productList", listRef);
	    	subObj.put("balance", "2000");
	    	subObj.put("pendingTransactions", new Array[0]);
	    	accounts.save(subObj);

保存效果:

查询:从结果的document对象里获取DBRed对象,就可以输出关联的集合明 文档id 等

MongoCursor<Document> cur = collection.find(
                Filters.and(Filters.eq("参数1", XXX),
                        Filters.eq("参数2", xxx))).iterator();
        while (cur.hasNext()) {
            Document object = (Document) cur.next();
            List<DBRef> ref_obj = (List<DBRef>)object.get("DETAILS");//获取到DBRef的list
            System.out.println(ref_obj.get(0));//只能输出DBRef的id和name
        }

 

 srping-data-monog 操作DBref

但是实际应用中也是会有类似的需求的。

我们就以学生和班级的关系来讲解一对一以及一对多的关联操作。

 

  • 一个班级有多个学生,班级对学生是一对多的关系
  • 一个学生属于一个班级,学生对班级是一对一的关系

 

@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来的。

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);

 

//自动带出班级信息
Student student = mongoTemplate.findOne(Query.query(Criteria.where("stuName").is("张学生")), Student.class);
System.out.println(student.getStuName() + "\t" + student.getClassObj().getClassName());

上面将的是一对一的操作,一对多的话就比较麻烦了

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);

 

 

Logo

更多推荐