在用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
}

Golang处理数据库的nil数据的更多相关文章

  1. 一分钟上手, 让 Golang 操作数据库成为一种享受

    gorose, 最风骚的 go orm, 拥有链式操作, 开箱即用, 一分钟上手等八大风骚, 让 golang 操作数据库成为一种享受, 妈妈再也看不到我处理数据的痛苦了, 下面就来为大家一一讲解 g ...

  2. 将Oracle数据库中的数据写入Excel

    将Oracle数据库中的数据写入Excel 1.准备工作 Oracle数据库"TBYZB_FIELD_PRESSURE"表中数据如图: Excel模板(201512.xls): 2 ...

  3. OpenLayers添加点【php请求MySQL数据库返回GeoJSON数据】

    php请求MySQL数据库返回GeoJSON数据的实现方法请参见: http://www.cnblogs.com/marost/p/6234514.html OpenLayers[v3.19.1-di ...

  4. Django数据操作F和Q、model多对多操作、Django中间件、信号、读数据库里的数据实现分页

    models.tb.objects.all().using('default'),根据using来指定在哪个库里查询,default是settings中配置的数据库的连接名称. 外话:django中引 ...

  5. EF 连接MySQL 数据库  保存中文数据后乱码问题

    EF 连接MySQL 数据库  保存中文数据后乱码问题 采用Code First 生成的数据库,MySQL数据库中,生成的表的编码格式为***** 发现这个问题后,全部手动改成UTF8(图是另一个表的 ...

  6. java更改数据库中的数据

    不废话,上代码 package com.ningmeng; import java.sql.*; /** * 1:更改数据库中的数据 * @author biexiansheng * */ publi ...

  7. Eclipse中java向数据库中添加数据,更新数据,删除数据

    前面详细写过如何连接数据库的具体操作,下面介绍向数据库中添加数据. 注意事项:如果参考下面代码,需要 改包名,数据库名,数据库账号,密码,和数据表(数据表里面的信息) package com.ning ...

  8. C#-WinForm-ListView-表格式展示数据、如何将数据库中的数据展示到ListView中、如何对选中的项进行修改

    在展示数据库中不知道数量的数据时怎么展示最好呢?--表格 ListView - 表格形式展示数据 ListView 常用属性 HeaderStyle - "详细信息"视图中列标头的 ...

  9. phpexcel的写操作将数据库中的数据导入到excel中

    这个版本据说是可以支持excel2007,但是我使用2007编辑的xlsx是无法获得该库的支持.于是乎我就将它转化为2003.感觉支持地很好. 下面介绍一下具体的使用: require_once('. ...

随机推荐

  1. 如何用frp进行来无影去无踪

    准备工作 和 注意事项: 1.frp 下载地址  https://github.com/fatedier/frp/releases 2. 需要给有公网ip 的服务端服务器 和 本地客户端服务器 各放一 ...

  2. 了解一下SQL映射文件

    1:SQL映射文件 MyBatis真正强大之处就在于SQL映射语句,相对于强大的功能,SQL映射文件的配置非常简单,与JDBC相比减少了50%的代码.下面是关于SQL映射文件的几个顶级元素配置 map ...

  3. centos7 安装 transmission

    貌似官方文档 注意:是centos7才能用此方法 Step 1. First, you need to enable EPEL repository on your system. yum insta ...

  4. 微信小程序获取二维码参数

    var scene = decodeURIComponent(options.scene)

  5. Struts2源码解析-----转载

    前面一节描述的Struts2很多东西,这节是对源码进行分析描述,通过这一节应该对struts2有了清楚认识! 还是把Struts2这个框图贴出来: 第一步:HttpServletRequest进入到S ...

  6. Node.js前端程序通过Nginx部署后刷新出现404问题的解决办法

    方案一 (这种方式容易被第三方劫持) location / { root /data/nginx/html; index index.html index.htm; error_page 404 /i ...

  7. mysql启动服务

    mysql.server start 启动mysql服务mysql.server stop 停止mysql服务 mysql密码:123456Az_

  8. pycrypto 安装 Crypto 报错 error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": http://landinghub.visualstudio.com/visual-cpp-build-tools

    error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools&quo ...

  9. windows安装oracle遇INS 30131 错误

    win2008 64位安装oracle 11.2.0.4也遇到INS 30131 错误 描述: 原因 - 无法访问临时位置. 操作 - 请确保当前用户具有访问临时位置所需的权限. 附加信息: - 所有 ...

  10. python基础之Day23

    1.封装 什么是? 封:明确地把属性隐藏起来 ,对外隐藏,对内开放 申请名称空间,往里面装入一系列名字 /属性(类比 类 和对象   只是装的概念) 为什么要用? __init__往对象里丢属性 封装 ...