- Created by 郭强, last modified on Jun 21, 2021
Insert/Replace/Save
这几个链式操作方法用于数据的写入,并且支持自动的单条或者批量的数据写入,区别如下:
Insert
使用
INSERT INTO
语句进行数据库写入,如果写入的数据中存在主键或者唯一索引时,返回失败,否则写入一条新数据;Replace
使用
REPLACE INTO
语句进行数据库写入,如果写入的数据中存在主键或者唯一索引时,会删除原有的记录,必定会写入一条新记录;Save
使用
INSERT INTO
语句进行数据库写入,如果写入的数据中存在主键或者唯一索引时,更新原有数据,否则写入一条新数据;
在部分数据库类型中,并不支持
Replace/Save
方法,具体请参考数据库类型介绍章节。
这几个方法往往需要结合Data
方法使用,该方法用于传递数据参数,用于数据写入/更新等写操作,支持的参数为string/map/slice/struct/*struct
。例如,在进行Insert
操作时,开发者可以传递任意的map
类型,如: map[string]string
/map[string]interface{}
/map[interface{}]interface{}
等等,也可以传递任意的struct/*struct/[]struct/[]*struct
类型。此外,这几个方法的参数列表也支持直接的data
参数输入,该参数Data
方法参数一致。
InsertIgnore
从goframe v1.9.0
版本开始,goframe
的ORM
提供了一个常用写入方法InsertIgnore
,用于写入数据时如果写入的数据中存在主键或者唯一索引时,忽略错误继续执行写入。该方法定义如下:
func (m *Model) InsertIgnore(data ...interface{}) (result sql.Result, err error)
InsertAndGetId
从goframe v1.15.7
版本开始,goframe
的ORM
同时也提供了一个常用写入方法InsertAndGetId
,用于写入数据时并直接返回自增字段的ID
。该方法定义如下:
func (m *Model) InsertAndGetId(data ...interface{}) (lastInsertId int64, err error)
使用示例
示例1,基本使用
数据写入/保存方法往往需要结合Data
方法使用:
// INSERT INTO `user`(`name`) VALUES('john') db.Model("user").Data(g.Map{"name": "john"}).Insert() // INSERT IGNORE INTO `user`(`uid`,`name`) VALUES(10000,'john') db.Model("user").Data(g.Map{"uid": 10000, "name": "john"}).InsertIgnore() // REPLACE INTO `user`(`uid`,`name`) VALUES(10000,'john') db.Model("user").Data(g.Map{"uid": 10000, "name": "john"}).Replace() // INSERT INTO `user`(`uid`,`name`) VALUES(10001,'john') ON DUPLICATE KEY UPDATE `uid`=VALUES(`uid`),`name`=VALUES(`name`) db.Model("user").Data(g.Map{"uid": 10001, "name": "john"}).Save()
也可以不使用Data
方法,而给写入/保存方法直接传递数据参数:
db.Model("user").Insert(g.Map{"name": "john"})
db.Model("user").Replace(g.Map{"uid": 10000, "name": "john"})
db.Model("user").Save(g.Map{"uid": 10001, "name": "john"})
数据参数也常用
struct
类型,例如当表字段为
uid/name/site
时:
type User struct {
Uid int `orm:"uid"`
Name string `orm:"name"`
Site string `orm:"site"`
}
user := &User{
Uid: 1,
Name: "john",
Site: "https://goframe.org",
}
// INSERT INTO `user`(`uid`,`name`,`site`) VALUES(1,'john','https://goframe.org')
g.DB().Model("user").Data(user).Insert()
示例2,数据批量写入
// INSERT INTO `user`(`name`) VALUES('john_1'),('john_2'),('john_3')
db.Model("user").Data(g.List{
{"name": "john_1"},
{"name": "john_2"},
{"name": "john_3"},
}).Insert()
可以通过Batch
方法指定批量操作中分批写入条数数量(默认是10
),以下示例将会被拆分为两条写入请求:
// INSERT INTO `user`(`name`) VALUES('john_1'),('john_2')
// INSERT INTO `user`(`name`) VALUES('john_3')
db.Model("user").Data(g.List{
{"name": "john_1"},
{"name": "john_2"},
{"name": "john_3"},
}).Batch(2).Insert()
示例3,数据批量保存
批量保存操作与单条保存操作原理是一样的,当写入的数据中存在主键或者唯一索引时将会更新原有记录值,否则新写入一条记录。
// INSERT INTO `user`(`uid`,`name`) VALUES(10000,'john_1'),(10001,'john_2'),(10002,'john_3')
// ON DUPLICATE KEY UPDATE `uid`=VALUES(`uid`),`name`=VALUES(`name`)
db.Model("user").Data(g.List{
{"uid":10000, "name": "john_1"},
{"uid":10001, "name": "john_2"},
{"uid":10002, "name": "john_3"},
}).Save()
RawSQL
语句嵌入
gdb.Raw
是字符串类型,该类型的参数将会直接作为SQL
片段嵌入到提交到底层的SQL
语句中,不会被自动转换为字符串参数类型、也不会被当做预处理参数。例如:
// INSERT INTO `user`(`id`,`passport`,`password`,`nickname`,`create_time`) VALUES('id+2','john','123456','now()') db.Model("user").Data(g.Map{ "id": "id+2", "passport": "john", "password": "123456", "nickname": "JohnGuo", "create_time": "now()", }).Insert() // 执行报错:Error Code: 1136. Column count doesn't match value count at row 1
使用gdb.Raw
改造后:
// INSERT INTO `user`(`id`,`passport`,`password`,`nickname`,`create_time`) VALUES(id+2,'john','123456',now()) db.Model("user").Data(g.Map{ "id": gdb.Raw("id+2"), "passport": "john", "password": "123456", "nickname": "JohnGuo", "create_time": gdb.Raw("now()"), }).Insert()
- No labels
16 Comments
糖水不加糖
Save
方法应该需要做一个Data
的区分,分为InsertData
与SaveData
.例如以下场景:记录当日ip
访问次数,不存在时写入数据ip,count=1
,存在时count=count+1
.这种情况下不做数据源区分会直接导致数据覆盖.郭强
这样的易用性比较低,复杂度也提高了,你见过有其他
ORM
这么设计么?糖水不加糖
SaveData
作为可选参数,当SaveData
不存在时直接引用InsertData
作为填充.的确作为ORM
很少实现这样的功能,所以最后都是以手撸sql
来实现.sanrentai
Insert支持model数组,批量插入吗?
郭强
支持。
singsing
1、User.Insert() 和 db.Table("user").Data(g.Map{"name": "john"}).Insert() 是不是会造成不统一的混乱
2、insert后能否支持一下,回写主键自增id,现在是通过LastInsertId手动回写的
感谢
郭强
1、
dao.User.Insert(xxx)
方式只是为了对简单的操作提供便捷的快捷方式,这种操作比dao.User.Data(xxx).Insert()
要方便一些。其实Update操作更能体现便捷,例如dao.User.Data(xxx).Where(xxx).Update()
与dao.User.Update(xxx, xxx)
差别就比较大。2、自增ID都是通过
LastInsertId
获取的,这其实也是标准库推荐的方式,因为这个自增ID获取其实是一条新的SQL操作,由开发者主动选择性调用。HeyBelsa
oracle下该如何通过Insert()插入一条自增数据,我使用gdb.Raw(“table_seq.nextval”)来对主键进行自增,然而Raw的嵌入没有生效,打印的sql显示这依然是一个字符串 'table_seq.nextval',本人比较菜,不知道是不是操作有问题哈
郭强
麻烦将代码往github上提个issue
maccoxiong
最新的 v1.15.7 版本,无法写入数据库 ,Insert,Data,InsertAndGetId 方法均不能写入数据库,也不报错
db := g.DB("default")
r, err := db.Table("chat_window").Insert(g.Map{"from_account": "keke"})
aries
看下database的配置,是不是在空跑模式下呢
maccoxiong
感谢,是我复制官网的配置文件,里面有dryrun =1 只读不写造成的
jason
orm 操作能否返回这个对象的实例呢,例如加个 create 方法成功后返回这个 model 的实例,有些场景需要给前端返回这个 model 的一些字段,看到现在设计基本上是实现了 sql.Result 这个接口返回主键 id 和插入记录的行数
HW
jory
尝试了下,1.6 版还不支持"InsertAndGetId" 这个方法?
那么,链式的结构,目前可以支持从链式结构转为原生的sql语句么?
郭强
从
2.2
版本开始提供了ToSQL
和CatchSQL
的方法,可以看看。