阿奴平湖人 發表於 2020-12-8 11:27:00

Go微服务实践之增删改查

<blockquote>
<p>从此篇文章开始,我们来陆续介绍 <code>go-zero</code> 开发一个项目所需要的组件和开发实践。</p>
</blockquote>
<p>首先我们从 <code>model</code> 层开始,来说说<code>go-zero</code> 的API以及封装细节。首先 <code>model</code> 层连接的API集中在<code>core/stores</code>。我们先来看看操作 <code>mysql</code> 这类数据库,API方法我们来到 <code>core/stores/sqlx</code>,所以接下来用几篇的文章总体介绍一下 <code>sqlx</code> 的使用和设计思想。</p>
<h2 id="快速使用">快速使用</h2>
<pre><code class="language-go">func main() {
// 1
const datasource = "user:password@/dbname"
mysqlDB := sqlx.NewMysql(datasource)
// 2
um := model.NewUserModel(mysqlDB,"User")
// 3
ul := logic.NewUserLogic(um)
// 4
engine.AddRoutes(nginxApi(ul))
engine.Start()
}

// NewUserModel,NewUserLogic 类似
func NewUserModel(conn sqlx.SqlConn, table string) *UserModel {
        return &amp;UserModel{conn: conn, table: table}
}

// nginxApi将logic注入到handle,同时绑定路由和handler
func nginxApi(ul *logic.UserLogic) []rest.Route {
        return []rest.Route{
                {
                        Method:http.MethodGet,
                        Path:    "/user/:id", // /user/54er6;
                        Handler: handler.NewUserHandler(ul).GetUserById,
    }
}
</code></pre>
<p>总结一下:</p>
<ol>
<li><code>NewMysql</code> 创建数据库连接</li>
<li>创建相应的 <code>model</code>,并将连接传入「相应的 <code>NewModel</code> 需要开发者编写」</li>
<li><code>model</code> 是为上一层 <code>logic</code> 提供服务</li>
<li>将 <code>logic</code> 注入到 <code>handler</code> 中,同时 <code>handler</code> 与路由绑定,开启 <code>Server</code></li>
</ol>
<p>这样 <code>model-logic-handler </code> 最简单的结构就出来了。然后来看看在 <code>model</code> 层如何进行数据操作:</p>
<pre><code class="language-go">var userBuilderQueryRows = strings.Join(builderx.FieldNames(&amp;User{}), ",")

type User struct {
Avatar string `db:"avatar"` // 头像
UserName string `db:"user_name"` // 姓名
Sex int `db:"sex"` // 1男,2女
MobilePhone string `db:"mobile_phone"` // 手机号
}

func (um *UserModel) Insert(user *User) (int64, error) {
const insertsql = `insert into `+um.table+` (`+userBuilderQueryRows+`) values(?, ?, ?)`
// insert「delete使用方式一致」
res, err := um.conn.Exec(insertsql, user.Avatar, user.UserName, user.Sex, user.MobilePhone)
if err != nil {
    logx.Errorf("insert User Position Model Model err, err=%v", err)
    return -1, err
}
id, err := res.LastInsertId()
        if err != nil {
                logx.Errorf("insert User Model to Idparse id err,err=%v", err)
                return -1, err
        }
        return id, nil
}

func (um *UserModel) FindOne(uid int64) (*User, error) {
var user User
// query
const querysql = `select `+userBuilderQueryRows+` from `+um.table+` where id=? limit 1`
        err := um.conn.QueryRow(&amp;user, querysql, uid)
        if err != nil {
                logx.Errorf("userModile.findOne error ,id=%d,err=%s", uid, err.Error())
                if err == sqlx.ErrNotFound {
                        return nil, ErrNotFound
                }
                return nil, err
        }
        return &amp;user, nil
}
</code></pre>
<ul>
<li><code>insert/update/delete</code>:<code>conn.Exec(insertsql/updatesql/deletesql, args...)</code></li>
<li><code>query</code>:<code>conn.QueryRow(&amp;model, querysql, args...)</code></li>
</ul>
<p>上述就是最简单的 <code>crud</code> 的结构:首先是构建 <code>model</code> ,然后操作 <code>model</code> 进行操作。</p>
<h2 id="代码结构">代码结构</h2>
<table>
<thead>
<tr>
<th>文件名</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td>bulkinserter.go</td>
<td>批量插入</td>
</tr>
<tr>
<td>mysql.go</td>
<td>NewMysql</td>
</tr>
<tr>
<td>orm.go</td>
<td>解析,序列化<code>model</code>的操作</td>
</tr>
<tr>
<td>sqlconn.go</td>
<td>抽象<code>crud</code>操作的接口</td>
</tr>
<tr>
<td>tx.go</td>
<td>事务操作</td>
</tr>
</tbody>
</table>
<p>从 <code>sqlconn.go</code> 的相互接口关系:</p>
<p><img src="https://img2020.cnblogs.com/other/14470/202012/14470-20201208112717320-257248203.png" alt="" loading="lazy"></p>
<p>可以看出:<code>commonSqlConn</code> 和 <code>txSession</code> 是真正实现的地方。先从 <code>API</code> 的功能整体介绍一下:</p>
<table>
<thead>
<tr>
<th>API</th>
<th>参数</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td>Exec(query, args...)</td>
<td>sql, sql参数</td>
<td>insert/update/delete</td>
</tr>
<tr>
<td>Prepare(query)</td>
<td>sql</td>
<td>预编译sql</td>
</tr>
<tr>
<td>QueryRow(&amp;model, query, args...)</td>
<td>model, sql, sql参数</td>
<td>查询一行数据同时赋值给「model」</td>
</tr>
<tr>
<td>QueryRowPartial(&amp;model, query, args...)</td>
<td>model, sql, sql参数</td>
<td>功能同上,但是select sql可以只选取model的部分column「映衬Partial」</td>
</tr>
<tr>
<td>QueryRows/QueryRowsPartial</td>
<td>同上</td>
<td>查询多行API</td>
</tr>
<tr>
<td>Transact(func(session Session) error)</td>
<td>事务操作</td>
<td>将参数中的操作用事务包裹,开发者只需专注参数中的函数编写</td>
</tr>
</tbody>
</table>
<h2 id="总结">总结</h2>
<p><code>go-zero</code> 的 <code>sqlx</code> 屏蔽了go原生的sql操作,开发者只需关注sql编写和业务封装的数据对象,不需要像原生开发中需要手动prepare,赋值数据时Scan。</p>
<p>本节只是简略介绍了接口的相互关系以及开发者平时关注的API,下节将着重分析<strong>go-zero是怎么帮你赋值数据,同时在并发大的情况下,如何不让流量直接把你的数据库打死</strong>。</p>
<h2 id="参考">参考</h2>
<ul>
<li>go-zero sqlx</li>
<li>Go database/sql tutorial</li>
</ul>
<p>欢迎大家使用 <code>go-zero</code></p>
<p>项目地址:https://github.com/tal-tech/go-zero</p>
<p>如果觉得文章不错,欢迎 github 点个 star 🤝</p>
<blockquote>
<p>项目地址:<br>
https://github.com/tal-tech/go-zero</p>
</blockquote><br><br>
来源:https://www.cnblogs.com/kevinwan/p/14101691.html
頁: [1]
查看完整版本: Go微服务实践之增删改查