Skip to content

Commit

Permalink
Implement eager fetch by inner join
Browse files Browse the repository at this point in the history
  • Loading branch information
ybkuroki committed Sep 22, 2020
1 parent bf00683 commit 50c1b64
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 28 deletions.
60 changes: 46 additions & 14 deletions model/book.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,34 @@ type Book struct {
Format *Format `json:"format"`
}

// RecordBook defines struct represents the record of the database.
type RecordBook struct {
ID uint
Title string
Isbn string
CategoryID uint
CategoryName string
FormatID uint
FormatName string
}

const selectBook = "select b.*, c.id as category_id, c.name as category_name, f.id as format_id, f.name as format_name " +
"from book b inner join category_master c on c.id = b.category_id inner join format_master f on f.id = b.format_id "

// TableName returns the table name of book struct and it is used by gorm.
func (Book) TableName() string {
return "book"
}

// NewBook is constructor
func NewBook(title string, isbn string, category *Category, format *Format) *Book {
return &Book{Title: title, Isbn: isbn, Category: category, Format: format}
func NewBook(title string, isbn string, categoryID uint, formatID uint) *Book {
return &Book{Title: title, Isbn: isbn, CategoryID: categoryID, FormatID: formatID}
}

// FindByID returns a book full matched given book's ID.
func (b *Book) FindByID(rep *repository.Repository, id uint) (*Book, error) {
var book Book
if error := rep.Preload("Category").Preload("Format").Where("id = ?", id).Find(&book).Error; error != nil {
if error := rep.Where("id = ?", id).Find(&book).Error; error != nil {
return nil, error
}
return &book, nil
Expand All @@ -40,7 +54,7 @@ func (b *Book) FindByID(rep *repository.Repository, id uint) (*Book, error) {
// FindAll returns all books of the book table.
func (b *Book) FindAll(rep *repository.Repository) (*[]Book, error) {
var books []Book
if error := rep.Preload("Category").Preload("Format").Find(&books).Error; error != nil {
if error := rep.Find(&books).Error; error != nil {
return nil, error
}
return &books, nil
Expand All @@ -49,26 +63,38 @@ func (b *Book) FindAll(rep *repository.Repository) (*[]Book, error) {
// FindAllByPage returns the page object of all books.
func (b *Book) FindAllByPage(rep *repository.Repository, page int, size int) (*Page, error) {
var books []Book
p := createPage(rep, &books, page, size)

if error := rep.Preload("Category").Preload("Format").Offset(page * p.Size).Limit(size).Find(&books).Error; error != nil {
return nil, error
var rec RecordBook
rows, err := rep.Raw(selectBook+" limit ? offset ? ", size, page*size).Rows()
if err != nil {
return nil, err
}
for rows.Next() {
rep.ScanRows(rows, &rec)
book := converToEntity(&rec)
books = append(books, *book)
}

p.Content = &books
p := createPage(rep, &books, page, size)
return p, nil
}

// FindByTitle returns the page object of books partially matched given book title.
func (b *Book) FindByTitle(rep *repository.Repository, title string, page int, size int) (*Page, error) {
var books []Book
p := createPage(rep, &books, page, size)

if error := rep.Preload("Category").Preload("Format").Where("title LIKE ?", "%"+title+"%").Offset(page * p.Size).Limit(size).Find(&books).Error; error != nil {
return nil, error
var rec RecordBook
rows, err := rep.Raw(selectBook+" where title like ? limit ? offset ? ", "%"+title+"%", size, page*size).Rows()
if err != nil {
return nil, err
}
for rows.Next() {
rep.ScanRows(rows, &rec)
book := converToEntity(&rec)
books = append(books, *book)
}

p.Content = &books
p := createPage(rep, &books, page, size)
return p, nil
}

Expand All @@ -77,9 +103,9 @@ func createPage(rep *repository.Repository, books *[]Book, page int, size int) *
p.Page = page
p.Size = size
p.NumberOfElements = p.Size

rep.Preload("Category").Preload("Format").Find(&books).Count(&p.TotalElements)
p.TotalElements = len(*books)
p.TotalPages = int(math.Ceil(float64(p.TotalElements) / float64(p.Size)))
p.Content = books

return p
}
Expand Down Expand Up @@ -116,6 +142,12 @@ func (b *Book) Delete(rep *repository.Repository) (*Book, error) {
return b, nil
}

func converToEntity(rec *RecordBook) *Book {
c := &Category{ID: rec.CategoryID, Name: rec.CategoryName}
f := &Format{ID: rec.FormatID, Name: rec.FormatName}
return &Book{ID: rec.ID, Title: rec.Title, Isbn: rec.Isbn, CategoryID: rec.CategoryID, Category: c, FormatID: rec.FormatID, Format: f}
}

// ToString is return string of object
func (b *Book) ToString() (string, error) {
bytes, error := json.Marshal(b)
Expand Down
9 changes: 9 additions & 0 deletions model/category.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ func (c *Category) FindByID(rep *repository.Repository, id uint) (*Category, err
return &category, nil
}

// CountByID returns a size of categories full matched given category's ID.
func (c *Category) CountByID(rep *repository.Repository, id uint) (int, error) {
var count int
if error := rep.Model(&Category{}).Where("id = ?", id).Count(&count).Error; error != nil {
return 0, error
}
return count, nil
}

// FindAll returns all categories of the category table.
func (c *Category) FindAll(rep *repository.Repository) (*[]Category, error) {
var categories []Category
Expand Down
4 changes: 1 addition & 3 deletions model/dto/book.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ func NewRegBookDto() *RegBookDto {

// Create creates a book model from this DTO.
func (b *RegBookDto) Create() *model.Book {
c := model.NewCategory("")
f := model.NewFormat("")
return model.NewBook(b.Title, b.Isbn, c, f)
return model.NewBook(b.Title, b.Isbn, b.CategoryID, b.FormatID)
}

// Validate performs validation check for the each item.
Expand Down
9 changes: 9 additions & 0 deletions model/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ func (f *Format) FindByID(rep *repository.Repository, id uint) (*Format, error)
return &format, nil
}

// CountByID returns a size of formats full matched given format's ID.
func (f *Format) CountByID(rep *repository.Repository, id uint) (int, error) {
var count int
if error := rep.Model(&Format{}).Where("id = ?", id).Count(&count).Error; error != nil {
return 0, error
}
return count, nil
}

// FindAll returns all formats of the format table.
func (f *Format) FindAll(rep *repository.Repository) (*[]Format, error) {
var formats []Format
Expand Down
11 changes: 11 additions & 0 deletions repository/repository.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package repository

import (
"database/sql"
"fmt"

"github.com/jinzhu/gorm"
Expand Down Expand Up @@ -60,6 +61,11 @@ func GetDB() *gorm.DB {
return rep.db
}

// Model specify the model you would like to run db operations
func (rep *Repository) Model(value interface{}) *gorm.DB {
return rep.db.Model(value)
}

// Find find records that match given conditions.
func (rep *Repository) Find(out interface{}, where ...interface{}) *gorm.DB {
return rep.db.Find(out, where...)
Expand Down Expand Up @@ -115,6 +121,11 @@ func (rep *Repository) Scopes(funcs ...func(*gorm.DB) *gorm.DB) *gorm.DB {
return rep.db.Scopes(funcs...)
}

// ScanRows scan `*sql.Rows` to give struct
func (rep *Repository) ScanRows(rows *sql.Rows, result interface{}) error {
return rep.db.ScanRows(rows, result)
}

// Transaction start a transaction as a block.
// If it is failed, will rollback and return error.
// If it is sccuessed, will commit.
Expand Down
28 changes: 17 additions & 11 deletions service/book.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package service

import (
"fmt"

"github.com/ybkuroki/go-webapp-sample/logger"
"github.com/ybkuroki/go-webapp-sample/model"
"github.com/ybkuroki/go-webapp-sample/model/dto"
Expand Down Expand Up @@ -53,16 +55,17 @@ func RegisterBook(dto *dto.RegBookDto) (*model.Book, map[string]string) {

err := rep.Transaction(func(txrep *repository.Repository) error {
var err error
var count int
book := dto.Create()

category := model.Category{}
if book.Category, err = category.FindByID(txrep, dto.CategoryID); err != nil {
return err
if count, err = category.CountByID(txrep, dto.CategoryID); err != nil || count == 0 {
return fmt.Errorf("Not found category entity")
}

format := model.Format{}
if book.Format, err = format.FindByID(txrep, dto.FormatID); err != nil {
return err
if count, err = format.CountByID(txrep, dto.FormatID); err != nil || count == 0 {
return fmt.Errorf("Not found format entity")
}

if result, err = book.Create(txrep); err != nil {
Expand All @@ -73,7 +76,7 @@ func RegisterBook(dto *dto.RegBookDto) (*model.Book, map[string]string) {
})

if err != nil {
logger.GetEchoLogger().Error(err.Error)
logger.GetEchoLogger().Error(err)
return nil, map[string]string{"error": "transaction error"}
}

Expand All @@ -93,6 +96,7 @@ func EditBook(dto *dto.ChgBookDto) (*model.Book, map[string]string) {

err := rep.Transaction(func(txrep *repository.Repository) error {
var err error
var count int
var book *model.Book

b := model.Book{}
Expand All @@ -102,15 +106,17 @@ func EditBook(dto *dto.ChgBookDto) (*model.Book, map[string]string) {

book.Title = dto.Title
book.Isbn = dto.Isbn
book.CategoryID = dto.CategoryID
book.FormatID = dto.FormatID

category := model.Category{}
if book.Category, err = category.FindByID(txrep, dto.CategoryID); err != nil {
return err
if count, err = category.CountByID(txrep, dto.CategoryID); err != nil || count == 0 {
return fmt.Errorf("Not found category entity")
}

format := model.Format{}
if book.Format, err = format.FindByID(txrep, dto.FormatID); err != nil {
return err
if count, err = format.CountByID(txrep, dto.FormatID); err != nil || count == 0 {
return fmt.Errorf("Not found format entity")
}

if result, err = book.Save(txrep); err != nil {
Expand All @@ -121,7 +127,7 @@ func EditBook(dto *dto.ChgBookDto) (*model.Book, map[string]string) {
})

if err != nil {
logger.GetEchoLogger().Error(err.Error)
logger.GetEchoLogger().Error(err)
return nil, map[string]string{"error": "transaction error"}
}

Expand Down Expand Up @@ -156,7 +162,7 @@ func DeleteBook(dto *dto.ChgBookDto) (*model.Book, map[string]string) {
})

if err != nil {
logger.GetEchoLogger().Error(err.Error)
logger.GetEchoLogger().Error(err)
return nil, map[string]string{"error": "transaction error"}
}

Expand Down

0 comments on commit 50c1b64

Please sign in to comment.