golang orm_用golang寻找到数据库层的orm

golang orm

Anyone who’s starting to work with Golang can note some lower productivity comparing with another language when building the layer of the database. A lot of repetead SQL code that makes us waste time worrying with things that do not add value to what we want to deliver.

在构建数据库层时,任何开始使用Golang的人都会注意到与另一种语言相比生产力有所降低。 许多重复SQL代码使我们浪费时间担心那些无法为我们交付的产品增加价值的事物。

Looking for a library that can help us to give productivity on the database layer, some libraries can be find and the first I explore is the REL library:

寻找一个可以帮助我们提高数据库层生产力的库,可以找到一些库,而我首先探索的是REL库:

To see how this lib works on the field, I built a little example project of a book library, that has CRUD operations and you will see how do insert, update, delete, do queries, and how to handle transactions on the database in a simple way.

为了查看该库如何在现场工作,我构建了一个带有CRUD操作的小书库示例项目,您将看到如何在数据库中插入,更新,删除,执行查询以及如何处理数据库中的事务。简单的方法。

The book library API has four operations:

图书库API有四个操作:

  • POST /books (Insert a new book on the library)

    POST / books (在图书馆插入新书)

  • GET /books/{bookID} (Get a book by your ID on the library)

    GET / books / {bookID} (通过您在图书馆的ID获得一本书)

  • PUT /books/{bookID} (Update a book on the library)

    PUT / books / {bookID} (在图书馆中更新一本书)

  • GET /books (Get all books on the library)

    GET / books (获取图书馆中的所有书籍)

To do the routing of the application I choose chi and as you can see the main function became something like that:

为了进行应用程序的路由,我选择chi ,并且您可以看到主要功能变成了类似的样子:

Here I map the routes with your handlers but I create the repo of REL libs too, as you can see at the part of code below:

在这里,我用您的处理程序映射了路由,但是我也创建了REL库的仓库,如下面的代码所示:

adapter, err := mysql.Open(dsn) 
if err != nil {
panic(err)
}
defer adapter.Close() // initialize rel's
repo := rel.New(adapter)

With this, I have an instance of REL repo with the connection to MySQL, and this repo provides all functions that I need to communicate with the database. So I reuse the REL repo in the Repository Layer, as you can see below:

有了这个,我有了一个与MySQL连接的REL回购实例,这个回购提供了我与数据库通信所需的所有功能。 因此,我在存储库层中重用了REL repo,如下所示:

type BooksRepository struct { 
repo rel.Repository
} func New(repo rel.Repository) BooksRepository {
return BooksRepository{
repo: repo,
}
}

To start using the repo functions I had to map the entities as below:

要开始使用回购功能,我必须按如下所示映射实体:

By default, REL will use the name of each field of the struct as the name of the column, and the name of the struct as name of the table on plural.

默认情况下,REL将使用该结构的每个字段的名称作为列的名称,并使用该结构的名称作为复数形式的表的名称。

To customize the column name I use the tag db:"shelf_id" saying to the REL what name of this column has to be. And to customize the table name, I implement the function Table() with the struct as the receiver that I want to specify the name of table returning the name what I want.

为了自定义列名,我使用标签db:"shelf_id"向REL表示该列的名称。 为了自定义表名,我以struct作为接收器实现了函数Table() ,我想指定表名并返回想要的名称。

After this I implement the first operation of the repository to insert a new book and wheres shelf this book belongs, to do that I needed two functions: one to find the shelf where the book will belong to validate your capacity and another function to insert the book and increment the amount of the shelf:

之后,我执行存储库的第一个操作以插入一本新书以及该书所属的书架,为此,我需要两个功能:一个用于查找书所属的书架以验证您的能力,另一个功能用于插入书本。预定并增加货架数量:

Looking at each part of this code:

查看这段代码的每个部分:

r.repo.Find(ctx, &shelf, where.Eq("id", shelfID))

Here I’m doing a select from table shelf and filtering by your ID. REL is capable of figuring out which tables to query only by using the struct passed in the second parameter, and allows the possibility to use some other parameters to do a filter and another SQL statements, here I’m using the where to fetch the only ID with the value on variable shelfID. You can see a lot of other options on the REL documentation.

在这里,我从桌架上进行选择,并按您的ID进行过滤。 REL能够仅通过使用第二个参数中传递的结构来确定要查询的表,并允许使用其他一些参数来进行过滤和其他SQL语句的可能性,这里我使用的是where获取唯一数据ID的值包含在变量shelfID 。 您可以在REL文档中看到许多其他选项。

