Cri*_* A. 5 postgresql json go
假设我有以下结构,它是映射表。
type Publisher struct{
ID int `db:"id"`
Name string `db:"name"`
Books []*Book
}
type Book struct {
ID int `db:"id"`
Name string `db:"name"`
PublisherID `db:"publisher_id"`
}
Run Code Online (Sandbox Code Playgroud)
所以,如果我想检索所有相关书籍的所有出版商,我想得到JSON
这样的结果:
[ //Publisher 1
{
"id" : "10001",
"name":"Publisher1",
"books" : [
{ "id":321,"name": "Book1"},
{ "id":333,"name": "Book2"}
]
},
//Publisher 2
{
"id" : "10002",
"name":"Slytherin Publisher",
"books" : [
{ "id":4021,"name": "Harry Potter and the Chamber of Secrets"},
{ "id":433,"name": "Harry Potter and the Order of the Phoenix"}
]
},
]
Run Code Online (Sandbox Code Playgroud)
所以我有以下结构,我用来检索与相关的所有类型的查询 Publisher
type PublisherRepository struct{
Connection *sql.DB
}
// GetEbooks return all the books related with a publisher
func (r *PublisherRepository) GetBooks(idPublisher int) []*Book {
bs := make([]Book,0)
sql := "SELECT * FROM books b WHERE b.publisher_id =$1 "
row, err := r.Connection.Query(sql,idPublisher)
if err != nil {
//log
}
for rows.Next() {
b := &Book{}
rows.Scan(&b.ID, &b.Name, &b.PublisherID)
bs := append(bs,b)
}
return bs
}
func (r *PublisherRepository) GetAllPublishers() []*Publisher {
sql := "SELECT * FROM publishers"
ps := make([]Publisher,0)
rows, err := r.Connection.Query(sql)
if err != nil {
// log
}
for rows.Next() {
p := &Publisher{}
rows.Scan(&p.ID,&p.Name)
// Is this the best way?
books := r.GetBooks(p.ID)
p.Books = books
}
return ps
}
Run Code Online (Sandbox Code Playgroud)
所以,这里是我的问题
publisher
以最佳性能检索所有内容的最佳方法是什么,因为 a for
inside afor
不是最佳解决方案,如果我有 200 个出版商并且每个出版商平均有 100 本书怎么办。
在 GoLang 中是惯用的PublisherRepository
还是有另一种方法来创建一些东西来管理带有纯 sql 的实体的事务?
1) 不好的地方是每次迭代的 sql 请求。所以这里有一个不会为每个发布者提出额外请求的解决方案:
func (r *PublisherRepository) GetAllPublishers() []*Publisher {
sql := "SELECT * FROM publishers"
ps := make(map[int]*Publisher)
rows, err := connection.Query(sql)
if err != nil {
// log
}
for rows.Next() {
p := &Publisher{}
rows.Scan(&p.ID,&p.Name)
ps[p.ID] = p
}
sql = "SELECT * FROM books"
rows, err := connection.Query(sql)
if err != nil {
//log
}
for rows.Next() {
b := &Book{}
rows.Scan(&b.ID, &b.Name, &b.PublisherID)
ps[b.PublisherID].Books = append(ps[b.PublisherID].Books, b)
}
// you might choose to keep the map as a return value, but otherwise:
// preallocate memory for the slice
publishers := make([]*Publisher, 0, len(ps))
for _, p := range ps {
publishers = append(publishers, p)
}
return publishers
}
Run Code Online (Sandbox Code Playgroud)
2) 除非您只创建一次 PublisherRepository,否则创建和关闭连接负载可能是个坏主意。还取决于您的 sql 客户端实现,我建议(并且也已经在许多其他 go 数据库客户端中看到过)为整个服务器建立一个连接。池化是由许多 sql 客户端在内部完成的,这就是为什么你应该检查你的 sql 客户端。如果您的 sql 客户端库在内部进行池化,则为“连接”使用全局变量(如果池化是在内部完成的,则它实际上不是一个连接):
connection *sql.DB
func New () *PublisherRepository {
repo := &PublisherRepository{}
return repo.connect()
}
type PublisherRepository struct{
}
func (r *PublisherRepository) connect() *PublisherRepository {
// open new connection if connection is nil
// or not open (if there is such a state)
// you can also check "once.Do" if that suits your needs better
if connection == nil {
// ...
}
return r
}
Run Code Online (Sandbox Code Playgroud)
所以每次你创建一个新的 PublisherRepository 时,它只会检查连接是否已经存在。如果你使用 once.Do,go 只会创建一次“连接”,你就完成了。
如果您有其他结构也将使用该连接,则您的连接变量需要一个全局位置,或者(甚至更好)您为您的 sql 客户端编写一个小包装包,然后在您的所有结构中使用它。