最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Go語(yǔ)言如何對(duì)數(shù)據(jù)庫(kù)進(jìn)行CRUD操作

 更新時(shí)間:2023年05月21日 15:50:49   作者:LightSaid  
在這篇文章中,主要帶大家來(lái)學(xué)習(xí)一下在Go語(yǔ)言中如何對(duì)數(shù)據(jù)庫(kù)進(jìn)行CRUD操作,從而探討一下Go的接口編程,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

Go語(yǔ)言中的接口

在這里我將不會(huì)介紹接口基礎(chǔ)知識(shí),如:接口定義和實(shí)現(xiàn)、空接口、類型斷言、v, ok := i.(T)、switch x.(type)、接口嵌套、指針接收器與值接收器實(shí)現(xiàn)接口、接口零值 這些基礎(chǔ)概念不作多說(shuō),如不太清楚上面接口知識(shí),自行學(xué)習(xí)補(bǔ)充。這里介紹在開發(fā)中接口實(shí)在用法,也是由上面基礎(chǔ)演變而成。

使用接口編程

有這么一個(gè)普通場(chǎng)景,上傳并保存文件,三七二十一蹭蹭蹭,一下子搞完,so easy,代碼如下:

項(xiàng)目準(zhǔn)備上線,公司購(gòu)買了OSS存儲(chǔ),文件資源要存到OSS上,那么如何修改上面的代碼呢? 這個(gè)時(shí)候接口就可以上場(chǎng)了。 實(shí)現(xiàn)如下:

// NOTE: storage.go
// 存儲(chǔ)文件接口
type Storage interface {
    Save(data []byte) (string, error)
}
// 開發(fā)時(shí)候使用本地存儲(chǔ)
type localStorage struct {
    config *struct{ base string }
}
func NewLocalStorage(cfg struct{ base string }) *localStorage {
    return &localStorage{config: &cfg}
}
func (store localStorage) Save(data []byte) (string, error) {
    return "http://127.0.0.1/" + store.config.base + "/a.png", nil
}
// 生產(chǎn)使用oss存儲(chǔ)
type ossStorage struct{}
func NewOSSStorage() *ossStorage {
    return &ossStorage{}
}
func (store ossStorage) Save(data []byte) (string, error) {
    return "https://abc.com/oss/a.png", nil
}
// NOTE: upload.go
// 保存文件
func saveFile(store Storage) {
    var data []byte // 偽代碼
    url, _ := store.Save(data)
    fmt.Println(url)
}
func main() {
    var store Storage
    if os.Getenv("ENV") == "prod" {
        store = NewOSSStorage()
    } else {
        store = NewLocalStorage(struct{ base string }{base: "/static"})
	}
    saveFile(store)
}

上面定義Storage接口,localStorage、ossStorage 都是實(shí)現(xiàn)了接口,而 saveFile(store Storage) 接受指針。 這種方式就是接受接口返回結(jié)構(gòu),是 Go 語(yǔ)言接口編程中非常經(jīng)典實(shí)用模式。

接受接口返回結(jié)構(gòu)

上面已經(jīng)的例子就是使用這個(gè)法則,使用這個(gè)法則讓代碼變得更加靈活,它是松耦合的,更加方便mock數(shù)據(jù)測(cè)試,可以避免一些不必要的bug。

現(xiàn)在舉一個(gè)不方便測(cè)試的例子:

type Repository struct {
    DB *sql.DB
}
func NewRepository(db *sql.DB) *Repository {
    return &Repository{DB: db}
}
func (repo *Repository) Insert() {
    // do some thing
}
func (repo *Repository) Update() {
    // do some thing
}
type Service struct {
    Repo Repository
}
func NewService(db *sql.DB) *Service {
    return &Service{
        Repo: *NewRepository(db),
    }
}
func main() {
    // 假設(shè)是真實(shí)的鏈接 &sql.DB{}
    srv := NewService(&sql.DB{})
    srv.Repo.Insert()
    srv.Repo.Update()
}

上面的例子看著無(wú)任何問(wèn)題啊?這不很正常。是的很正常,就是測(cè)試的時(shí)候不方便。 如果要測(cè)試起來(lái),你必須要?jiǎng)?chuàng)建一個(gè)數(shù)據(jù)庫(kù),連接數(shù)據(jù)庫(kù),創(chuàng)建sql.DB實(shí)例。才能完成測(cè)試。如果想簡(jiǎn)單mock數(shù)據(jù),就不好辦了。

因此還是建議,使用接口編程。修改如下:

type Repository interface {
	Insert()
	Update()
}

type repository struct {
	DB *sql.DB
}

func NewRepository(db *sql.DB) *repository {
	return &repository{DB: db}
}

