英文:
Do you know how to bind one-many association to Golang struct without orm?
问题
你知道如何在不使用 ORM 的情况下,将一对多关联绑定到 Golang 结构体吗?
我想将下面的 SQL 结果绑定到以下的 Golang 结构体:
CREATE TABLE
users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255)
);
CREATE TABLE
posts (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
title VARCHAR(255) NOT NULL
);
INSERT INTO users (name) VALUES ("one"), ("two");
INSERT INTO
posts (user_id, title)
VALUES (1, "one's post"), (1, "one's second post"), (1, "one's third post"), (2, "two's post"), (2, "two's second post"), (2, "two's third post");
SELECT u.id, u.name, p.id, p.title
FROM users AS u
JOIN posts AS p ON u.id = p.user_id;
type (
User struct {
ID int
Name string
Posts []*Post
}
Post struct {
ID int
Title string
}
)
我经常使用 MySQL,所以我想要使用 MySQL 的方法。但是如果你知道 PostgreSQL、MariaDb 等其他数据库的好方法,请教我那种方法。
英文:
Do you know how to bind a one-to-many association to a Golang struct without using an ORM?
I want to bind the SQL results below to the following Golang struct:
CREATE TABLE
users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255)
);
CREATE TABLE
posts (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
title VARCHAR(255) NOT NULL
);
INSERT INTO users (name) VALUES ("one"), ("two");
INSERT INTO
posts (user_id, title)
VALUES (1, "one's post"), (1, "one's second post"), (1, "one's third post"), (2, "two's post"), (2, "two's second post"), (2, "two's third post");
SELECT u.id, u.name, p.id, p.title
FROM users AS u
JOIN posts AS p ON u.id = p.user_id;
type (
User struct {
ID int
Name string
Posts []*Post
}
Post struct {
ID int
Title string
}
)
I often use MySQL. So I want the method with MySQL. But if you know good method with PostgreSQL, MariaDb and so on, please teach me that method.
答案1
得分: 2
func example() ([]*User, error) {
// 执行查询语句
rows, err := db.Query(`SELECT ...`)
if err != nil {
return nil, err
}
defer rows.Close()
// 使用映射以便于查找已经扫描过的用户
userMap := make(map[int]*User)
// 遍历返回的行
for rows.Next() {
// 为查询返回的每一列声明一个变量
var (
uid int
uname string
// 使用 sql.NullXxx 类型,因为你的 JOIN 可能会导致包含 NULL 值的行,
// 或者在查询中使用 COALESCE,这样你就不需要使用这些类型。
pid sql.NullInt64
ptitle sql.NullString
)
// 将当前行的列扫描到变量中
if err := rows.Scan(&uid, &uname, &pid, &ptitle); err != nil {
return nil, err
}
// 使用扫描到的 id 在映射中查找用户
u, ok := userMap[uid]
if !ok {
// 如果找不到,则创建一个新的用户实例并将其存储在映射中
u = &User{ID: uid, Name: uname}
userMap[uid] = u
}
// 将帖子实例添加到用户中
u.Posts = append(u.Posts, &Post{
ID: int(pid.Int64),
Title: ptitle.String,
})
}
if err := rows.Err(); err != nil {
return nil, err
}
// 将映射转换为切片
users := make([]*User, 0, len(userMap))
for _, u := range userMap {
users = append(users, u)
}
return users, nil
}
英文:
func example() ([]*User, error) {
// execute select query
rows, err := db.Query(`SELECT ...`)
if err != nil {
return nil, err
}
defer rows.Close()
// use a map for easy lookup of already scanned users
userMap := make(map[int]*User)
// iterate over the returned rows
for rows.Next() {
// declare a variable for each column returned by the query
var (
uid int
uname string
// Use sql.NullXxx types since your JOIN
// can potentially result in rows with NULLs,
// or use COALESCE in your query, then you won't
// have to use these types.
pid sql.NullInt64
ptitle sql.NullString
)
// scan the current row's columns into the variables
if err := rows.Scan(&uid, &uname, &pid, &ptitle); err != nil {
return nil, err
}
// lookup the user in the map using the scanned id
u, ok := userMap[uid]
if !ok {
// if not found, create a new instance of the user and store it in the map
u = &User{ID: uid, Name: uname}
userMap[uid] = u
}
// add the post instance to the user
u.Posts = append(u.Posts, &Post{
ID: int(pid.Int64),
Title: ptitle.String,
})
}
if err := rows.Err(); err != nil {
return nil, err
}
// convert map into slice
users := make([]*User, 0, len(userMap))
for _, u := range userMap {
users = append(users, u)
}
return users, nil
}
答案2
得分: 1
如果你想在没有ORM的情况下将一个一对多的关联绑定到一个Golang结构体中,你可以使用Golang的MySQL驱动程序执行查询,从数据库中获取所需的数据,然后手动将结果映射到结构体中。
我将给你一个使用MySQL驱动程序实现这一目标的示例:
/* * @Author: Sk Shahriar Ahmed Raka * Email: skshahriarahmedraka@gmail.com * @Last Modified by: Sk Shahirar Ahmed Raka * @Last Modified time: 2023-04-23 00:16:31 */
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
type User struct {
ID int
Name string
Posts []*Post
}
type Post struct {
ID int
Title string
}
func main() {
// 连接到MySQL数据库
db, err := sql.Open("mysql", "username:password@tcp(localhost:3306)/databasename")
if err != nil {
fmt.Println("🚀 sql.Open() ~ err :", err)
}
defer db.Close()
// 准备SQL查询语句
query := `SELECT u.id, u.name, p.id, p.title FROM Users AS u JOIN posts AS p ON u.id = p.user_id `
// 执行查询语句
rows, err := db.Query(query)
if err != nil {
fmt.Println("🚀 db.Query(query)~ err :", err)
}
defer rows.Close()
// 将结果映射到结构体
Users := []*User{}
var userMap = make(map[int]*User)
for rows.Next() {
userID := 0
userName := ""
postID := 0
postTitle := ""
err := rows.Scan(&userID, &userName, &postID, &postTitle)
if err != nil {
fmt.Println("🚀 rows.Scan() ~ err :", err)
}
user, ok := userMap[userID]
if !ok {
user = &User{ID: userID, Name: userName, Posts: make([]*Post, 0)}
userMap[userID] = user
Users = append(Users, user)
}
user.Posts = append(user.Posts, &Post{ID: postID, Title: postTitle})
}
// 打印结果
for _, user := range Users {
fmt.Printf("UserID: %d, Name: %s\n", user.ID, user.Name)
for _, post := range user.Posts {
fmt.Printf(" PostID: %d, Title: %s\n", post.ID, post.Title)
}
}
}
对于这个示例,首先你需要使用database/sql
包的sql.Open
函数连接到MySQL数据库,并准备用于获取所需数据的SQL查询语句。然后使用db.Query
函数执行查询,并循环遍历结果行,手动将数据映射到User
和Post
结构体中。为了将结果映射到结构体,你首先创建一个基于ID存储User
结构体的映射,然后循环遍历行来创建或更新User
结构体及其关联的Post
结构体。最后,通过循环遍历User
结构体及其关联的Post
结构体来打印结果。
英文:
If you want to bind a one-to-many association to a Golang struct without an ORM, you can use the MySQL driver for Golang to execute a query that fetches the required data from the database and then manually map the result to the struct.
I will give you an example of how you can achieve this using the MySQL driver:
/* * @Author: Sk Shahriar Ahmed Raka * Email: skshahriarahmedraka@gmail.com * @Last Modified by: Sk Shahirar Ahmed Raka * @Last Modified time: 2023-04-23 00:16:31 */
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
type User struct {
ID int
Name string
Posts []*Post
}
type Post struct {
ID int
Title string
}
func main() {
// Connect to the mysql database
db, err := sql.Open("mysql", "username:password@tcp(localhost:3306)/databasename")
if err != nil {
fmt.Println("🚀 sql.Open() ~ err : ", err)
}
defer db.Close()
// Prepare the SQL query in a string format
query := `SELECT u.id, u.name, p.id, p.title FROM Users AS u JOIN posts AS p ON u.id = p.user_id `
// Execute your query string
rows, err := db.Query(query)
if err != nil {
fmt.Println("🚀 db.Query(query)~ err : ", err)
}
defer rows.Close()
// you have to map the results to the struct
Users := []*User{}
var userMap = make(map[int]*User)
for rows.Next() {
userID:=0
userName:=""
postID:=0
postTitle:=""
err := rows.Scan(&userID, &userName, &postID, &postTitle)
if err != nil {
fmt.Println("🚀 rows.Scan() ~ err : ", err)
}
user, ok := userMap[userID]
if !ok {
user = &User{ID: userID, Name: userName, Posts: make([]*Post, 0)}
userMap[userID] = user
Users = append(Users, user)
}
user.Posts = append(user.Posts, &Post{ID: postID, Title: postTitle})
}
// Print your results
for _, user := range Users {
fmt.Printf("UserID: %d, Name: %s\n", user.ID, user.Name)
for _, post := range user.Posts {
fmt.Printf(" PostID: %d, Title: %s\n", post.ID, post.Title)
}
}
}
For this example, first you need to connect to the MySQL database using the sql.Open
function from the database/sql
package and prepare the SQL query to fetch the required data. You then execute the query using the db.Query
function and loop over the resulting rows to map the data to the User
and Post
structs manually.To map the results to the structs, you first create a map to store the User
structs based on their ID, and then loop over the rows to create or update the User
structs and their associated Post
structs. you print the results by looping over the User
structs and their associated Post
structs.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论