r.repo.Preload(ctx, &shelf, "books")

Now to fetch all books on the shelf, I call the function Preload that will load all the books inside of shelf struct using the mapping that was done before.

现在要获取书架上的所有书,我调用函数Preload,该函数将使用之前完成的映射将所有书加载到书架结构中。

The REL will execute the query and print the SQL generated:

REL将执行查询并打印生成SQL:

SELECT * FROM `shelf` WHERE `id`=? LIMIT 1;
SELECT * FROM `book` WHERE `shelf_id` IN (?);

To insert a book I need a transaction to insert and update the shelf table:

要插入一本书,我需要一笔交易来插入和更新书架表:

r.repo.Transaction(ctx, func(ctx context.Context) error {
book.ShelfID = book.BookShelf.ID
r.repo.MustInsert(ctx, &book, rel.Cascade(false))
return r.repo.Update(ctx, &book.BookShelf, rel.Inc("amount"), rel.Reload(false))
})

And to do that I’m using the Transaction function that expects a callback function that will be executed inside the transaction, being responsible for handle the errors. To perform the rollback I use the MustInsert function, responsible for insert a book. Moreover using the Update function to update the shelf table only incrementing the amount field and saying to not reload the variable after the update. With this, the SQL executed will be:

为此,我正在使用Transaction函数,该函数期望将在事务内部执行的回调函数负责处理错误。 为了执行回滚,我使用MustInsert函数,该函数负责插入一本书。 此外,使用Update功能更新货架表仅增加了amount字段,并说在更新后不重新加载变量。 这样,执行SQL将是:

begin transaction
INSERT INTO `book` (`title`,`description`,`author`,`edition`,`shelf_id`,`created_at`,`updated_at`) VALUES (?,?,?,?,?,?,?);
UPDATE `shelf` SET `amount`=`amount`+? WHERE `id`=?;
commit transaction

The REL lib provides a way to do tests by mocking the connection with the database and validating if the functions will work as expected, to test these functions was created the following tests:

REL库提供了一种通过模拟与数据库的连接并验证功能是否可以按预期工作来进行测试的方法,以测试这些功能是通过以下测试创建的:

After inserting the books was built the operation to find a book by ID:

插入书籍后,便建立了通过ID查找书籍的操作:

To find a book by ID I did something like I already did when finding a shelf by ID using the Find and Preload functions. And also create a test to validate if all will be working as expected.

为了按ID查找书,我做了一些与使用FindPreload函数按ID Find书架时已经做过的事情。 并创建一个测试以验证所有功能是否都能按预期工作。

In order to update a book I had to implement the function UpdateBook on the repository and to do that I only had to use the Update function of REL repo, specifying each field needed to update and after create a test after to validate if all works as expected, as below:

为了更新一本书,我必须在存储库上实现功能UpdateBook ,并且要做到这一点,我只需要使用REL repo的Update函数,指定需要更新的每个字段,并在创建测试后验证是否所有功能都可以预期如下:

To finish the CRUD of books on a library, I added a function to find all existent books on the library implementing the function FindAllBooks that use two functions of REL repo, the FindAll function to fetch all books and the Preload function to fetch the shelf of each returned book, and of course, was built a test to validate:

为了完成图书馆图书的FindAllBooks ,我添加了一个功能来查找图书馆中所有现有的图书,以实现使用REL repo的两个功能的FindAllBooks函数,即FindAll函数用于获取所有图书,而Preload函数用于获取书架。每本归还的书,当然都经过了测试以验证:

With this, the database layer was built using the REL lib without any SQL complexity and you can find this project on my Github:

这样,数据库层是使用REL lib构建的,而没有任何SQL复杂性,您可以在我的Github上找到此项目:

Please leave a comment or suggestion and let me know what you think about REL. If you are familiar with another library, also leave a comment so we can compare them.

请发表评论或建议,让我知道您对REL的看法。 如果您熟悉另一个库,也请发表评论,以便我们进行比较。

Image for post

To continue looking for a good ORM it’s good to explore at least three options, so I opt beyond REL the libraries SQLX and GORM. So the next library that I will explore it’s the most popular: GORM.

要继续寻找一个好的ORM,最好探索至少三个选项,因此我选择使用REL库之外的SQLXGORM 。 因此,下一个我将要探索的库是最受欢迎的库: GORM

翻译自: https://medium.com/@rafaelholanda90/looking-for-an-orm-to-database-layer-with-golang-92e360d3a920

golang orm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值