DB 表示數(shù)據(jù)庫連接,是一個(gè)抽象類,部分核心功能由子類提供,由 DBApiLayer 繼承。
由子類實(shí)現(xiàn)的抽象方法
// 開始數(shù)據(jù)庫連接
public abstract void requestStart();
// 結(jié)束數(shù)據(jù)庫連接
public abstract void requestDone();
// 保持?jǐn)?shù)據(jù)庫連接
public abstract void requestEnsureConnection();
// 獲取指定名稱的數(shù)據(jù)集
protected abstract DBCollection doGetCollection( String name );
數(shù)據(jù)集相關(guān)的方法
// 創(chuàng)建數(shù)據(jù)集
public final DBCollection createCollection( String name, DBObject options ){
// 如果 options 不為空
// 則先以 options 構(gòu)造創(chuàng)建數(shù)據(jù)庫的命令
// 然后執(zhí)行創(chuàng)建數(shù)據(jù)庫的命令并得到結(jié)果
if ( options != null ){
DBObject createCmd = new BasicDBObject("create", name);
createCmd.putAll(options);
CommandResult result = command(createCmd);
result.throwOnError();
}
return getCollection(name);
}
// 解析用 "." 分隔的字符串,獲取指定的數(shù)據(jù)集
public DBCollection getCollectionFromString( String s ){
DBCollection foo = null;
// 獲取 "." 所在位置
int idx = s.indexOf( "." );
// 當(dāng)分解后的字符串中仍然包含 "." 時(shí)
// 循環(huán)分解字符串,知道所有 "." 號(hào)解析完畢
// 效果類似于遞歸調(diào)用,但這里采用了循環(huán)的寫法
while ( idx >= 0 ){
// 獲取 "." 之前的字符串 b
String b = s.substring( 0 , idx );
// 獲取 "." 之后的字符串 s
s = s.substring( idx + 1 );
// 檢查上次解析得到的對(duì)象 foo 是否為空
if ( foo == null )
foo = getCollection( b );
else
foo = foo.getCollection( b );
// 獲取 "." 所在位置
idx = s.indexOf( "." );
}
if ( foo != null )
return foo.getCollection( s );
return getCollection( s );
}
// 獲取所有數(shù)據(jù)集名稱
public Set<String> getCollectionNames()
throws MongoException {
// 獲取系統(tǒng)的 namespace 數(shù)據(jù)集
DBCollection namespaces = getCollection("system.namespaces");
if (namespaces == null)
throw new RuntimeException("this is impossible");
// 獲取用于遍歷 namespace 的迭代器
Iterator<DBObject> i = namespaces.__find(new BasicDBObject(), null, 0, 0, 0, getOptions());
if (i == null)
return new HashSet<String>();
// 表名稱 List,最后轉(zhuǎn)換為 set 并返回
List<String> tables = new ArrayList<String>();
for (; i.hasNext();) {
DBObject o = i.next();
if ( o.get( "name" ) == null ){
throw new MongoException( "how is name null : " + o );
}
// 獲取 namespace 名稱
String n = o.get("name").toString();
// 獲取 namespace 名稱前綴
int idx = n.indexOf(".");
String root = n.substring(0, idx);
// 如果前綴不為當(dāng)前 DB 的名稱
// 表示這個(gè) namespace 不屬于當(dāng)前 DB
if (!root.equals(_name))
continue;
// 忽略特殊數(shù)據(jù)集
if (n.indexOf("$") >= 0)
continue;
// 獲取數(shù)據(jù)集名稱
String table = n.substring(idx + 1);
tables.add(table);
}
Collections.sort(tables);
// 轉(zhuǎn)換為 Set
return new LinkedHashSet<String>(tables);
}
數(shù)據(jù)庫命令相關(guān)的方法
// 執(zhí)行數(shù)據(jù)庫命令
public CommandResult command( DBObject cmd , int options )
throws MongoException {
// 調(diào)用 DBCollection 的 find 方法
Iterator<DBObject> i = getCollection("$cmd").__find(cmd, new BasicDBObject(), 0, -1, 0, options);
if ( i == null || ! i.hasNext() )
return null;
// 獲得數(shù)據(jù)庫命令的返回結(jié)果結(jié)果
CommandResult res = (CommandResult)i.next();
res._cmd = cmd;
return res;
}
// 以 "eval" 方式解析命令
public CommandResult doEval( String code , Object ... args )
throws MongoException {
// 構(gòu)造 "eval" 命令
// 調(diào)用 command 方法執(zhí)行并獲得結(jié)果
return command( BasicDBObjectBuilder.start()
.add( "$eval" , code )
.add( "args" , args )
.get() );
}
// 刪除數(shù)據(jù)庫
public void dropDatabase()
throws MongoException {
CommandResult res = command(new BasicDBObject("dropDatabase", 1));
res.throwOnError();
_mongo._dbs.remove(this.getName());
}
用戶相關(guān)的方法
// 驗(yàn)證用戶名和密碼
public CommandResult authenticateCommand(String username, char[] passwd )
throws MongoException {
if ( username == null || passwd == null )
throw new NullPointerException( "username can't be null" );
if ( _username != null )
throw new IllegalStateException( "can't call authenticate twice on the same DBObject" );
// 根據(jù)用戶名和密碼,通過 MD5 計(jì)算哈希值
String hash = _hash( username , passwd );
// 驗(yàn)證用戶名和密碼并獲得返回結(jié)果
CommandResult res = _doauth( username , hash.getBytes() );
res.throwOnError();
_username = username;
_authhash = hash.getBytes();
return res;
}
// 驗(yàn)證用戶名和密碼并獲得返回結(jié)果
private CommandResult _doauth( String username , byte[] hash ){
// 獲取 "鹽值",用于加密
CommandResult res = command(new BasicDBObject("getnonce", 1), getOptions());
res.throwOnError();
// 利用當(dāng)前的用戶名和密碼,執(zhí)行一個(gè)沒有實(shí)際意義的操作
// 利用 username, 加密后的密碼,以及鹽值進(jìn)行驗(yàn)證,看看是否產(chǎn)生錯(cuò)誤
DBObject cmd = _authCommand( res.getString( "nonce" ) , username , hash );
return command(cmd, getOptions());
}
// 利用 username, 加密后的密碼,以及鹽值進(jìn)行驗(yàn)證,看看是否產(chǎn)生錯(cuò)誤
static DBObject _authCommand( String nonce , String username , byte[] hash ){
// 獲取由 username, 加密后的密碼,以及鹽值 組成的 key
String key = nonce + username + new String( hash );
// 構(gòu)造用于驗(yàn)證用戶的命令
BasicDBObject cmd = new BasicDBObject();
cmd.put("authenticate", 1);
cmd.put("user", username);
cmd.put("nonce", nonce);
// 對(duì) key 進(jìn)行 MD5 加密
cmd.put("key", Util.hexMD5(key.getBytes()));
return cmd;
}
// 添加用戶
public WriteResult addUser( String username , char[] passwd, boolean readOnly ){
// 獲取系統(tǒng)用戶
DBCollection c = getCollection( "system.users" );
// 根據(jù)指定的 username 獲取用戶
DBObject o = c.findOne( new BasicDBObject( "user" , username ) );
// 如果不存在,則創(chuàng)建
if ( o == null )
o = new BasicDBObject( "user" , username );
// 設(shè)置密碼
o.put( "pwd" , _hash( username , passwd ) );
// 設(shè)置只讀權(quán)限
o.put( "readOnly" , readOnly );
// 將構(gòu)造出來的用戶對(duì)象添加到 系統(tǒng)用戶數(shù)據(jù)集 中
return c.save( o );
}
// 刪除用戶
public WriteResult removeUser( String username ){
DBCollection c = getCollection( "system.users" );
return c.remove(new BasicDBObject( "user" , username ));
}