剛接觸mongodb不久。踩到許多坑,記錄下一些基於spring-data-mongodb的東西吧html
首先。應該瞭解下什麼狀況下使用mongodb,什麼狀況下用mysql:java
連接:csdn上關於何時使用mongodb的博客mysql
下面開始接入spring-data-mongodb
使用maven和spring-boot,pom文件裏引入依賴便可spring
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency>
在java代碼裏,向集合的實體類上面加上@Document,表示這個類被spring-data-mongodb標識了,能夠映射成mongodb裏的一個集合,默認名字就是類名,也能夠取別名@Document(collection="alias"),示例以下:sql
@Document public class Example{ private String id; private String picture; }
注意,String類型的id屬性不可缺乏,由於mongodb的每一個文檔都要有一個id(也能夠是其餘類型的Id,詳情能夠去官方文檔看看),setget方法我就不貼了,用lombok更方便。
這裏就能夠直接new 一個Example保存了,因此如今建個Repositry用於保存數據
很是簡單隻要繼承一個接口就能夠 public interface ExampleRepository extends CrudRepository<Example, String> {}
這裏第一個泛型是上面的文檔對象實體,第二個泛型是文檔的id類型
而後就能夠直接用exampleRepositry.save(example)保存文檔了。簡單的介紹就到這裏mongodb
定義一個Repository類,並加上@Repository註解,而後添加本身的方法就能夠了,下面上代碼數組
@Repository public class ExampleAdvancedRepository { @Autowired private MongoOperations mongoOperations; public boolean updateAlias(String id, String alias) { return mongoOperations.updateFirst( query(where("id").is(id)), new Update().set("alias", alias), Example.class).isUpdateOfExisting(); } }
簡單的一個實例,根據Id修改別名,這裏一點要說的就是這個id屬性咱們傳入進去是String類型,可是monogdbTemplate會將這個id轉成ObjectId,可是只侷限於這個文檔屬性名叫id的,若是是其餘屬性好比userId,雖然也是ObjectId,可是用where("userId").is(userId)
這樣查詢是失敗的,由於這裏的userId並無轉成ObjectId,正確的作法是where("userId").is(new ObjectId(userId))
maven
數組操做:
增:只要push進數組裏就行spring-boot
public boolean createReply(Reply reply) { return mongoOperations.updateFirst( query(where("id").is(reply.getCommentId())), new Update() .inc("total_replies", 1) .push("replies", reply), Comment.class).isUpdateOfExisting(); }
刪:刪除操做構建一個帶id的DB對象,而後丟進pull方法裏就好了,代碼以下.net
public boolean deleteReply(String commentId, String replyId) { BasicDBObject object = new BasicDBObject(); object.put("id", replyId); return mongoOperations.updateFirst( query(where("id").is(commentId)), new Update() .inc("total_replies", -1) .pull("replies", object), Comment.class).isUpdateOfExisting(); }
這個示例刪除了數組裏某個對象,因此要使用傳入一個刪除對象的id,若是數組裏存的就是數字,或者字符串就能夠直接pull("users","張三")
改:
public boolean createReplyVote(String commentId, String replyId, String userId) { return mongoOperations.updateFirst( query(where("id").is(commentId).andOperator( where("replies.id").is(new ObjectId(replyId)))), new Update() .addToSet("replies.$.voter_ids", userId) .inc("replies.$.total_voters", 1), Comment.class).isUpdateOfExisting(); }
用佔位符$表示數組裏的某個匹配的元素
set("replies.$.voters.$.name","李四")
可是這種寫法是錯誤的。這種狀況在mongodb3.6以前是沒辦法解決的,只能將數組嵌套數組的結構改變,變成Map的形式,不能出現兩個佔位符的狀況,或者修改設計,將裏邊的數組拿出來做爲獨立的文檔創建索引可使用mongodb的腳本去創建,也能夠用spring-data-mongo提供的註解創建索引
下面弄個簡單的索引,代碼以下:
@Document @CompoundIndexes({ @CompoundIndex(def = "{'userId': 1, 'createdDate': -1}") }) public class Example { private String id; private String userId; private Date createdDate; }
這裏創建 了一個簡單的聚合索引,根據userId和createdDate創建的索引。
查看索引是否起效果,可使用explain方法查看執行計劃,示例以下
collection結構
{ "_id" : ObjectId("5b0cc0f2fde29f2ea0641b16"), "userId" : "1", "createdDate" : ISODate("1995-09-30T16:00:00.000+0000"), "gender" : "MALE" }
執行語句db.Example.find({"userId":"1"}).explain();
能夠查看到結果
{ "queryPlanner": { "plannerVersion": 1.0, ... "winningPlan": { "stage": "FETCH", "inputStage": { "stage": "IXSCAN", "keyPattern": { "user_id": 1.0, "created_date": -1.0 }, "indexName": "user_id_1_created_date_-1", "isMultiKey": false, "isUnique": false, "isSparse": false, "isPartial": false, "indexVersion": 1.0, "direction": "forward", "indexBounds": { "user_id": ["[\"22\", \"22\"]"], "created_date": ["[MaxKey, MinKey]"] } } }, "rejectedPlans": [] } ... }
能夠看到關鍵的信息,winningPlan 表示走了索引,而後是具體的信息,rejectedPlans表示沒有走的索引
在使用mongdb的時候常常會遇到一個集合嵌套了另外一個集合的狀況,若是不用引用的話,就得本身手動寫代碼冗餘數據,修改的時候就比較麻煩,要同時修改多處。用mongodb提供的$ref 能很好的解決這個問題
首先寫個例子
@Document public class Clazz{ private String id; private String name; @DBRef private People leader; } @Document public class People{ private String id; private String name; private Integer gender; private Integer age; }
每一個班級都須要個班主任leader,這個時候用外鍵很是合適使用很簡單,只要在要用到外鍵的字段上,加上@DBRef就能夠了。先建立個People集合,保存寫people信息,而後再建立Clazz集合,能夠發現Clazz存儲的時候leader變成了一個引用類型。
{ "_id" : ObjectId("5b10a741628e881fc8848d21"), "name" : "王大錘", "leader" : DBRef("People", ObjectId("5b0cf065fde29f25486bf532")), }
那麼查詢的時候,若是要根據leader查詢班級怎麼辦,@DBRef的查詢也很是簡單。
public Clazz findByLeaderId(String leaderId) { return mongoOperations.findOne( query(where("leader.$id").is(new ObjectId(leaderId))), GhostFriend.class); }
這裏注意2個地方,第一個就是引用的時候要加上$id
,這樣才能表示此id是引用,第二個地方就是new ObjectId()
在自定義複雜查詢哪裏有提到爲何要new ObjectId()
1.隊列(先進先出)
好比我要保留最新的3個瀏覽記錄,那麼就應該創建一個數組,先進的先刪除,永遠保留3個元素
public boolean updateHistory(String id,History history) { return mongoOperations.updateFirst( query(where("id").is(id)), new Update() .push("histories").slice(-3).each(history) People.class).isUpdateOfExisting(); }
這裏利用了slice接收int類型的值,表示截取多少個元素,正值則是從前到後保留,負值表示從後到前保留,each則表示加入數組的元素
這裏只是作一個簡單的介紹,網上中文資料很少。詳情能夠查看mongodb官方文檔slice用法