1. 程式人生 > >mongodb多表查詢、外來鍵關聯,命令+java方式實現

mongodb多表查詢、外來鍵關聯,命令+java方式實現

首先,我們回憶一下,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);