第一階段的弱聯網遊戲已基本完成,截至今天下午,測試也基本差很少了,前端還有一些小bug須要優化,接下來會接入小米,360,百度,騰訊等平臺,而後推廣一波,年前公司還能賺一筆,而我,也即將準備作下一款SLG。上一次,我第一次嘗試了Netty,而且也着實感覺到了Nio的魅力,Netty的魅力,在作的過程當中也學到了不少有用的東西,這一次,在數據持久化方面,我思考了好久,我愈加的以爲,我即將作的這款遊戲的數據用nosql來存儲更合適,甚至是以前作的那款弱聯網遊戲的存儲,我也認爲彷佛應該使用nosql來作存儲,由於遊戲數據的擴展性太強了,也不須要傳統關係型數據庫那些複雜的約束,彷佛是高性能的mongo更適合這類數據的存儲,以前由於項目比較急,沒敢在數據庫這塊作嘗試,而此次,我準備嘗試使用Mongo作數據層的持久化,之前作Web開發的時候,項目中用過Mongo,可是當時對它瞭解並很少,最多瞭解到它是一種文檔型數據庫,和傳統數據庫有較大區別,但我並無太多的瞭解,而當我開始作遊戲以後,卻愈來愈以爲這種Nosql數據庫彷佛更加適合遊戲數據存儲的需求。最終我仍是準備使用mongo來作數據持久化,而後開始了個人mongo之旅,心中不禁說了一句——你好,mongo!javascript
衆多nosql數據庫中,我爲何要用mongo呢?實際上,我還會用到memcache和redis,memcached用來作緩存,memcache的緩存性能相信你們也清楚,用它來配合mongo來作緩存再適合不過了,把玩家完整的遊戲數據都放在緩存中,讀取所有數據直接讀取緩存(具體哪些數據緩存到時候看狀況而定),而使用redis,主要是看中它多樣的數據類型和數據本地化的功能,而且redis的性能也沒必要memcache差多少,而mongo,更是一種高性能的文檔型數據庫,不瞭解的同窗能夠去官網逛逛,mongo的官網地址:http://www.mongo.org Mongo主要特下以下:前端
僅憑以上4點,我認爲,像我作的這種數據類型複雜多變的遊戲數據用mongo來存儲再合適不過了。固然mongo還有更多的優勢如複製和故障恢復,支持徹底索引等。java
mongo自己是由C++編寫的,可它卻也支持不少語言,固然,最重要的是,它有提供Java api,如下是mongo java api官方文檔:http://api.mongodb.org/java/2.11.2/ 固然,既然有官方api,那就必定有人封裝了更方便更好用的框架出來,像morphia,就是一款相似於關係型數據庫ORM的Hibernate同樣,方便易用,另外,spring也提供了mongo的封裝,有興趣的均可以去了解一下,畢竟每一個人有本身的習慣,本身喜歡的纔是最好的。使用官方提供的java api,操做mongo也是很是的方便,舉例,好比如下保存對象的方法:redis
//第一:實例化mongo對象,鏈接mongodb服務器 包含全部的數據庫 //默認構造方法,默認是鏈接本機,端口號,默認是27017 //至關於Mongo mongo =new Mongo("localhost",27017) Mongo mongo =new Mongo(); //第二:鏈接具體的數據庫 //其中參數是具體數據庫的名稱,若服務器中不存在,會自動建立 DB db=mongo.getDB("myMongo"); //第三:操做具體的表 //在mongodb中沒有表的概念,而是指集合 //其中參數是數據庫中表,若不存在,會自動建立 DBCollection collection=db.getCollection("user"); //添加操做 //在mongodb中沒有行的概念,而是指文檔 BasicDBObject document=new BasicDBObject(); document.put("id", 1); document.put("name", "小明"); //而後保存到集合中 //collection.insert(document); //固然我也能夠保存這樣的json串 /* { "id":1, "name","小明", "address": { "city":"beijing", "code":"065000" } }*/ //實現上述json串思路以下: //第一種:相似xml時,不斷添加 BasicDBObject addressDocument=new BasicDBObject(); addressDocument.put("city", "beijing"); addressDocument.put("code", "065000"); document.put("address", addressDocument); //而後保存數據庫中 collection.insert(document); //第二種:直接把json存到數據庫中 /* String jsonTest="{'id':1,'name':'小明',"+ "'address':{'city':'beijing','code':'065000'}"+ "}"; DBObject dbobjct=(DBObject)JSON.parse(jsonTest); collection.insert(dbobjct);*/
其他方法我就不贅述了,想了解的能夠查官方文檔。spring
既然決定使用mongo,那就要着手開始寫工具類,按照官方文檔,我寫了MongoUtil和DBObjectUtil兩個工具類,MongoUtil負責數據庫的鏈接和操做,DBObjectUtil則是Mongo中的操做對象DBObject與Javabean之間的相互轉換。首先,我要在個人maven的pom.xml文件中依賴mongo的jar包sql
<dependency> <groupId>org.mongodb</groupId> <artifactId>mongo-java-driver</artifactId> <version>2.11.2</version> </dependency>
而後是個人MongoUtil類,其中簡單作了數據庫的鏈接管理,MC是Memcache緩存,這部分代碼就不貼出來了,上篇文章已貼出過Memcache部分代碼:mongodb
/** * @ClassName: MongoUtil * @Description: mongo * @author 何金成 * @date 2016年1月19日 下午3:35:25 * */ public class MongoUtil { private MongoClient mongo = null; private DB db = null; private static Logger logger = LoggerFactory.getLogger(MongoUtil.class); private static final Map<String, MongoUtil> instances = new ConcurrentHashMap<String, MongoUtil>(); private static final String CONF_PATH = "/spring-mongodb/mongodb.properties"; public static final String DB_ID = "id";// DB中id字段名 /** * 實例化 * * @return MongoDBManager對象 */ static { getInstance("db");// 初始化默認的MongoDB數據庫 } public static MongoUtil getInstance() { return getInstance("db");// 配置文件默認數據庫前綴爲db } public static MongoUtil getInstance(String dbName) { MongoUtil mongoMgr = instances.get(dbName); if (mongoMgr == null) { mongoMgr = buildInstance(dbName); if (mongoMgr == null) { return null; } instances.put(dbName, mongoMgr); } return mongoMgr; } private static synchronized MongoUtil buildInstance(String dbName) { MongoUtil mongoMgr = new MongoUtil(); try { mongoMgr.mongo = new MongoClient(getServerAddress(dbName), getMongoCredential(dbName), getDBOptions(dbName)); mongoMgr.db = mongoMgr.mongo.getDB(getProperty(CONF_PATH, dbName + ".database")); logger.info("connect to MongoDB success!"); boolean flag = mongoMgr.db.authenticate( getProperty(CONF_PATH, dbName + ".username"), getProperty(CONF_PATH, dbName + ".password").toCharArray()); if (!flag) { logger.error("MongoDB auth failed"); return null; } } catch (Exception e) { logger.info("Can't connect " + dbName + " MongoDB! {}", e); return null; } return mongoMgr; } /** * 根據properties文件的key獲取value * * @param filePath * properties文件路徑 * @param key * 屬性key * @return 屬性value */ private static String getProperty(String filePath, String key) { Properties props = new Properties(); try { InputStream in = MongoUtil.class.getResourceAsStream(filePath); props.load(in); String value = props.getProperty(key); return value; } catch (Exception e) { logger.info("load mongo properties exception {}", e); System.exit(0); return null; } } /** * 獲取集合(表) * * @param collection */ public DBCollection getCollection(String collection) { DBCollection collect = db.getCollection(collection); return collect; } /** * 插入 * * @param collection * @param o */ public void insert(String collection, DBObject o) { getCollection(collection).insert(o); // 添加到MC控制 MC.add(o, o.get(DB_ID)); } /** * 批量插入 * * @param collection * @param list */ public void insertBatch(String collection, List<DBObject> list) { if (list == null || list.isEmpty()) { return; } getCollection(collection).insert(list); // 批量插入MC for (DBObject o : list) { MC.add(o, o.get(DB_ID)); } } /** * 刪除 * * @param collection * @param q * 查詢條件 */ public List<DBObject> delete(String collection, DBObject q) { getCollection(collection).remove(q); List<DBObject> list = find(collection, q); // MC中刪除 for (DBObject tmp : list) { DBObject dbObject = MC.<DBObject> get(DBObject.class, (Long) tmp.get(DB_ID)); if (null != dbObject) { MC.delete(DBObject.class, (Long) dbObject.get(DB_ID)); } } return list; } /** * 批量刪除 * * @param collection * @param list * 刪除條件列表 */ public void deleteBatch(String collection, List<DBObject> list) { if (list == null || list.isEmpty()) { return; } for (int i = 0; i < list.size(); i++) { // 批量條件刪除 delete(collection, list.get(i)); } } /** * 計算集合總條數 * * @param collection */ public int getCount(String collection) { int count = (int) getCollection(collection).find().count(); return count; } /** * 計算知足條件條數 * * @param collection * @param q * 查詢條件 */ public long getCount(String collection, DBObject q) { return getCollection(collection).getCount(q); } /** * 更新 * * @param collection * @param q * 查詢條件 * @param setFields * 更新對象 * @return List<DBObject> 更新後的對象列表 */ public List<DBObject> update(String collection, DBObject q, DBObject setFields) { getCollection(collection).updateMulti(q, new BasicDBObject("$set", setFields)); List<DBObject> list = find(collection, q); // 遍歷 for (DBObject dbObject : list) { // MC 中修改 DBObject tmp = MC.<DBObject> get(DBObject.class, (Long) dbObject.get(DB_ID)); if (null != tmp) { MC.update(dbObject, (Long) tmp.get(DB_ID)); } } return list; } /** * 查找集合全部對象 * * @param collection */ public List<DBObject> findAll(String collection) { List<DBObject> list = getCollection(collection).find().toArray(); return list; } /** * 按順序查找集合全部對象 * * @param collection * 數據集 * @param orderBy * 排序 */ public List<DBObject> findAll(String collection, DBObject orderBy) { return getCollection(collection).find().sort(orderBy).toArray(); } /** * 查找(返回一個對象) * * @param collection * @param q * 查詢條件 */ public DBObject findOne(String collection, DBObject q) { return findOne(collection, q, null); } /** * 查找(返回一個對象) * * @param collection * @param q * 查詢條件 * @param fileds * 返回字段 */ public DBObject findOne(String collection, DBObject q, DBObject fields) { if (q.containsField(DB_ID)) {// 若是根據id來查詢,先從緩存取數據 DBObject tmp = MC.<DBObject> get(DBObject.class, (Long) q.get(DB_ID)); if (tmp != null) {// 緩存沒有數據,從數據庫取 if (fields != null) {// 留下須要返回的字段 for (String key : tmp.keySet()) { if (!fields.containsField(key)) { tmp.removeField(key); } } } return tmp; } } return fields == null ? getCollection(collection).findOne(q) : getCollection(collection).findOne(q, fields); } /** * 查找返回特定字段(返回一個List<DBObject>) * * @param collection * @param q * 查詢條件 * @param fileds * 返回字段 */ public List<DBObject> findLess(String collection, DBObject q, DBObject fileds) { DBCursor c = getCollection(collection).find(q, fileds); if (c != null) return c.toArray(); else return null; } /** * 查找返回特定字段(返回一個List<DBObject>) * * @param collection * @param q * 查詢條件 * @param fileds * 返回字段 * @param orderBy * 排序 */ public List<DBObject> findLess(String collection, DBObject q, DBObject fileds, DBObject orderBy) { DBCursor c = getCollection(collection).find(q, fileds).sort(orderBy); if (c != null) return c.toArray(); else return null; } /** * 分頁查找集合對象,返回特定字段 * * @param collection * @param q * 查詢條件 * @param fileds * 返回字段 * @pageNo 第n頁 * @perPageCount 每頁記錄數 */ public List<DBObject> findLess(String collection, DBObject q, DBObject fileds, int pageNo, int perPageCount) { return getCollection(collection).find(q, fileds) .skip((pageNo - 1) * perPageCount).limit(perPageCount) .toArray(); } /** * 按順序分頁查找集合對象,返回特定字段 * * @param collection * 集合 * @param q * 查詢條件 * @param fileds * 返回字段 * @param orderBy * 排序 * @param pageNo * 第n頁 * @param perPageCount * 每頁記錄數 */ public List<DBObject> findLess(String collection, DBObject q, DBObject fileds, DBObject orderBy, int pageNo, int perPageCount) { return getCollection(collection).find(q, fileds).sort(orderBy) .skip((pageNo - 1) * perPageCount).limit(perPageCount) .toArray(); } /** * 查找(返回一個List<DBObject>) * * @param collection * @param q * 查詢條件 */ public List<DBObject> find(String collection, DBObject q) { DBCursor c = getCollection(collection).find(q); if (c != null) return c.toArray(); else return null; } /** * 按順序查找(返回一個List<DBObject>) * * @param collection * @param q * 查詢條件 * @param orderBy * 排序 */ public List<DBObject> find(String collection, DBObject q, DBObject orderBy) { DBCursor c = getCollection(collection).find(q).sort(orderBy); if (c != null) return c.toArray(); else return null; } /** * 分頁查找集合對象 * * @param collection * @param q * 查詢條件 * @pageNo 第n頁 * @perPageCount 每頁記錄數 */ public List<DBObject> find(String collection, DBObject q, int pageNo, int perPageCount) { return getCollection(collection).find(q) .skip((pageNo - 1) * perPageCount).limit(perPageCount) .toArray(); } /** * 按順序分頁查找集合對象 * * @param collection * 集合 * @param q * 查詢條件 * @param orderBy * 排序 * @param pageNo * 第n頁 * @param perPageCount * 每頁記錄數 */ public List<DBObject> find(String collection, DBObject q, DBObject orderBy, int pageNo, int perPageCount) { return getCollection(collection).find(q).sort(orderBy) .skip((pageNo - 1) * perPageCount).limit(perPageCount) .toArray(); } /** * distinct操做 * * @param collection * 集合 * @param field * distinct字段名稱 */ public Object[] distinct(String collection, String field) { return getCollection(collection).distinct(field).toArray(); } /** * distinct操做 * * @param collection * 集合 * @param field * distinct字段名稱 * @param q * 查詢條件 */ public Object[] distinct(String collection, String field, DBObject q) { return getCollection(collection).distinct(field, q).toArray(); } /** * group分組查詢操做,返回結果少於10,000keys時可使用 * * @param collection * 集合 * @param key * 分組查詢字段 * @param q * 查詢條件 * @param reduce * reduce Javascript函數,如:function(obj, * out){out.count++;out.csum=obj.c;} * @param finalize * reduce * function返回結果處理Javascript函數,如:function(out){out.avg=out.csum * /out.count;} */ public BasicDBList group(String collection, DBObject key, DBObject q, DBObject initial, String reduce, String finalize) { return ((BasicDBList) getCollection(collection).group(key, q, initial, reduce, finalize)); } /** * group分組查詢操做,返回結果大於10,000keys時可使用 * * @param collection * 集合 * @param map * 映射javascript函數字符串,如:function(){ for(var key in this) { * emit(key,{count:1}) } } * @param reduce * reduce Javascript函數字符串,如:function(key,emits){ total=0; for(var * i in emits){ total+=emits[i].count; } return {count:total}; } * @param q * 分組查詢條件 * @param orderBy * 分組查詢排序 */ public Iterable<DBObject> mapReduce(String collection, String map, String reduce, DBObject q, DBObject orderBy) { // DBCollection coll = db.getCollection(collection); // MapReduceCommand cmd = new MapReduceCommand(coll, map, reduce, null, // MapReduceCommand.OutputType.INLINE, q); // return coll.mapReduce(cmd).results(); MapReduceOutput out = getCollection(collection).mapReduce(map, reduce, null, q); return out.getOutputCollection().find().sort(orderBy).toArray(); } /** * group分組分頁查詢操做,返回結果大於10,000keys時可使用 * * @param collection * 集合 * @param map * 映射javascript函數字符串,如:function(){ for(var key in this) { * emit(key,{count:1}) } } * @param reduce * reduce Javascript函數字符串,如:function(key,emits){ total=0; for(var * i in emits){ total+=emits[i].count; } return {count:total}; } * @param q * 分組查詢條件 * @param orderBy * 分組查詢排序 * @param pageNo * 第n頁 * @param perPageCount * 每頁記錄數 */ public List<DBObject> mapReduce(String collection, String map, String reduce, DBObject q, DBObject orderBy, int pageNo, int perPageCount) { MapReduceOutput out = getCollection(collection).mapReduce(map, reduce, null, q); return out.getOutputCollection().find().sort(orderBy) .skip((pageNo - 1) * perPageCount).limit(perPageCount) .toArray(); } /** * group分組查詢操做,返回結果大於10,000keys時可使用 * * @param collection * 集合 * @param map * 映射javascript函數字符串,如:function(){ for(var key in this) { * emit(key,{count:1}) } } * @param reduce * reduce Javascript函數字符串,如:function(key,emits){ total=0; for(var * i in emits){ total+=emits[i].count; } return {count:total}; } * @param outputCollectionName * 輸出結果表名稱 * @param q * 分組查詢條件 * @param orderBy * 分組查詢排序 */ public List<DBObject> mapReduce(String collection, String map, String reduce, String outputCollectionName, DBObject q, DBObject orderBy) { if (!db.collectionExists(outputCollectionName)) { getCollection(collection).mapReduce(map, reduce, outputCollectionName, q); } return getCollection(outputCollectionName) .find(null, new BasicDBObject("_id", false)).sort(orderBy) .toArray(); } /** * group分組分頁查詢操做,返回結果大於10,000keys時可使用 * * @param collection * 集合 * @param map * 映射javascript函數字符串,如:function(){ for(var key in this) { * emit(key,{count:1}) } } * @param reduce * reduce Javascript函數字符串,如:function(key,emits){ total=0; for(var * i in emits){ total+=emits[i].count; } return {count:total}; } * @param outputCollectionName * 輸出結果表名稱 * @param q * 分組查詢條件 * @param orderBy * 分組查詢排序 * @param pageNo * 第n頁 * @param perPageCount * 每頁記錄數 */ public List<DBObject> mapReduce(String collection, String map, String reduce, String outputCollectionName, DBObject q, DBObject orderBy, int pageNo, int perPageCount) { if (!db.collectionExists(outputCollectionName)) { getCollection(collection).mapReduce(map, reduce, outputCollectionName, q); } return getCollection(outputCollectionName) .find(null, new BasicDBObject("_id", false)).sort(orderBy) .skip((pageNo - 1) * perPageCount).limit(perPageCount) .toArray(); } /** * @Title: getServerAddress * @Description: 獲取數據庫服務器列表 * @param dbName * @return * @throws UnknownHostException * @return List<ServerAddress> * @throws */ private static List<ServerAddress> getServerAddress(String dbName) throws UnknownHostException { List<ServerAddress> list = new ArrayList<ServerAddress>(); String hosts = getProperty(CONF_PATH, dbName + ".host"); for (String host : hosts.split("&")) { String ip = host.split(":")[0]; String port = host.split(":")[1]; list.add(new ServerAddress(ip, Integer.parseInt(port))); } return list; } /** * @Title: getMongoCredential * @Description: 獲取數據庫安全驗證信息 * @param dbName * @return * @return List<MongoCredential> * @throws */ private static List<MongoCredential> getMongoCredential(String dbName) { String username = getProperty(CONF_PATH, dbName + ".username"); String password = getProperty(CONF_PATH, dbName + ".password"); String database = getProperty(CONF_PATH, dbName + ".database"); MongoCredential credentials = MongoCredential.createMongoCRCredential( username, database, password.toCharArray()); List<MongoCredential> credentialsList = new ArrayList<MongoCredential>(); credentialsList.add(credentials); return credentialsList; } /** * @Title: getDBOptions * @Description: 獲取數據參數設置 * @return * @return MongoClientOptions * @throws */ private static MongoClientOptions getDBOptions(String dbName) { MongoClientOptions.Builder build = new MongoClientOptions.Builder(); build.connectionsPerHost(Integer.parseInt(getProperty(CONF_PATH, dbName + ".connectionsPerHost"))); // 與目標數據庫可以創建的最大connection數量爲50 build.threadsAllowedToBlockForConnectionMultiplier(Integer .parseInt(getProperty(CONF_PATH, dbName + ".threadsAllowedToBlockForConnectionMultiplier"))); // 若是當前全部的connection都在使用中,則每一個connection上能夠有50個線程排隊等待 build.maxWaitTime(Integer.parseInt(getProperty(CONF_PATH, dbName + ".maxWaitTime"))); build.connectTimeout(Integer.parseInt(getProperty(CONF_PATH, dbName + ".connectTimeout"))); MongoClientOptions myOptions = build.build(); return myOptions; } public static void main(String[] args) { try { // getInstance().insert( // "user", // new BasicDBObject().append("name", "admin3") // .append("type", "2").append("score", 70) // .append("level", 2) // .append("inputTime", new Date().getTime())); // getInstance().update("user", // new BasicDBObject().append("status", 1), // new BasicDBObject().append("status", 2)); // === group start ============= // StringBuilder sb = new StringBuilder(100); // sb.append("function(obj, out){out.count++;out.").append("scoreSum") // .append("+=obj.").append("score").append(";out.") // .append("levelSum").append("+=obj.").append("level") // .append('}'); // String reduce = sb.toString(); // BasicDBList list = getInstance().group( // "user", // new BasicDBObject("type", true), // new BasicDBObject(), // new BasicDBObject().append("count", 0) // .append("scoreSum", 0).append("levelSum", 0) // .append("levelAvg", (Double) 0.0), reduce, // "function(out){ out.levelAvg = out.levelSum / out.count }"); // for (Object o : list) { // DBObject obj = (DBObject) o; // System.out.println(obj); // } // ======= group end========= // === mapreduce start ============= // Iterable<DBObject> list2 = getInstance() // .mapReduce( // "user", // "function(){emit( {type:this.type}, {score:this.score, level:this.level} );}", // "function(key,values){var result={score:0,level:0};var count = 0;values.forEach(function(value){result.score += value.score;result.level += value.level;count++});result.level = result.level / count;return result;}", // new BasicDBObject(), new BasicDBObject("score", 1)); // for (DBObject o : list2) { // System.out.println(o); // } // List<DBObject> list3 = getInstance().mapReduce("user", // "function(){emit({type:this.type},{type:this.type,score:this.score,level:this.level});}", // "function(key,values){var result={type:key.type,score:0,level:0};var count=0;values.forEach(function(value){result.score+=value.score;result.level+=value.level;count++});result.level=result.level/count;return result;}", // "group_temp_user", // new BasicDBObject(), // new BasicDBObject("score",1)); // for (DBObject o : list3) { // System.out.println(o); // } // ======= mapreduce end========= // System.out.print(getInstance().findAll("user")); // System.out.print(getInstance().find( // "user", // new BasicDBObject("inputTime", new BasicDBObject("$gt", // 1348020002890L)), // new BasicDBObject().append("_id", "-1"), 1, 2)); // getInstance().delete("user", new BasicDBObject()); } catch (Exception e) { System.out.println(e.getMessage()); } } }
如下是mongo的鏈接配置properties文件shell
#ip和端口,多個主機用&相連 db.host=127.0.0.1:27017 #數據庫名字 db.database=war #用戶名 db.username=root #密碼 db.password=123456 #每一個主機的最大鏈接數 db.connectionsPerHost=50 #線程容許最大等待鏈接數 db.threadsAllowedToBlockForConnectionMultiplier=50 #鏈接超時時間1分鐘 db.connectTimeout=60000 #一個線程訪問數據庫的時候,在成功獲取到一個可用數據庫鏈接以前的最長等待時間爲2分鐘 #這裏比較危險,若是超過maxWaitTime都沒有獲取到這個鏈接的話,該線程就會拋出Exception #故這裏設置的maxWaitTime應該足夠大,以避免因爲排隊線程過多形成的數據庫訪問失敗 db.maxWaitTime=120000
DBObject和Javabean之間的轉換就容易多了,能夠經過json爲中介來轉換。數據庫
public class DBObjectUtil { /** * 把實體bean對象轉換成DBObject * * @param bean * @return * @throws IllegalArgumentException * @throws IllegalAccessException */ public static <T> DBObject bean2DBObject(T bean) { if (bean == null) { return null; } DBObject dbObject = new BasicDBObject(); String json = JsonUtils.objectToJson(bean); dbObject = (DBObject) JSON.parse(json); return dbObject; } /** * 把DBObject轉換成bean對象 * * @param dbObject * @param bean * @return * @throws IllegalAccessException * @throws InvocationTargetException * @throws NoSuchMethodException */ @SuppressWarnings("unchecked") public static <T> T dbObject2Bean(DBObject dbObject, T bean) { if (bean == null) { return null; } String json = JSON.serialize(dbObject); bean = (T) JsonUtils.jsonToBean(json, bean.getClass()); return bean; } }
至此,mongo搭建基本完成,更多關於mongo的探索,仍是要在實踐中完成,實踐是檢驗真理的惟一標準,nosql現在煊赫一時,但咱們也要保持理性的態度看待問題,傳統數據庫和nosql究竟誰更勝一籌,不妨咱們都動手試一試,是騾子是馬,都拉出來溜溜!以上代碼可直接用過工具類,歡迎交流探討!json