教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 golang sqlx scan 到结构体中_GOLANG:使用SQLX库执行简单的SQL语句

golang sqlx scan 到结构体中_GOLANG:使用SQLX库执行简单的SQL语句

发布时间:2022-02-03   编辑:jiaochengji.com
教程集为您提供golang sqlx scan 到结构体中,GOLANG:使用SQLX库执行简单的SQL语句等资源,欢迎您收藏本站,我们将为您提供最新的golang sqlx scan 到结构体中,GOLANG:使用SQLX库执行简单的SQL语句资源

虽然 GOLANG 自带 SQL 库 database/sql,但为了简化代码提高编程效率,笔者推荐使用第三方库来处理数据库相关操作。

本文介绍如何使用 SQLX 库连接 MySQL 数据库并执行简单的 SELECT 查询语句。

示例代码来自 nwpc-oper/nmc-typhoon-db-client 项目,从 NMC 台风数据库中检索台风数据,计划用于 CMA-PI 上的业务系统。

<h2>安装</h2>

连接 MySQL 数据库需要下载数据库驱动 Go-MySQL-Driver

<pre class="has"><code>go get -u github.com/go-sql-driver/mysql</code></pre>

下载 SQLX 库

<pre class="has"><code>go get github.com/jmoiron/sqlx</code></pre>

在源码中载入上述两个库

<pre class="has"><code>import (
// ...skip... _ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
)
</code></pre> <h2>创建数据库连接</h2>

SQLX 使用的数据库连接地址字符串与 SQLAlchemy 不同,格式如下所示

<pre class="has"><code>user:password@tcp(host:port)/database?param1=value1
</code></pre>

为了解析时间字段,增加 <code>parseTime=true</code> 参数

<pre class="has"><code>var db *sqlx.DB

conn := fmt.Sprintf(
"%s:%s@tcp(%s)/%s?charset=utf8&parseTime=true&loc=Local",
config.Auth.User,
config.Auth.Password,
config.Host,
config.DatabaseName,
)

db, err = sqlx.Open("mysql", conn)

if err != nil {
log.Fatal("open db connection has error:", err)
return nil, err
}

defer db.Close()
</code></pre> <h2>执行查询</h2>

使用 <code>sqlx.DB</code> 对象的 <code>Queryx()</code> 函数执行 SQL 语句,返回值中的 <code>rows</code> 是一个迭代器。

<pre class="has"><code>querySQL := fmt.Sprintf("SELECT %s FROM %s WHERE %s AND %s",
queryColumnsString,
tableName,
datetimeQuery,
forecastHourQuery,
)

rows, err := db.Queryx(querySQL)

if err != nil {
log.Fatal("query db has error:", err)
return nil, err
}

defer rows.Close()
</code></pre> <h2>处理结果</h2>

在标准库 database/sql 中,对每行对象使用 <code>Scan()</code> 方法获取具体的字段值。

该方法需要为每个字段设置一个变量。例如

<pre class="has"><code>var (
name string,
number int,
speed float,
)
row.Scan(&name, &number, &speed)
</code></pre>

如果检索结果中字段很多,就可能会导致代码不够简洁。

SQLX 库提供 <code>StructScan()</code> 方法,可以将所有字段放入一个结构体中。

下面首先定义结构体 <code>Record</code> 代表每行数据,再将检索结果全部转为 <code>Record</code>。

<h3>定义记录结构体</h3>

为每个字段都创建对应的成员,可以使用名称为 <code>db</code> 的 struct tag,为字段指定对应的列名。

<pre class="has"><code>type Record struct {
Xuhao int `csv:"xuhao"`
Center string `csv:"center"`
Bwtype DataString `csv:"bwtype"`
FcstType string `db:"FCSTType" csv:"FCSTType"` Zone DataString `csv:"zone"`
Tfbh DataString `csv:"tfbh"`
Tfbhbabj DataString `csv:"tfbhbabj"`
Engname DataString `csv:"engname"`
Datetime NullDateTime `csv:"datetime"`
Fcsthour int `csv:"fcsthour"`
Lat DataFloat64 `csv:"lat"`
Lon DataFloat64 `csv:"lon"`
Pressure DataInt32 `csv:"pressure"`
Windv DataFloat64 `csv:"windv"`
Gusts DataFloat64 `csv:"gusts"`
Strength DataString `csv:"strength"`
Windclass DataInt32 `csv:"windclass"`
Movedir DataString `csv:"movedir"`
Movespeed DataFloat64 `csv:"movespeed"`
Wind7v1 DataFloat64 `csv:"wind7v1"`
Wind7v2 DataFloat64 `csv:"wind7v2"`
Wind7v3 DataFloat64 `csv:"wind7v3"`
Wind7v4 DataFloat64 `csv:"wind7v4"`
Wind10v1 DataFloat64 `csv:"wind10v1"`
Wind10v2 DataFloat64 `csv:"wind10v2"`
Wind10v3 DataFloat64 `csv:"wind10v3"`
Wind10v4 DataFloat64 `csv:"wind10v4"`
Wind12v1 DataFloat64 `csv:"wind12v1"`
Wind12v2 DataFloat64 `csv:"wind12v2"`
Wind12v3 DataFloat64 `csv:"wind12v3"`
Wind12v4 DataFloat64 `csv:"wind12v4"`
}
</code></pre>

