Database layer
- Go course
- Advanced
- Lesson 44/68
- 6 min
Write the data access layer with database/sql.
In-memory repository
Writing an in-memory repository first, instead of a database, lets you try the service right away. Because the HTTP server handles requests concurrently, the data is protected with a sync.Mutex:
type memRepo struct {
mu sync.Mutex
urunler []Urun
}
func (r *memRepo) Ekle(u Urun) error {
r.mu.Lock()
defer r.mu.Unlock()
r.urunler = append(r.urunler, u)
return nil
}Moving to a database
When you write a second type implementing the same Repo interface with database/sql, the service code doesn't change:
type sqlRepo struct{ db *sql.DB }
func (r sqlRepo) Ekle(u Urun) error {
_, err := r.db.Exec("INSERT INTO urunler(ad, fiyat) VALUES(?, ?)", u.Ad, u.Fiyat)
return err
}main.go chooses which implementation to use; this is called dependency injection.
Practice Memory repository
Write Urun and memRepo (sync.Mutex + []Urun). The Ekle and Liste methods work under the lock. Add two products and print len(r.Liste()).
Starter code
package main
import (
"fmt"
"sync"
)
func main() {
// buraya yaz
}Sample solution
package main
import (
"fmt"
"sync"
)
type Urun struct {
Ad string
Fiyat float64
}
type memRepo struct {
mu sync.Mutex
urunler []Urun
}
func (r *memRepo) Ekle(u Urun) error {
r.mu.Lock()
defer r.mu.Unlock()
r.urunler = append(r.urunler, u)
return nil
}
func (r *memRepo) Liste() []Urun {
r.mu.Lock()
defer r.mu.Unlock()
return append([]Urun(nil), r.urunler...)
}
func main() {
r := &memRepo{}
r.Ekle(Urun{"Kalem", 10})
r.Ekle(Urun{"Defter", 25})
fmt.Println(len(r.Liste()))
}Expected output
2
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 Why do memRepo methods use a pointer receiver (<code>*memRepo</code>)?
- For JSON
- So the mutex and slice aren't copied and changes stay on the same object Correct answer
- Because it's shorter
- Because interfaces don't support value receivers
Explanation: A value receiver copies on each call; the added product stays in the copy and a copied mutex protects nothing.
2 Release the lock
r.mu.Lock()
____ r.mu.Unlock()
return append([]Urun(nil), r.urunler...)
r.mu.Lock()
defer r.mu.Unlock()
return append([]Urun(nil), r.urunler...)
Explanation: defer Unlock guarantees the lock is released however the function ends.
3 Repository implementations
| Term | Meaning |
|---|---|
memRepo |
In memory, for tests |
sqlRepo |
With database/sql |
Repo |
Common interface |
sync.Mutex |
Guards concurrent access |