這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
由於revel架構本身對於model層的編寫沒有提供任何指導,所以在設計這部分的時候就有些猶豫,反覆斟酌到底怎樣才算是最佳實務。
我在做山坡網的時候剛開始也糾結了一下,拿不準mongodb的session的建立和銷毀應該在什麼地方處理。直到有一天看到了revmgo的作者在與revel的作者討論(具體內容在這裡),就去研究了下revmgo,之後立即就用它替換了我自己的實現。
先說下用法吧。
1. 在app.conf添加mongodb的連接字串。
revmgo.dial = mongodb://username:password@serverip/database
2. 在controller層的init方法中初始化。
func init() {
revmgo.ControllerInit()
revel.OnAppStart(func() {
revmgo.AppInit()
}
}
3. 把MongoController簽入到所有需要訪問資料庫的Controller中。
type Application struct {
*revel.Controller
revmgo.MongoController
}
這裡有個小插曲,revmgo.MongoController已經內嵌過revel.Controller了,但此處還是不能省略*revel.Controller。原因是revel在確定有效controller的action的時候只反射了一層,跟revel的作者討論過這個問題,後來他說服我了,原因是如果不斷遞迴反射的話複雜度會變高而且效能損失會不可控。好吧,其實這也算是OO的遺毒。看起來挺難受,多寫了一行代碼,設計上也不太舒服,但仔細想來對效能和整體的複雜度卻都有好處,既然如此,何必執著設計上的完美呢?簡單就是美。
4. 修改Dal的實現。
//Data access layer
type Dal struct {
session *mgo.Session
}
//建立新的Dal
func NewDal(session *mgo.Session) *Dal {
if session == nil {
panic("session cannot be nil.")
}
return &Dal{session}
}
現在來看看在controller中如何使用。
func (c *Account) HandleRegister(user *models.MockUser) revel.Result {
//Validate parameter here.
dal := models.NewDal(c.MongoSession)
err := dal.RegisterUser(user)
//Biz logic here
}
revmgo的內部實現非常簡單,整個代碼也就110行,基本可以一目瞭然。
總的來說,調用revmgo.AppInit()的時候讀取app.conf中的配置,可以看到有兩個配置項有效,"revmgo.dial"為連接字串,"db.spec"確定mongodb的session的重複利用方式(具體含義參考這裡),然後建立session。
之後的revmgo.ControllerInit註冊了兩個interceptfunc,用於在controller的action訪問前後建立和關閉session。
func ControllerInit() {
revel.InterceptMethod((*MongoController).Begin, revel.BEFORE)
revel.InterceptMethod((*MongoController).End, revel.FINALLY)
}
其實我對於這個設計頗有微詞,ControllerInit的存在簡直就是對revel的module設計機制的嘲笑,很難相信作為一個模組竟然還需要使用者手動調用初始化函數。這個部分應該不難處理,常規來說只應該讓使用者註冊需要使用的module和執行順序,初始化和解構都由module管理器負責。跟revel的作者也討論了這個問題,看他似乎有別的設計意圖,等等看revel 1.0正式發布時會是什麼樣子吧。