Go sqlx库
<p>在项目中我们通常可能会使用<code>database/sql</code>连接MySQL数据库。本文借助使用<code>sqlx</code>实现批量插入数据的例子,介绍了<code>sqlx</code>中可能被你忽视了的<code>sqlx.In</code>和<code>DB.NamedExec</code>方法。</p><h2 id="autoid-0-0-0">sqlx介绍</h2>
<p>在项目中我们通常可能会使用<code>database/sql</code>连接MySQL数据库。<code>sqlx</code>可以认为是Go语言内置<code>database/sql</code>的超集,它在优秀的内置<code>database/sql</code>基础上提供了一组扩展。这些扩展中除了大家常用来查询的<code>Get(dest interface{}, ...) error</code>和<code>Select(dest interface{}, ...) error</code>外还有很多其他强大的功能。</p>
<h2 id="autoid-0-1-0">安装sqlx</h2>
<pre class="line-numbers language-go"><code class=" language-go"><span class="token keyword keyword-go">go get github<span class="token punctuation">.com<span class="token operator">/jmoiron<span class="token operator">/sqlx
</span></span></span></span></code></pre>
<h2 id="autoid-0-2-0">基本使用</h2>
<h3 id="autoid-0-3-0">连接数据库</h3>
<pre class="line-numbers language-go"><code class=" language-go"><span class="token keyword keyword-var">var db <span class="token operator">*sqlx<span class="token punctuation">.DB
<span class="token keyword keyword-func">func <span class="token function">initDB<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">(err <span class="token builtin">error<span class="token punctuation">) <span class="token punctuation">{
dsn <span class="token operator">:= <span class="token string">"user:password@tcp(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=True"
<span class="token comment">// 也可以使用MustConnect连接不成功就panic
db<span class="token punctuation">, err <span class="token operator">= sqlx<span class="token punctuation">.<span class="token function">Connect<span class="token punctuation">(<span class="token string">"mysql"<span class="token punctuation">, dsn<span class="token punctuation">)
<span class="token keyword keyword-if">if err <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"connect DB failed, err:%v\n"<span class="token punctuation">, err<span class="token punctuation">)
<span class="token keyword keyword-return">return
<span class="token punctuation">}
db<span class="token punctuation">.<span class="token function">SetMaxOpenConns<span class="token punctuation">(<span class="token number">20<span class="token punctuation">)
db<span class="token punctuation">.<span class="token function">SetMaxIdleConns<span class="token punctuation">(<span class="token number">10<span class="token punctuation">)
<span class="token keyword keyword-return">return
<span class="token punctuation">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h3 id="autoid-0-3-1">查询</h3>
<p>查询单行数据示例代码如下:</p>
<pre class="line-numbers language-go"><code class=" language-go"><span class="token comment">// 查询单条数据示例
<span class="token keyword keyword-func">func <span class="token function">queryRowDemo<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
sqlStr <span class="token operator">:= <span class="token string">"select id, name, age from user where id=?"
<span class="token keyword keyword-var">var u user
err <span class="token operator">:= db<span class="token punctuation">.<span class="token function">Get<span class="token punctuation">(<span class="token operator">&u<span class="token punctuation">, sqlStr<span class="token punctuation">, <span class="token number">1<span class="token punctuation">)
<span class="token keyword keyword-if">if err <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"get failed, err:%v\n"<span class="token punctuation">, err<span class="token punctuation">)
<span class="token keyword keyword-return">return
<span class="token punctuation">}
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"id:%d name:%s age:%d\n"<span class="token punctuation">, u<span class="token punctuation">.ID<span class="token punctuation">, u<span class="token punctuation">.Name<span class="token punctuation">, u<span class="token punctuation">.Age<span class="token punctuation">)
<span class="token punctuation">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>查询多行数据示例代码如下:</p>
<pre class="line-numbers language-go"><code class=" language-go"><span class="token comment">// 查询多条数据示例
<span class="token keyword keyword-func">func <span class="token function">queryMultiRowDemo<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
sqlStr <span class="token operator">:= <span class="token string">"select id, name, age from user where id > ?"
<span class="token keyword keyword-var">var users <span class="token punctuation">[<span class="token punctuation">]user
err <span class="token operator">:= db<span class="token punctuation">.<span class="token function">Select<span class="token punctuation">(<span class="token operator">&users<span class="token punctuation">, sqlStr<span class="token punctuation">, <span class="token number">0<span class="token punctuation">)
<span class="token keyword keyword-if">if err <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"query failed, err:%v\n"<span class="token punctuation">, err<span class="token punctuation">)
<span class="token keyword keyword-return">return
<span class="token punctuation">}
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"users:%#v\n"<span class="token punctuation">, users<span class="token punctuation">)
<span class="token punctuation">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h3 id="autoid-0-3-2">插入、更新和删除</h3>
<p>sqlx中的exec方法与原生sql中的exec使用基本一致:</p>
<pre class="line-numbers language-go"><code class=" language-go"><span class="token comment">// 插入数据
<span class="token keyword keyword-func">func <span class="token function">insertRowDemo<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
sqlStr <span class="token operator">:= <span class="token string">"insert into user(name, age) values (?,?)"
ret<span class="token punctuation">, err <span class="token operator">:= db<span class="token punctuation">.<span class="token function">Exec<span class="token punctuation">(sqlStr<span class="token punctuation">, <span class="token string">"沙河小王子"<span class="token punctuation">, <span class="token number">19<span class="token punctuation">)
<span class="token keyword keyword-if">if err <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"insert failed, err:%v\n"<span class="token punctuation">, err<span class="token punctuation">)
<span class="token keyword keyword-return">return
<span class="token punctuation">}
theID<span class="token punctuation">, err <span class="token operator">:= ret<span class="token punctuation">.<span class="token function">LastInsertId<span class="token punctuation">(<span class="token punctuation">) <span class="token comment">// 新插入数据的id
<span class="token keyword keyword-if">if err <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"get lastinsert ID failed, err:%v\n"<span class="token punctuation">, err<span class="token punctuation">)
<span class="token keyword keyword-return">return
<span class="token punctuation">}
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"insert success, the id is %d.\n"<span class="token punctuation">, theID<span class="token punctuation">)
<span class="token punctuation">}
<span class="token comment">// 更新数据
<span class="token keyword keyword-func">func <span class="token function">updateRowDemo<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
sqlStr <span class="token operator">:= <span class="token string">"update user set age=? where id = ?"
ret<span class="token punctuation">, err <span class="token operator">:= db<span class="token punctuation">.<span class="token function">Exec<span class="token punctuation">(sqlStr<span class="token punctuation">, <span class="token number">39<span class="token punctuation">, <span class="token number">6<span class="token punctuation">)
<span class="token keyword keyword-if">if err <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"update failed, err:%v\n"<span class="token punctuation">, err<span class="token punctuation">)
<span class="token keyword keyword-return">return
<span class="token punctuation">}
n<span class="token punctuation">, err <span class="token operator">:= ret<span class="token punctuation">.<span class="token function">RowsAffected<span class="token punctuation">(<span class="token punctuation">) <span class="token comment">// 操作影响的行数
<span class="token keyword keyword-if">if err <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"get RowsAffected failed, err:%v\n"<span class="token punctuation">, err<span class="token punctuation">)
<span class="token keyword keyword-return">return
<span class="token punctuation">}
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"update success, affected rows:%d\n"<span class="token punctuation">, n<span class="token punctuation">)
<span class="token punctuation">}
<span class="token comment">// 删除数据
<span class="token keyword keyword-func">func <span class="token function">deleteRowDemo<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
sqlStr <span class="token operator">:= <span class="token string">"delete from user where id = ?"
ret<span class="token punctuation">, err <span class="token operator">:= db<span class="token punctuation">.<span class="token function">Exec<span class="token punctuation">(sqlStr<span class="token punctuation">, <span class="token number">6<span class="token punctuation">)
<span class="token keyword keyword-if">if err <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"delete failed, err:%v\n"<span class="token punctuation">, err<span class="token punctuation">)
<span class="token keyword keyword-return">return
<span class="token punctuation">}
n<span class="token punctuation">, err <span class="token operator">:= ret<span class="token punctuation">.<span class="token function">RowsAffected<span class="token punctuation">(<span class="token punctuation">) <span class="token comment">// 操作影响的行数
<span class="token keyword keyword-if">if err <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"get RowsAffected failed, err:%v\n"<span class="token punctuation">, err<span class="token punctuation">)
<span class="token keyword keyword-return">return
<span class="token punctuation">}
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"delete success, affected rows:%d\n"<span class="token punctuation">, n<span class="token punctuation">)
<span class="token punctuation">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h3 id="autoid-0-3-3">NamedExec</h3>
<p><code>DB.NamedExec</code>方法用来绑定SQL语句与结构体或map中的同名字段。</p>
<pre class="line-numbers language-go"><code class=" language-go"><span class="token keyword keyword-func">func <span class="token function">insertUserDemo<span class="token punctuation">(<span class="token punctuation">)<span class="token punctuation">(err <span class="token builtin">error<span class="token punctuation">)<span class="token punctuation">{
sqlStr <span class="token operator">:= <span class="token string">"INSERT INTO user (name,age) VALUES (:name,:age)"
<span class="token boolean">_<span class="token punctuation">, err <span class="token operator">= db<span class="token punctuation">.<span class="token function">NamedExec<span class="token punctuation">(sqlStr<span class="token punctuation">,
<span class="token keyword keyword-map">map<span class="token punctuation">[<span class="token builtin">string<span class="token punctuation">]<span class="token keyword keyword-interface">interface<span class="token punctuation">{<span class="token punctuation">}<span class="token punctuation">{
<span class="token string">"name"<span class="token punctuation">: <span class="token string">"七米"<span class="token punctuation">,
<span class="token string">"age"<span class="token punctuation">: <span class="token number">28<span class="token punctuation">,
<span class="token punctuation">}<span class="token punctuation">)
<span class="token keyword keyword-return">return
<span class="token punctuation">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h3 id="autoid-0-3-4">NamedQuery</h3>
<p>与<code>DB.NamedExec</code>同理,这里是支持查询。</p>
<pre class="line-numbers language-go"><code class=" language-go"><span class="token keyword keyword-func">func <span class="token function">namedQuery<span class="token punctuation">(<span class="token punctuation">)<span class="token punctuation">{
sqlStr <span class="token operator">:= <span class="token string">"SELECT * FROM user WHERE name=:name"
<span class="token comment">// 使用map做命名查询
rows<span class="token punctuation">, err <span class="token operator">:= db<span class="token punctuation">.<span class="token function">NamedQuery<span class="token punctuation">(sqlStr<span class="token punctuation">, <span class="token keyword keyword-map">map<span class="token punctuation">[<span class="token builtin">string<span class="token punctuation">]<span class="token keyword keyword-interface">interface<span class="token punctuation">{<span class="token punctuation">}<span class="token punctuation">{<span class="token string">"name"<span class="token punctuation">: <span class="token string">"七米"<span class="token punctuation">}<span class="token punctuation">)
<span class="token keyword keyword-if">if err <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"db.NamedQuery failed, err:%v\n"<span class="token punctuation">, err<span class="token punctuation">)
<span class="token keyword keyword-return">return
<span class="token punctuation">}
<span class="token keyword keyword-defer">defer rows<span class="token punctuation">.<span class="token function">Close<span class="token punctuation">(<span class="token punctuation">)
<span class="token keyword keyword-for">for rows<span class="token punctuation">.<span class="token function">Next<span class="token punctuation">(<span class="token punctuation">)<span class="token punctuation">{
<span class="token keyword keyword-var">var u user
err <span class="token operator">:= rows<span class="token punctuation">.<span class="token function">StructScan<span class="token punctuation">(<span class="token operator">&u<span class="token punctuation">)
<span class="token keyword keyword-if">if err <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"scan failed, err:%v\n"<span class="token punctuation">, err<span class="token punctuation">)
<span class="token keyword keyword-continue">continue
<span class="token punctuation">}
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"user:%#v\n"<span class="token punctuation">, u<span class="token punctuation">)
<span class="token punctuation">}
u <span class="token operator">:= user<span class="token punctuation">{
Name<span class="token punctuation">: <span class="token string">"七米"<span class="token punctuation">,
<span class="token punctuation">}
<span class="token comment">// 使用结构体命名查询,根据结构体字段的 db tag进行映射
rows<span class="token punctuation">, err <span class="token operator">= db<span class="token punctuation">.<span class="token function">NamedQuery<span class="token punctuation">(sqlStr<span class="token punctuation">, u<span class="token punctuation">)
<span class="token keyword keyword-if">if err <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"db.NamedQuery failed, err:%v\n"<span class="token punctuation">, err<span class="token punctuation">)
<span class="token keyword keyword-return">return
<span class="token punctuation">}
<span class="token keyword keyword-defer">defer rows<span class="token punctuation">.<span class="token function">Close<span class="token punctuation">(<span class="token punctuation">)
<span class="token keyword keyword-for">for rows<span class="token punctuation">.<span class="token function">Next<span class="token punctuation">(<span class="token punctuation">)<span class="token punctuation">{
<span class="token keyword keyword-var">var u user
err <span class="token operator">:= rows<span class="token punctuation">.<span class="token function">StructScan<span class="token punctuation">(<span class="token operator">&u<span class="token punctuation">)
<span class="token keyword keyword-if">if err <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"scan failed, err:%v\n"<span class="token punctuation">, err<span class="token punctuation">)
<span class="token keyword keyword-continue">continue
<span class="token punctuation">}
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"user:%#v\n"<span class="token punctuation">, u<span class="token punctuation">)
<span class="token punctuation">}
<span class="token punctuation">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h3 id="autoid-0-3-5">事务操作</h3>
<p>对于事务操作,我们可以使用<code>sqlx</code>中提供的<code>db.Beginx()</code>和<code>tx.Exec()</code>方法。示例代码如下:</p>
<pre class="line-numbers language-go"><code class=" language-go"><span class="token keyword keyword-func">func <span class="token function">transactionDemo2<span class="token punctuation">(<span class="token punctuation">)<span class="token punctuation">(err <span class="token builtin">error<span class="token punctuation">) <span class="token punctuation">{
tx<span class="token punctuation">, err <span class="token operator">:= db<span class="token punctuation">.<span class="token function">Beginx<span class="token punctuation">(<span class="token punctuation">) <span class="token comment">// 开启事务
<span class="token keyword keyword-if">if err <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"begin trans failed, err:%v\n"<span class="token punctuation">, err<span class="token punctuation">)
<span class="token keyword keyword-return">return err
<span class="token punctuation">}
<span class="token keyword keyword-defer">defer <span class="token keyword keyword-func">func<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword keyword-if">if p <span class="token operator">:= <span class="token function">recover<span class="token punctuation">(<span class="token punctuation">)<span class="token punctuation">; p <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
tx<span class="token punctuation">.<span class="token function">Rollback<span class="token punctuation">(<span class="token punctuation">)
<span class="token function">panic<span class="token punctuation">(p<span class="token punctuation">) <span class="token comment">// re-throw panic after Rollback
<span class="token punctuation">} <span class="token keyword keyword-else">else <span class="token keyword keyword-if">if err <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
fmt<span class="token punctuation">.<span class="token function">Println<span class="token punctuation">(<span class="token string">"rollback"<span class="token punctuation">)
tx<span class="token punctuation">.<span class="token function">Rollback<span class="token punctuation">(<span class="token punctuation">) <span class="token comment">// err is non-nil; don't change it
<span class="token punctuation">} <span class="token keyword keyword-else">else <span class="token punctuation">{
err <span class="token operator">= tx<span class="token punctuation">.<span class="token function">Commit<span class="token punctuation">(<span class="token punctuation">) <span class="token comment">// err is nil; if Commit returns error update err
fmt<span class="token punctuation">.<span class="token function">Println<span class="token punctuation">(<span class="token string">"commit"<span class="token punctuation">)
<span class="token punctuation">}
<span class="token punctuation">}<span class="token punctuation">(<span class="token punctuation">)
sqlStr1 <span class="token operator">:= <span class="token string">"Update user set age=20 where id=?"
rs<span class="token punctuation">, err <span class="token operator">:= tx<span class="token punctuation">.<span class="token function">Exec<span class="token punctuation">(sqlStr1<span class="token punctuation">, <span class="token number">1<span class="token punctuation">)
<span class="token keyword keyword-if">if err<span class="token operator">!= <span class="token boolean">nil<span class="token punctuation">{
<span class="token keyword keyword-return">return err
<span class="token punctuation">}
n<span class="token punctuation">, err <span class="token operator">:= rs<span class="token punctuation">.<span class="token function">RowsAffected<span class="token punctuation">(<span class="token punctuation">)
<span class="token keyword keyword-if">if err <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
<span class="token keyword keyword-return">return err
<span class="token punctuation">}
<span class="token keyword keyword-if">if n <span class="token operator">!= <span class="token number">1 <span class="token punctuation">{
<span class="token keyword keyword-return">return errors<span class="token punctuation">.<span class="token function">New<span class="token punctuation">(<span class="token string">"exec sqlStr1 failed"<span class="token punctuation">)
<span class="token punctuation">}
sqlStr2 <span class="token operator">:= <span class="token string">"Update user set age=50 where i=?"
rs<span class="token punctuation">, err <span class="token operator">= tx<span class="token punctuation">.<span class="token function">Exec<span class="token punctuation">(sqlStr2<span class="token punctuation">, <span class="token number">5<span class="token punctuation">)
<span class="token keyword keyword-if">if err<span class="token operator">!=<span class="token boolean">nil<span class="token punctuation">{
<span class="token keyword keyword-return">return err
<span class="token punctuation">}
n<span class="token punctuation">, err <span class="token operator">= rs<span class="token punctuation">.<span class="token function">RowsAffected<span class="token punctuation">(<span class="token punctuation">)
<span class="token keyword keyword-if">if err <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
<span class="token keyword keyword-return">return err
<span class="token punctuation">}
<span class="token keyword keyword-if">if n <span class="token operator">!= <span class="token number">1 <span class="token punctuation">{
<span class="token keyword keyword-return">return errors<span class="token punctuation">.<span class="token function">New<span class="token punctuation">(<span class="token string">"exec sqlStr1 failed"<span class="token punctuation">)
<span class="token punctuation">}
<span class="token keyword keyword-return">return err
<span class="token punctuation">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h2 id="autoid-0-3-6">sqlx.In</h2>
<p><code>sqlx.In</code>是<code>sqlx</code>提供的一个非常方便的函数。</p>
<h3 id="autoid-0-4-0">sqlx.In的批量插入示例</h3>
<h4 id="表结构">表结构</h4>
<p>为了方便演示插入数据操作,这里创建一个<code>user</code>表,表结构如下:</p>
<pre class="line-numbers language-sql"><code class=" language-sql">CREATE TABLE `user` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(20) DEFAULT '',
`age` INT(11) DEFAULT '0',
PRIMARY KEY(`id`)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
</code></pre>
<h4 id="结构体">结构体</h4>
<p>定义一个<code>user</code>结构体,字段通过tag与数据库中user表的列一致。</p>
<pre class="line-numbers language-go"><code class=" language-go"><span class="token keyword keyword-type">type User <span class="token keyword keyword-struct">struct <span class="token punctuation">{
Name <span class="token builtin">string <span class="token string">`db:"name"`
Age<span class="token builtin">int <span class="token string">`db:"age"`
<span class="token punctuation">}
</span></span></span></span></span></span></span></span></code></pre>
<h4 id="bindvars-绑定变量">bindvars(绑定变量)</h4>
<p>查询占位符<code>?</code>在内部称为<strong>bindvars(查询占位符)</strong>,它非常重要。你应该始终使用它们向数据库发送值,因为它们可以防止SQL注入攻击。<code>database/sql</code>不尝试对查询文本进行任何验证;它与编码的参数一起按原样发送到服务器。除非驱动程序实现一个特殊的接口,否则在执行之前,查询是在服务器上准备的。因此<code>bindvars</code>是特定于数据库的:</p>
<ul>
<li>MySQL中使用<code>?</code></li>
<li>PostgreSQL使用枚举的<code>$1</code>、<code>$2</code>等bindvar语法</li>
<li>SQLite中<code>?</code>和<code>$1</code>的语法都支持</li>
<li>Oracle中使用<code>:name</code>的语法</li>
</ul>
<p><code>bindvars</code>的一个常见误解是,它们用来在sql语句中插入值。它们其实仅用于参数化,不允许更改SQL语句的结构。例如,使用<code>bindvars</code>尝试参数化列或表名将不起作用:</p>
<pre class="line-numbers language-go"><code class=" language-go"><span class="token comment">// ?不能用来插入表名(做SQL语句中表名的占位符)
db<span class="token punctuation">.<span class="token function">Query<span class="token punctuation">(<span class="token string">"SELECT * FROM ?"<span class="token punctuation">, <span class="token string">"mytable"<span class="token punctuation">)
<span class="token comment">// ?也不能用来插入列名(做SQL语句中列名的占位符)
db<span class="token punctuation">.<span class="token function">Query<span class="token punctuation">(<span class="token string">"SELECT ?, ? FROM people"<span class="token punctuation">, <span class="token string">"name"<span class="token punctuation">, <span class="token string">"location"<span class="token punctuation">)
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h4 id="自己拼接语句实现批量插入">自己拼接语句实现批量插入</h4>
<p>比较笨,但是很好理解。就是有多少个User就拼接多少个<code>(?, ?)</code>。</p>
<pre class="line-numbers language-go"><code class=" language-go"><span class="token comment">// BatchInsertUsers 自行构造批量插入的语句
<span class="token keyword keyword-func">func <span class="token function">BatchInsertUsers<span class="token punctuation">(users <span class="token punctuation">[<span class="token punctuation">]<span class="token operator">*User<span class="token punctuation">) <span class="token builtin">error <span class="token punctuation">{
<span class="token comment">// 存放 (?, ?) 的slice
valueStrings <span class="token operator">:= <span class="token function">make<span class="token punctuation">(<span class="token punctuation">[<span class="token punctuation">]<span class="token builtin">string<span class="token punctuation">, <span class="token number">0<span class="token punctuation">, <span class="token function">len<span class="token punctuation">(users<span class="token punctuation">)<span class="token punctuation">)
<span class="token comment">// 存放values的slice
valueArgs <span class="token operator">:= <span class="token function">make<span class="token punctuation">(<span class="token punctuation">[<span class="token punctuation">]<span class="token keyword keyword-interface">interface<span class="token punctuation">{<span class="token punctuation">}<span class="token punctuation">, <span class="token number">0<span class="token punctuation">, <span class="token function">len<span class="token punctuation">(users<span class="token punctuation">) <span class="token operator">* <span class="token number">2<span class="token punctuation">)
<span class="token comment">// 遍历users准备相关数据
<span class="token keyword keyword-for">for <span class="token boolean">_<span class="token punctuation">, u <span class="token operator">:= <span class="token keyword keyword-range">range users <span class="token punctuation">{
<span class="token comment">// 此处占位符要与插入值的个数对应
valueStrings <span class="token operator">= <span class="token function">append<span class="token punctuation">(valueStrings<span class="token punctuation">, <span class="token string">"(?, ?)"<span class="token punctuation">)
valueArgs <span class="token operator">= <span class="token function">append<span class="token punctuation">(valueArgs<span class="token punctuation">, u<span class="token punctuation">.Name<span class="token punctuation">)
valueArgs <span class="token operator">= <span class="token function">append<span class="token punctuation">(valueArgs<span class="token punctuation">, u<span class="token punctuation">.Age<span class="token punctuation">)
<span class="token punctuation">}
<span class="token comment">// 自行拼接要执行的具体语句
stmt <span class="token operator">:= fmt<span class="token punctuation">.<span class="token function">Sprintf<span class="token punctuation">(<span class="token string">"INSERT INTO user (name, age) VALUES %s"<span class="token punctuation">,
strings<span class="token punctuation">.<span class="token function">Join<span class="token punctuation">(valueStrings<span class="token punctuation">, <span class="token string">","<span class="token punctuation">)<span class="token punctuation">)
<span class="token boolean">_<span class="token punctuation">, err <span class="token operator">:= DB<span class="token punctuation">.<span class="token function">Exec<span class="token punctuation">(stmt<span class="token punctuation">, valueArgs<span class="token operator">...<span class="token punctuation">)
<span class="token keyword keyword-return">return err
<span class="token punctuation">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h4 id="使用sqlx-in实现批量插入">使用sqlx.In实现批量插入</h4>
<p>前提是需要我们的结构体实现<code>driver.Valuer</code>接口:</p>
<pre class="line-numbers language-go"><code class=" language-go"><span class="token keyword keyword-func">func <span class="token punctuation">(u User<span class="token punctuation">) <span class="token function">Value<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">(driver<span class="token punctuation">.Value<span class="token punctuation">, <span class="token builtin">error<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword keyword-return">return <span class="token punctuation">[<span class="token punctuation">]<span class="token keyword keyword-interface">interface<span class="token punctuation">{<span class="token punctuation">}<span class="token punctuation">{u<span class="token punctuation">.Name<span class="token punctuation">, u<span class="token punctuation">.Age<span class="token punctuation">}<span class="token punctuation">, <span class="token boolean">nil
<span class="token punctuation">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>使用<code>sqlx.In</code>实现批量插入代码如下:</p>
<pre class="line-numbers language-go"><code class=" language-go"><span class="token comment">// BatchInsertUsers2 使用sqlx.In帮我们拼接语句和参数, 注意传入的参数是[]interface{}
<span class="token keyword keyword-func">func <span class="token function">BatchInsertUsers2<span class="token punctuation">(users <span class="token punctuation">[<span class="token punctuation">]<span class="token keyword keyword-interface">interface<span class="token punctuation">{<span class="token punctuation">}<span class="token punctuation">) <span class="token builtin">error <span class="token punctuation">{
query<span class="token punctuation">, args<span class="token punctuation">, <span class="token boolean">_ <span class="token operator">:= sqlx<span class="token punctuation">.<span class="token function">In<span class="token punctuation">(
<span class="token string">"INSERT INTO user (name, age) VALUES (?), (?), (?)"<span class="token punctuation">,
users<span class="token operator">...<span class="token punctuation">, <span class="token comment">// 如果arg实现了 driver.Valuer, sqlx.In 会通过调用 Value()来展开它
<span class="token punctuation">)
fmt<span class="token punctuation">.<span class="token function">Println<span class="token punctuation">(query<span class="token punctuation">) <span class="token comment">// 查看生成的querystring
fmt<span class="token punctuation">.<span class="token function">Println<span class="token punctuation">(args<span class="token punctuation">)<span class="token comment">// 查看生成的args
<span class="token boolean">_<span class="token punctuation">, err <span class="token operator">:= DB<span class="token punctuation">.<span class="token function">Exec<span class="token punctuation">(query<span class="token punctuation">, args<span class="token operator">...<span class="token punctuation">)
<span class="token keyword keyword-return">return err
<span class="token punctuation">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h4 id="使用namedexec实现批量插入">使用NamedExec实现批量插入</h4>
<p><strong>注意</strong> :该功能目前有人已经推了#285 PR,但是作者还没有发<code>release</code>,所以想要使用下面的方法实现批量插入需要暂时使用<code>master</code>分支的代码:</p>
<p>在项目目录下执行以下命令下载并使用<code>master</code>分支代码:</p>
<pre class="line-numbers language-bash"><code class=" language-bash">go get github.com/jmoiron/sqlx@master
</code></pre>
<p>使用<code>NamedExec</code>实现批量插入的代码如下:</p>
<pre class="line-numbers language-go"><code class=" language-go"><span class="token comment">// BatchInsertUsers3 使用NamedExec实现批量插入
<span class="token keyword keyword-func">func <span class="token function">BatchInsertUsers3<span class="token punctuation">(users <span class="token punctuation">[<span class="token punctuation">]<span class="token operator">*User<span class="token punctuation">) <span class="token builtin">error <span class="token punctuation">{
<span class="token boolean">_<span class="token punctuation">, err <span class="token operator">:= DB<span class="token punctuation">.<span class="token function">NamedExec<span class="token punctuation">(<span class="token string">"INSERT INTO user (name, age) VALUES (:name, :age)"<span class="token punctuation">, users<span class="token punctuation">)
<span class="token keyword keyword-return">return err
<span class="token punctuation">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>把上面三种方法综合起来试一下:</p>
<pre class="line-numbers language-go"><code class=" language-go"><span class="token keyword keyword-func">func <span class="token function">main<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
err <span class="token operator">:= <span class="token function">initDB<span class="token punctuation">(<span class="token punctuation">)
<span class="token keyword keyword-if">if err <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
<span class="token function">panic<span class="token punctuation">(err<span class="token punctuation">)
<span class="token punctuation">}
<span class="token keyword keyword-defer">defer DB<span class="token punctuation">.<span class="token function">Close<span class="token punctuation">(<span class="token punctuation">)
u1 <span class="token operator">:= User<span class="token punctuation">{Name<span class="token punctuation">: <span class="token string">"七米"<span class="token punctuation">, Age<span class="token punctuation">: <span class="token number">18<span class="token punctuation">}
u2 <span class="token operator">:= User<span class="token punctuation">{Name<span class="token punctuation">: <span class="token string">"q1mi"<span class="token punctuation">, Age<span class="token punctuation">: <span class="token number">28<span class="token punctuation">}
u3 <span class="token operator">:= User<span class="token punctuation">{Name<span class="token punctuation">: <span class="token string">"小王子"<span class="token punctuation">, Age<span class="token punctuation">: <span class="token number">38<span class="token punctuation">}
<span class="token comment">// 方法1
users <span class="token operator">:= <span class="token punctuation">[<span class="token punctuation">]<span class="token operator">*User<span class="token punctuation">{<span class="token operator">&u1<span class="token punctuation">, <span class="token operator">&u2<span class="token punctuation">, <span class="token operator">&u3<span class="token punctuation">}
err <span class="token operator">= <span class="token function">BatchInsertUsers<span class="token punctuation">(users<span class="token punctuation">)
<span class="token keyword keyword-if">if err <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"BatchInsertUsers failed, err:%v\n"<span class="token punctuation">, err<span class="token punctuation">)
<span class="token punctuation">}
<span class="token comment">// 方法2
users2 <span class="token operator">:= <span class="token punctuation">[<span class="token punctuation">]<span class="token keyword keyword-interface">interface<span class="token punctuation">{<span class="token punctuation">}<span class="token punctuation">{u1<span class="token punctuation">, u2<span class="token punctuation">, u3<span class="token punctuation">}
err <span class="token operator">= <span class="token function">BatchInsertUsers2<span class="token punctuation">(users2<span class="token punctuation">)
<span class="token keyword keyword-if">if err <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"BatchInsertUsers2 failed, err:%v\n"<span class="token punctuation">, err<span class="token punctuation">)
<span class="token punctuation">}
<span class="token comment">// 方法3
users3 <span class="token operator">:= <span class="token punctuation">[<span class="token punctuation">]<span class="token operator">*User<span class="token punctuation">{<span class="token operator">&u1<span class="token punctuation">, <span class="token operator">&u2<span class="token punctuation">, <span class="token operator">&u3<span class="token punctuation">}
err <span class="token operator">= <span class="token function">BatchInsertUsers3<span class="token punctuation">(users3<span class="token punctuation">)
<span class="token keyword keyword-if">if err <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
fmt<span class="token punctuation">.<span class="token function">Printf<span class="token punctuation">(<span class="token string">"BatchInsertUsers3 failed, err:%v\n"<span class="token punctuation">, err<span class="token punctuation">)
<span class="token punctuation">}
<span class="token punctuation">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h3 id="autoid-0-4-1">sqlx.In的查询示例</h3>
<p>关于<code>sqlx.In</code>这里再补充一个用法,在<code>sqlx</code>查询语句中实现In查询和FIND_IN_SET函数。即实现<code>SELECT * FROM user WHERE id in (3, 2, 1);</code>和<code>SELECT * FROM user WHERE id in (3, 2, 1) ORDER BY FIND_IN_SET(id, '3,2,1');</code>。</p>
<h4 id="in查询">in查询</h4>
<p>查询id在给定id集合中的数据。</p>
<pre class="line-numbers language-go"><code class=" language-go"><span class="token comment">// QueryByIDs 根据给定ID查询
<span class="token keyword keyword-func">func <span class="token function">QueryByIDs<span class="token punctuation">(ids <span class="token punctuation">[<span class="token punctuation">]<span class="token builtin">int<span class="token punctuation">)<span class="token punctuation">(users <span class="token punctuation">[<span class="token punctuation">]User<span class="token punctuation">, err <span class="token builtin">error<span class="token punctuation">)<span class="token punctuation">{
<span class="token comment">// 动态填充id
query<span class="token punctuation">, args<span class="token punctuation">, err <span class="token operator">:= sqlx<span class="token punctuation">.<span class="token function">In<span class="token punctuation">(<span class="token string">"SELECT name, age FROM user WHERE id IN (?)"<span class="token punctuation">, ids<span class="token punctuation">)
<span class="token keyword keyword-if">if err <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
<span class="token keyword keyword-return">return
<span class="token punctuation">}
<span class="token comment">// sqlx.In 返回带 `?` bindvar的查询语句, 我们使用Rebind()重新绑定它
query <span class="token operator">= DB<span class="token punctuation">.<span class="token function">Rebind<span class="token punctuation">(query<span class="token punctuation">)
err <span class="token operator">= DB<span class="token punctuation">.<span class="token function">Select<span class="token punctuation">(<span class="token operator">&users<span class="token punctuation">, query<span class="token punctuation">, args<span class="token operator">...<span class="token punctuation">)
<span class="token keyword keyword-return">return
<span class="token punctuation">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h4 id="in查询和find-in-set函数">in查询和FIND_IN_SET函数</h4>
<p>查询id在给定id集合的数据并维持给定id集合的顺序。</p>
<pre class="line-numbers language-go"><code class=" language-go"><span class="token comment">// QueryAndOrderByIDs 按照指定id查询并维护顺序
<span class="token keyword keyword-func">func <span class="token function">QueryAndOrderByIDs<span class="token punctuation">(ids <span class="token punctuation">[<span class="token punctuation">]<span class="token builtin">int<span class="token punctuation">)<span class="token punctuation">(users <span class="token punctuation">[<span class="token punctuation">]User<span class="token punctuation">, err <span class="token builtin">error<span class="token punctuation">)<span class="token punctuation">{
<span class="token comment">// 动态填充id
strIDs <span class="token operator">:= <span class="token function">make<span class="token punctuation">(<span class="token punctuation">[<span class="token punctuation">]<span class="token builtin">string<span class="token punctuation">, <span class="token number">0<span class="token punctuation">, <span class="token function">len<span class="token punctuation">(ids<span class="token punctuation">)<span class="token punctuation">)
<span class="token keyword keyword-for">for <span class="token boolean">_<span class="token punctuation">, id <span class="token operator">:= <span class="token keyword keyword-range">range ids <span class="token punctuation">{
strIDs <span class="token operator">= <span class="token function">append<span class="token punctuation">(strIDs<span class="token punctuation">, fmt<span class="token punctuation">.<span class="token function">Sprintf<span class="token punctuation">(<span class="token string">"%d"<span class="token punctuation">, id<span class="token punctuation">)<span class="token punctuation">)
<span class="token punctuation">}
query<span class="token punctuation">, args<span class="token punctuation">, err <span class="token operator">:= sqlx<span class="token punctuation">.<span class="token function">In<span class="token punctuation">(<span class="token string">"SELECT name, age FROM user WHERE id IN (?) ORDER BY FIND_IN_SET(id, ?)"<span class="token punctuation">, ids<span class="token punctuation">, strings<span class="token punctuation">.<span class="token function">Join<span class="token punctuation">(strIDs<span class="token punctuation">, <span class="token string">","<span class="token punctuation">)<span class="token punctuation">)
<span class="token keyword keyword-if">if err <span class="token operator">!= <span class="token boolean">nil <span class="token punctuation">{
<span class="token keyword keyword-return">return
<span class="token punctuation">}
<span class="token comment">// sqlx.In 返回带 `?` bindvar的查询语句, 我们使用Rebind()重新绑定它
query <span class="token operator">= DB<span class="token punctuation">.<span class="token function">Rebind<span class="token punctuation">(query<span class="token punctuation">)
err <span class="token operator">= DB<span class="token punctuation">.<span class="token function">Select<span class="token punctuation">(<span class="token operator">&users<span class="token punctuation">, query<span class="token punctuation">, args<span class="token operator">...<span class="token punctuation">)
<span class="token keyword keyword-return">return
<span class="token punctuation">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>当然,在这个例子里面你也可以先使用<code>IN</code>查询,然后通过代码按给定的ids对查询结果进行排序。</p>
<p> </p>
<p>来自 https://www.liwenzhou.com/posts/Go/sqlx/</p><br><br>
来源:https://www.cnblogs.com/staff/p/13266574.html
頁:
[1]