func (repo *repository) Insert() {
	// do some thing
}

func (repo *repository) Update() {
	// do some thing
}

type Service struct {
	Repo Repository
}

func NewService(r Repository) *Service {
	return &Service{
		Repo: r,
	}
}

func main() {
	// 假設(shè)是真實(shí)的鏈接 &sql.DB{}
	r := NewRepository(&sql.DB{})
	srv := NewService(r)
	srv.Repo.Insert()
	srv.Repo.Update()
}

接口檢查

上面的例子,func NewRepository(db *sql.DB) *repository 返回的是結(jié)構(gòu)體指針。在編碼過(guò)程,很容易忘記實(shí)現(xiàn)接口,甚至全部接口都沒(méi)實(shí)現(xiàn),編譯器也不會(huì)報(bào)錯(cuò)。我怎么知道repository結(jié)構(gòu)體就一定全部實(shí)現(xiàn)了Repository接口呢,對(duì)吧。

那這個(gè)時(shí)候就要用到接口檢查了。 接口檢查的語(yǔ)法是

var _ MyInterface = (*MyStruct)(nil)

上面當(dāng)我沒(méi)有實(shí)現(xiàn)Inser方法時(shí)就會(huì)直接編譯不通過(guò)。

這就是接口檢查帶來(lái)好處。

擴(kuò)展接口

假設(shè)有這么一場(chǎng)景,在開發(fā)中我們使用到了第三方庫(kù),第三庫(kù)提供一個(gè)Worker接口,我們?cè)?code>work、next兩個(gè)方法都使用到Worker接口,入?yún)⒕褪撬?。這個(gè)時(shí)候我們希望在work函數(shù)中對(duì)Context增加額外的屬性,提供給next函數(shù)使用。但現(xiàn)實(shí)是沒(méi)法直接通過(guò)context.WithValue(w.Context(), "greet", "Hello")設(shè)置值的,因?yàn)?code>Worker接口僅返回Context并重新沒(méi)有設(shè)置Context方法提供;由于Worker是第三方的接口,不可直接去改的。因此就需要擴(kuò)展接口。問(wèn)題如圖:

如何解決這個(gè)問(wèn)題呢?現(xiàn)在要解決什么問(wèn)題?如果把問(wèn)題細(xì)分的話,要解決2個(gè)問(wèn)題:

  • 擴(kuò)展一個(gè)設(shè)置Context的方法
  • 保留原來(lái)的方法,只做擴(kuò)充,不做刪除 那要解決這個(gè)問(wèn)題最好的方式就是接口嵌套。

代碼該如何寫呢?解法如下:

首先是要定義新的接口wrapWorker,將舊的接口嵌套進(jìn)來(lái),這樣就保留了原始有的方法,然后在定義新的方法SetContext(context.Context),接口定義好了,就用實(shí)現(xiàn)接口對(duì)吧,思路很直接。

緊接著定義一個(gè) wrap 結(jié)構(gòu)體, 保存ctx,同時(shí)實(shí)SetContext(context.Context)

還有重要的一環(huán)節(jié),就是重寫 Context() context.Context 方法。

實(shí)現(xiàn)代碼如下:

// 第三方的接口定義
type Worker interface {
    Context() context.Context
    DoSomeThing()
    // ...
}

type wrapWorker interface {
    Worker
    SetContext(context.Context)
}
type wrap struct {
    Worker
    ctx context.Context
}

func newWrap(w Worker) wrapWorker {
    return &wrap{
        w,
        w.Context(),
    }
}
func (wp *wrap) SetContext(ctx context.Context) {
    wp.ctx = ctx
}

func (wp wrap) Context() context.Context {
    return wp.ctx
}

type contextKey string

func work(w Worker) {
    wp := newWrap(w)
    ctx := context.WithValue(w.Context(), contextKey("greet"), "Hello")
    wp.SetContext(ctx)

    next(wp)
}

func next(w Worker) {
    v := w.Context().Value(contextKey("greet"))
    fmt.Printf("-> %v \n", v)
    w.DoSomeThing()
}

type person string

func (person) Context() context.Context {
    return context.Background()
}
func (p person) DoSomeThing() {
    fmt.Println(string(p), "吃飯睡覺(jué)打代碼~")
}

func main() {
    var p person = "張三"
    work(p)
}

執(zhí)行結(jié)果符合預(yù)期,OK 沒(méi)問(wèn)題~

其實(shí)上面不定義新接口wrapWorker也是可以的,用wrap包裹接口即可。用接口設(shè)計(jì)更符合接口編程思想。

