구조체가 있다고 가정 해 보겠습니다.
type User struct {
Name string
Id int
Score int
}
그리고 동일한 스키마를 가진 데이터베이스 테이블. 데이터베이스 행을 구조체로 구문 분석하는 가장 쉬운 방법은 무엇입니까? 아래에 답변을 추가했지만 그것이 가장 좋은 것인지 잘 모르겠습니다.
답변:
Go 패키지 테스트는 종종 작업 방법에 대한 단서를 제공합니다. 예를 들어 database/sql/sql_test.go
,
func TestQuery(t *testing.T) {
/* . . . */
rows, err := db.Query("SELECT|people|age,name|")
if err != nil {
t.Fatalf("Query: %v", err)
}
type row struct {
age int
name string
}
got := []row{}
for rows.Next() {
var r row
err = rows.Scan(&r.age, &r.name)
if err != nil {
t.Fatalf("Scan: %v", err)
}
got = append(got, r)
}
/* . . . */
}
func TestQueryRow(t *testing.T) {
/* . . . */
var name string
var age int
var birthday time.Time
err := db.QueryRow("SELECT|people|age,name|age=?", 3).Scan(&age)
/* . . . */
}
귀하의 질문에 대해 행을 구조로 쿼리하면 다음과 같이 번역됩니다.
var row struct {
age int
name string
}
err = db.QueryRow("SELECT|people|age,name|age=?", 3).Scan(&row.age, &row.name)
귀하의 솔루션과 비슷해 보이지만 솔루션을 찾는 방법을 보여주는 것이 중요합니다.
github.com/jmoiron/sqlx를 권장 합니다. .
README에서 :
sqlx는 이동 표준
database/sql
라이브러리 에서 확장 세트를 제공하는 라이브러리입니다. 의 SQLX 버전sql.DB
,sql.TX
,sql.Stmt
, 등. 모두 기본 인터페이스를 그대로 유지하므로 해당 인터페이스가 표준 인터페이스의 상위 집합이됩니다. 따라서 데이터베이스 / SQL을 사용하여 기존 코드베이스를 sqlx와 통합하는 것이 비교적 어렵지 않습니다.주요 추가 개념은 다음과 같습니다.
- 행을 구조체 (내장 구조체 지원 포함), 맵 및 슬라이스로 마샬링
- 준비된 명령문을 포함한 명명 된 매개 변수 지원
Get
및Select
구조체 / 슬라이스 쿼리에서 빠르게 이동합니다
README에는 행을 구조체로 스캔하는 방법을 보여주는 코드 스 니펫도 포함되어 있습니다.
type Place struct {
Country string
City sql.NullString
TelephoneCode int `db:"telcode"`
}
// Loop through rows using only one struct
place := Place{}
rows, err := db.Queryx("SELECT * FROM place")
for rows.Next() {
err := rows.StructScan(&place)
if err != nil {
log.Fatalln(err)
}
fmt.Printf("%#v\n", place)
}
각 열을 구조체의 필드에 수동으로 매핑 할 필요가 없었습니다. sqlx에는 struct 필드에 대한 데이터베이스 열에 대한 몇 가지 기본 매핑이 있으며 태그를 사용하여 데이터베이스 열을 지정할 수 있습니다 ( 위 TelephoneCode
의 Place
struct 필드 참고 ). 이에 대한 자세한 내용 은 설명서를 참조하십시오 .
여기에 한 가지 방법이 Scan
있습니다. 함수 에서 모든 구조체 값을 수동으로 할당하기 만하면 됩니다.
func getUser(name string) (*User, error) {
var u User
// this calls sql.Open, etc.
db := getConnection()
// note the below syntax only works for postgres
err := db.QueryRow("SELECT * FROM users WHERE name = $1", name).Scan(&u.Id, &u.Name, &u.Score)
if err != nil {
return &User{}, err
} else {
return &u, nil
}
}
sql.NullString
구조를 사용 하고 변환 하면 문제가 JSON
발생합니다. 생성 된 것은 친숙하지 않습니다. a VO
또는 이와 비슷한 것을 사용해야 합니다
rows, err := connection.Query("SELECT `id`, `username`, `email` FROM `users`")
if err != nil {
panic(err.Error())
}
for rows.Next() {
var user User
if err := rows.Scan(&user.Id, &user.Username, &user.Email); err != nil {
log.Println(err.Error())
}
users = append(users, user)
}
이를위한 패키지가 있습니다 : sqlstruct
안타깝게도 지난번에 내장 된 구조체를 지원하지 않는지 확인했습니다 (자신을 구현하는 것은 사소한 일입니다. 몇 시간 만에 프로토 타입을 만들었습니다).
방금 sqlstruct에 대한 변경 사항을 커밋했습니다.
사용 : go-models-mysql sqlbuilder
val, err = m.ScanRowType(row, (*UserTb)(nil))
또는 전체 코드
import (
"database/sql"
"fmt"
lib "github.com/eehsiao/go-models-lib"
mysql "github.com/eehsiao/go-models-mysql"
)
// MyUserDao : extend from mysql.Dao
type MyUserDao struct {
*mysql.Dao
}
// UserTb : sql table struct that to store into mysql
type UserTb struct {
Name sql.NullString `TbField:"Name"`
Id int `TbField:"Id"`
Score int `TbField:"Score"`
}
// GetFirstUser : this is a data logical function, you can write more logical in there
// sample data logical function to get the first user
func (m *MyUserDao) GetFirstUser() (user *User, err error) {
m.Select("Name", "Id", "Score").From("user").Limit(1)
fmt.Println("GetFirstUser", m.BuildSelectSQL().BuildedSQL())
var (
val interface{}
row *sql.Row
)
if row, err = m.GetRow(); err == nil {
if val, err = m.ScanRowType(row, (*UserTb)(nil)); err == nil {
u, _ := val.(*UserTb)
user = &User{
Name: lib.Iif(u.Name.Valid, u.Nae.String, "").(string),
Id: u.Id,
Score: u.Score,
}
}
}
row, val = nil, nil
return
}
이를위한 라이브러리가 있습니다 : scany .
다음과 같이 사용할 수 있습니다.
type User struct {
Name string
Id int
Score int
}
// db is your *sql.DB instance
// ctx is your current context.Context instance
// Use sqlscan.Select to query multiple records.
var users []*User
sqlscan.Select(ctx, db, &users, `SELECT name, id, score FROM users`)
// Use sqlscan.Get to query exactly one record.
var user User
sqlscan.Get(ctx, db, &user, `SELECT name, id, score FROM users WHERE id=123`)
잘 문서화되어 있고 작업하기 쉽습니다.
면책 조항 : 나는이 도서관의 저자입니다.