在用golang獲取數據庫的數據的時候,不免會遇到可控field。這個時候拿到的數據若是直接用string
, time.Time
這樣的類型來解析的話會遇到panic。mysql
那麼如何處理這個問題呢,第一個出如今眼前的辦法就是用database/sql
。這個包裏包含了不少的能夠處理可控字段的類型,好比:sql.NullString
, sql.NullBool
等。因此,model能夠用這些類型來定義,如:git
package main import ( "database/sql" "fmt" "github.com/go-sql-driver/mysql" ) type Article struct { Id int `json:"id"` Title string `json:"title"` PubDate mysql.NullTime `json:"pub_date"` Body sql.NullString `json:"body"` User sql.NullInt64 `json:"user"` }
這樣的定義是能夠work的,可是會有一點奇怪:沒誰會用數據庫的類型來代替平時的類型。因此,咱們能夠改一種思路。在解析數據庫的時候會有專用的數據庫類型字段來接收,可是在返回json的時候有專門的model來使用,這個model用的是普通的類型。github
因此,寫起來是這樣的:golang
var person Person var personID int64 var password sql.NullString var lastLogin mysql.NullTime var isSuperuser sql.NullBool var userName sql.NullString var firstName sql.NullString var lastName sql.NullString var email sql.NullString var isStaff sql.NullBool var isActive sql.NullBool var dateJoined mysql.NullTime err = ret.Scan( &personID, &password, &lastLogin, &isSuperuser, &userName, &firstName, &lastName, &email, &isStaff, &isActive, &dateJoined, )
上面定義瞭解析,下面定義model:sql
type Person struct { ID int64 `json:"id"` Password string `json:"password"` LastLogin *time.Time `json:"last_login"` IsSuperuser bool `json:"is_superuser"` Username string `json:"username"` FirstName string `json:"first_name"` LastName string `json:"last_name"` Email string `json:"email"` IsStaff bool `json:"is_staff"` IsActive bool `json:"is_active"` DateJoined *time.Time `json:"date_joined"` }
有了前兩部以後,如今能夠裝配這些數據了:數據庫
person.ID = personID person.Password = If(password.Valid, password.String, "").(string) if tempTime, ok := If(lastLogin.Valid, lastLogin.Time, nil).(*time.Time); ok { person.LastLogin = tempTime } else { person.LastLogin = nil } person.IsSuperuser = If(isSuperuser.Valid, isSuperuser.Bool, false).(bool) person.Username = If(userName.Valid, userName.String, "").(string) person.FirstName = If(firstName.Valid, firstName.String, "").(string) person.LastName = If(lastName.Valid, lastName.String, "").(string) person.Email = If(email.Valid, email.String, "").(string) person.IsStaff = If(isStaff.Valid, isStaff.Bool, false).(bool) person.IsActive = If(isActive.Valid, isActive.Bool, false).(bool) if tempTime, ok := If(dateJoined.Valid, dateJoined.Time, nil).(*time.Time); ok { person.DateJoined = tempTime } else { person.DateJoined = nil }
有一點須要注意的。在golang裏類型轉換以前須要先作一個type assertion才行,不然報錯。並且nil
是不能作類型轉換的,好比:json
if tempTime, ok := If(dateJoined.Valid, dateJoined.Time, nil).(*time.Time); ok { person.DateJoined = tempTime } else { person.DateJoined = nil }
以上是同sqlite作爲數據庫是遇到的問題。還有一點,sqlite沒有處理時間爲空的類型,因此上面使用的是mysql的driver裏的NullTime
,奇怪的是用自定義的NullTime不行。懶得深究了,哪位知道的話但願留言。app
下面是所有的app代碼:this
package main import ( "encoding/json" "fmt" "net/http" "time" "database/sql" "log" "github.com/go-sql-driver/mysql" "github.com/labstack/echo" "github.com/labstack/echo/middleware" _ "github.com/mattn/go-sqlite3" ) func main() { // Echo instance e := echo.New() // Middleware e.Use(middleware.Logger()) e.Use(middleware.Recover()) // Routes e.GET("/", hello) // Start server e.Logger.Fatal(e.Start(":1323")) } func hello(c echo.Context) error { ret, err := executeSQL("select id,password,last_login,is_superuser,username,first_name,last_name,email,is_staff,is_active,date_joined from person where id = ?") if err != nil { return c.JSON(http.StatusOK, map[string]string{"error": "Something went wrong when getting data from db"}) } return c.JSON(http.StatusOK, ret) } // Execute sql statement from parameter, which looks like this: // select a, b, c from some_tabble where id = ? // Return a map func executeSQL(sqlStmt string) ([]Person, error) { db, err := sql.Open("sqlite3", "./db.sqlite3") if err != nil { log.Fatal(err) } defer db.Close() stmt, err := db.Prepare(sqlStmt) if err != nil { log.Fatal(err) return nil, err } defer stmt.Close() ret, err := stmt.Query(3) if err != nil { log.Fatal(err) return nil, err } personList := make([]Person, 0) for ret.Next() { var person Person var personID int64 var password sql.NullString var lastLogin mysql.NullTime var isSuperuser sql.NullBool var userName sql.NullString var firstName sql.NullString var lastName sql.NullString var email sql.NullString var isStaff sql.NullBool var isActive sql.NullBool var dateJoined mysql.NullTime err = ret.Scan( &personID, &password, &lastLogin, &isSuperuser, &userName, &firstName, &lastName, &email, &isStaff, &isActive, &dateJoined, ) if err != nil { log.Fatal(err) } person.ID = personID person.Password = If(password.Valid, password.String, "").(string) if tempTime, ok := If(lastLogin.Valid, lastLogin.Time, nil).(*time.Time); ok { person.LastLogin = tempTime } else { person.LastLogin = nil } person.IsSuperuser = If(isSuperuser.Valid, isSuperuser.Bool, false).(bool) person.Username = If(userName.Valid, userName.String, "").(string) person.FirstName = If(firstName.Valid, firstName.String, "").(string) person.LastName = If(lastName.Valid, lastName.String, "").(string) person.Email = If(email.Valid, email.String, "").(string) person.IsStaff = If(isStaff.Valid, isStaff.Bool, false).(bool) person.IsActive = If(isActive.Valid, isActive.Bool, false).(bool) if tempTime, ok := If(dateJoined.Valid, dateJoined.Time, nil).(*time.Time); ok { person.DateJoined = tempTime } else { person.DateJoined = nil } personList = append(personList, person) } j, err := json.Marshal(personList) if err != nil { log.Fatal(err) } fmt.Println(j) return personList, nil }