zoukankan      html  css  js  c++  java
  • Golang处理数据库的nil数据

    在用golang获取数据库的数据的时候,难免会遇到可控field。这个时候拿到的数据如果直接用string, time.Time这样的类型来解析的话会遇到panic。

    那么如何处理这个问题呢,第一个出现在眼前的办法就是用database/sql。这个包里包含了很多的可以处理可控字段的类型,比如:sql.NullString, sql.NullBool等。所以,model可以用这些类型来定义,如:

    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用的是普通的类型。

    所以,写起来是这样的:

    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:

    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是不能做类型转换的,比如:

    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代码:

    
    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
    }
    
    
  • 相关阅读:
    终于在园子里安家了
    Ajax简单应用,检测用户名是否存在 (转)
    ASP.NET 2.0防止同一用户同时登陆方法一(转)
    .NET:七道asp.net页面传值题(转)
    蛙蛙推荐:SQLServer优化资料整理(转)
    我说看起来怎么这么眼熟
    ASP.NET 2.0防止同一用户同时登陆方法二(转)
    Javascirpt Function 不能命名为reset
    关于Jquery ajax调用一般处理程序Handler报500 错误(Internal Server Error)解决办法
    智能匹配
  • 原文地址:https://www.cnblogs.com/sunshine-anycall/p/9738801.html
Copyright © 2011-2022 走看看