到此這篇關(guān)于詳解Go語(yǔ)言如何對(duì)數(shù)據(jù)庫(kù)進(jìn)行CRUD操作的文章就介紹到這了,更多相關(guān)Go語(yǔ)言數(shù)據(jù)庫(kù)CRUD操作內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go?gRPC服務(wù)端流式RPC教程示例

    Go?gRPC服務(wù)端流式RPC教程示例

    這篇文章主要為大家介紹了Go?gRPC服務(wù)端流式RPC教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • go 代碼的調(diào)試---打印調(diào)用堆棧的實(shí)例

    go 代碼的調(diào)試---打印調(diào)用堆棧的實(shí)例

    下面小編就為大家?guī)?lái)一篇go 代碼的調(diào)試---打印調(diào)用堆棧的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-10-10
  • golang如何操作csv文件詳解

    golang如何操作csv文件詳解

    這篇文章主要給大家介紹了關(guān)于golang如何操作csv文件的相關(guān)資料,以及使用Golang導(dǎo)出CSV數(shù)據(jù)并解決數(shù)據(jù)亂碼問(wèn)題的解決辦法,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-02-02
  • Golang利用casbin實(shí)現(xiàn)權(quán)限驗(yàn)證詳解

    Golang利用casbin實(shí)現(xiàn)權(quán)限驗(yàn)證詳解

    Casbin是一個(gè)強(qiáng)大的、高效的開源訪問(wèn)控制框架,其權(quán)限管理機(jī)制支持多種訪問(wèn)控制模型,Casbin只負(fù)責(zé)訪問(wèn)控制。本文將利用casbin實(shí)現(xiàn)權(quán)限驗(yàn)證功能,需要的可以參考一下
    2023-02-02
  • Go1.18新特性使用Generics泛型進(jìn)行流式處理

    Go1.18新特性使用Generics泛型進(jìn)行流式處理

    這篇文章主要為大家介紹了Go1.18新特性使用Generics泛型進(jìn)行流式處理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • 使用go操作redis的有序集合(zset)

    使用go操作redis的有序集合(zset)

    這篇文章主要介紹了使用go操作redis的有序集合(zset),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • 在Gin框架中解決跨域問(wèn)題的多種方法

    在Gin框架中解決跨域問(wèn)題的多種方法

    在使用Go語(yǔ)言進(jìn)行Web開發(fā)時(shí),Gin框架因其簡(jiǎn)潔、高效的特點(diǎn)而被廣泛使用,然而,在實(shí)際開發(fā)中,跨域問(wèn)題(CORS, Cross-Origin Resource Sharing)是一個(gè)常見(jiàn)的挑戰(zhàn),本文將結(jié)合實(shí)際案例,詳細(xì)介紹在Gin框架中解決跨域問(wèn)題的多種方法,需要的朋友可以參考下
    2024-10-10
  • Go語(yǔ)言實(shí)現(xiàn)的簡(jiǎn)單網(wǎng)絡(luò)端口掃描方法

    Go語(yǔ)言實(shí)現(xiàn)的簡(jiǎn)單網(wǎng)絡(luò)端口掃描方法

    這篇文章主要介紹了Go語(yǔ)言實(shí)現(xiàn)的簡(jiǎn)單網(wǎng)絡(luò)端口掃描方法,實(shí)例分析了Go語(yǔ)言網(wǎng)絡(luò)程序的實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • golang類型斷言的實(shí)現(xiàn)示例

    golang類型斷言的實(shí)現(xiàn)示例

    在Go語(yǔ)言中,類型斷言用于從接口類型獲取其具體類型的值,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-10-10
  • 一文探索Go語(yǔ)言中的內(nèi)存對(duì)齊

    一文探索Go語(yǔ)言中的內(nèi)存對(duì)齊

    在 Go 語(yǔ)言中,內(nèi)存對(duì)齊是一個(gè)經(jīng)常被忽略但非常重要的概念,本文將通過(guò)一個(gè)簡(jiǎn)單的例子來(lái)探討 Go 語(yǔ)言中的內(nèi)存對(duì)齊機(jī)制,感興趣的可以了解下
    2024-11-11

最新評(píng)論

临颍县| 临高县| 仲巴县| 高邑县| 翁牛特旗| 高雄市| 普洱| 高雄市| 大宁县| 东莞市| 同心县| 阳新县| 腾冲县| 太原市| 灵璧县| 安仁县| 永仁县| 望谟县| 同德县| 武胜县| 历史| 金平| 苍梧县| 雅江县| 义乌市| 高雄市| 邢台市| 遵义市| 彭泽县| 齐河县| 永登县| 金华市| 赤水市| 德兴市| 九寨沟县| 桓台县| 辛集市| 郸城县| 桐庐县| 绥化市| 新丰县|