PostgreSQL 內建 point 型別(幾何點,格式 (x,y)),Go database/sql 需實作 driver.Valuer(寫入)與 sql.Scanner(掃描)介面才能直接映射結構體欄位。本文提供完整可編譯範例。
package main
import (
"bytes"
"database/sql"
"database/sql/driver"
"fmt"
"strconv"
"strings"
_ "github.com/lib/pq"
)
// Point 對應 PostgreSQL point 型別
type Point struct {
X float64 `json:"lat"`
Y float64 `json:"lng"`
}
// 實作 driver.Valuer:寫入時轉為 "(x,y)" 格式
func (p *Point) Value() (driver.Value, error) {
buf := new(bytes.Buffer)
fmt.Fprintf(buf, "(%f %f)", p.X, p.Y)
return buf.Bytes(), nil
}
func (p *Point) String() string {
return fmt.Sprintf("(%v %v)", p.X, p.Y)
}
// 實作 sql.Scanner:掃描時解析 "(x,y)" 格式
func (p *Point) Scan(val interface{}) error {
if bb, ok := val.([]uint8); ok {
// 移除首尾括號
tmp := bb[1 : len(bb)-1]
coords := strings.Split(string(tmp), ",")
var err error
if p.X, err = strconv.ParseFloat(coords[0], 64); err != nil {
return err
}
if p.Y, err = strconv.ParseFloat(coords[1], 64); err != nil {
return err
}
}
return nil
}
type Agent struct {
ID int `json:"id"`
Name string `json:"name"`
Code string `json:"code"`
CSNo string `json:"cs_no"`
ChannelID int `json:"channel_id"`
Address string `json:"address"`
Coordinate *Point `json:"point"`
}
func main() {
pgURL := fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=disable",
"postgres", "123456", "localhost", "5432", "people")
db, err := sql.Open("postgres", pgURL)
if err != nil {
panic(fmt.Errorf("連線失敗: %v", err))
}
defer db.Close()
// 查詢掃描
querySQL := `SELECT * FROM t_agent`
rows, err := db.Query(querySQL)
if err != nil {
panic(fmt.Errorf("查詢失敗: %v", err))
}
defer rows.Close()
for rows.Next() {
ag := &Agent{Coordinate: &Point{}}
err = rows.Scan(&ag.ID, &ag.Name, &ag.Code, &ag.CSNo, &ag.ChannelID, &ag.Address, ag.Coordinate)
fmt.Println(ag, err)
}
// 插入並回傳 ID
var id int
err = db.QueryRow(
`INSERT INTO t_agent (name, code, cs_no, address, coordinate) VALUES($1,$2,$3,$4,$5) RETURNING id`,
"test1", "123457", "2", "111", "(12,43)").Scan(&id)
fmt.Println("id:", id, "err:", err)
}
| 介面 | 用途 | 方法簽名 |
|---|---|---|
| driver.Valuer | Go → DB 寫入 | Value() (driver.Value, error) |
| sql.Scanner | DB → Go 掃描 | Scan(src interface{}) error |