注:上面的代码中使用了自定义的数据类型 <code>DataString</code>,<code>DataFloat64</code>,<code>DataInt32</code> 和 <code>NullDateTime</code>,并定义名为 csv 的 struct tag,这都是为了将结果输出到 CSV 格式文件,后面会介绍。

<h3>解析检索数据</h3>

使用 <code>StructScan</code> 方法根据每行结果填充 <code>Record</code> 对象,将所有结果放到 <code>records</code> 数组中。

<pre class="has"><code>records := []Record{}
for rows.Next() {
var record Record
err = rows.StructScan(&record)

if err != nil {
log.Fatal("scan row has error:", err)
return nil, err
}

records = append(records, record)
}

err = rows.Err()
if err != nil {
log.Fatal("query rows has error:", err)
return nil, err
}
</code></pre> <h2>输出CSV文件</h2>

使用 csvutil 库将 <code>Records</code> 对象输出到 CSV 文件中

<h3>自定义类型</h3>

因为数据库中的部分字段可以为空,所以不能使用内置的格式,而是使用类似 <code>mysql.NullTime</code>,<code>sql.NullString</code> 等特殊类型。

csvutil 库在输出 CSV 文件时会调用对象的 <code>MarshalCSV()</code> 方法,上述这些特殊类型都没有定义该方法,所以无法直接使用。

nmc-typhoon-db-client 项目使用自定义类型封装上述类型,并为这些自定义类型创建 <code>MarshalCSV()</code> 方法。这就是上面提到的 <code>DataString</code>,<code>DataFloat64</code>,<code>DataInt32</code> 和 <code>NullDateTime</code> 几种类型。

以 <code>DataInt32</code> 为例说明。<code>MarshalCSV()</code> 方法会检测是否为空,只有不为空的情况下才会返回字节数组。

<pre class="has"><code>type DataInt32 struct {
sql.NullInt32
}

func (d DataInt32) MarshalCSV() ([]byte, error) {
if d.Valid {
return []byte(fmt.Sprintf("%d", d.Int32)), nil
} else {
return nil, nil
}
}
</code></pre>

另外,这种方式也可以用于自定义日期和时间的序列化。

<code>DateTime</code> 是对 <code>time.Time</code> 的封装,序列化时返回 <code>YYYYMMDDHH</code> 格式的字符串

<pre class="has"><code>type DateTime struct {
time.Time
}

func (date DateTime) MarshalCSV() ([]byte, error) {
return []byte(date.Format("200601021504")), nil
}
</code></pre> <h3>输出</h3>

<code>csvutil.Marshal()</code> 函数返回序列化后的字节数组,可以直接写入到文件中。

默认设置下,输出的 CSV 文件使用逗号分隔,并包含表头。

<pre class="has"><code>b, err := csvutil.Marshal(records)
if err != nil {
fmt.Println("error:", err)
return err
}

f, err := os.Create(filePath)
if err != nil {
fmt.Println("crate file error:", err)
return err
}
f.Write(b)
f.Close()
</code></pre> <h2>讨论</h2>

当前代码还有改进空间,<code>queryColumnsString</code> 字符串由列名字符串数组拼接而成,与 <code>Record</code> 类型的成员变量名称有重复。后续可以考虑使用反射机制,直接从 <code>Record</code> 类型生成 <code>queryColumnsString</code> 字符串。

<h2>参考</h2>

nwpc-oper/nmc-typhoon-db-client

https://github.com/nwpc-oper/nmc-typhoon-db-client

《SQLAlchemy:执行简单的SQL语句》介绍使用 Python 的 SQLAlchemy 库实现同样的功能。


题图由 Presentsquare Presentsquare 在 Pixabay 上发布。
到此这篇关于“golang sqlx scan 到结构体中_GOLANG:使用SQLX库执行简单的SQL语句”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
golang sqlx scan 到结构体中_GOLANG:使用SQLX库执行简单的SQL语句
go对接接口
【七天从零实现ORM框架】Day02:database/sql基础
零基础学习Go语言
golang sdk后端怎么用_Golang资深后端工程师需要了解的知识点
SQL2Struct:一款根据sql语句自动生成golang结构体的chrome插件
数据库查询语言(1)
php安全问题思考
JDBCTM 指南:入门4 - Statement
golang 回收机制

[关闭]
~ ~