GORM就是通过Go语言直接使用封装好的SQL语句,在使用的时候很多方法,那到底这些东西是如何执行的。主要说一下常见的几个CRUD方法。
0. 引言
GORM就是通过Go语言直接使用封装好的SQL语句,在使用的时候很多方法,那到底这些东西是如何执行的。主要说一下常见的几个CRUD方法。
1. 连接数据库
使用的是gorm.Open
参数第一个是用mysql
打开数据库(参数是数据库的相关信息),第二个参数是使用gorm
的一些参数,一般来说是不需要修改的。
dsn := "root:123456@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
2. AutoMigrate
这个方法是用来给把表和结构体连接起来的。
db.AutoMigrate(&User{})
这样一来,如果没有users
表就会创建一个,如果有也会再添加上原表没有的列。
里面的参数是地址&User{}
。
这样一来就可以直接通过db
对表和结构体进行操作了。
3. CRUD
3.1 增
3.1.1 CREATE
因为是创建一条信息,所以需要创建一个实例然后设定参数,最后作为参数使用Create
方法,这样就会把实例所携带的信息添加到users
表中。
// 方法一:
u := new(User)
u.Name = "Fire"
u.Age = 18
db.Create(u)
// 方法二:
db.Create(&User{Name: "Fire",Age: 18})
那既然如此,如果我传递的参数带了多条信息会发生什么?也就是说我传递的是一个User
的切片:
u := []User{{Name: "f1", Age: 19}, {Name: "f2", Age: 29}}
db.Create(&u)
wow,可以发现两条都添加成功了,说明Create
是把实例所带的信息全部给创建到表中。
3.2 查
3.2.1 First
这个方法是返回表单中满足条件的第一条信息。
很容易理解的,至于如何使用,直接把一个空实例放进去即可:
// 方法一:
u := new(User)
db.First(&u1)
// 方法二:
db.First(&User{})
第二种方法属于是匿名查询了,因为这个函数会把查询到的内容直接修改到参数中(这也是为什么要取地址的原因,结构体是默认值传递),之后可以打印这个参数看一下。
为什么说是满足条件的第一条信息而不是第一条信息呢,因为First
实际上是可以带参数的,只是默认条件下返回表单中第一条:
// 形式一:
db.First(&User{},"name = ?","firecar")
// 形式二:
db.Where("name = ?", "firecar2").First(&u1)
实际上两种形式是等价的,相当于一个条件查询。
那如果说,我作为参数的实例携带了信息会是什么效果呢?让我们做一个简单的测试:
//db.First(&User{Name: "f1", Age: 19})
u := User{Name: "f1", Age: 19}
db.First(&u)
最后得到的结果是——返回的是表单中的第一条信息。
也就是说通过这个方法你传递的实例是否携带信息是无关紧要的事情(不关心你个人怎么样,只关心你整个家族),这么说来这个方法还挺薄情。
3.2.2 Find
Find
这个方法就更有意思了。
Find
使用和First
是一模一样的,只不过它会返回所有满足条件的信息而First
只返回一条,当Find
没有其他参数时默认返回所有信息:
u := User{}
db.Find(&u)
上面的代码理论上是会返回所有的信息,但是如果你的参数是一个而不是切片,他只会返回第一条信息(返回到参数里,参数是一个就只能存一个),如:
u1 := User{}
db.Find(&u1) // u1只会存第一条信息
u2 := []User{}
db.Find(&u2) // u2会存储所有信息
至于Find
的条件查询也和First
一样,如果传进来的参数有信息,也会被查询给覆盖掉,因此,参数有没有信息都无所谓。
3.3 改
3.3.1 Save
修改(更新)总得有个修改的对象,因此修改是要依托于之前的查询的。
u := new(User)
db.First(u)
u.Name = "hyc"
db.Save(u)
上面是最基础的改法,查询到要修改的参数,然后直接进行修改,之后使用Save
来保存即可。
那我如果这样写db.Save(&User{Name: "qaq"})
会发生什么?发现多了一条新的记录也就是说按照主键找不到该内容的话,Save
就会根据参数信息再创建一条新的信息。
Save
这个方法只是一个单纯的保存行为,不能进行更多的操作了,因此按理来说只能通过上面一种方式来进行修改,不过参数u
应该可以是个切片,只要信息里有主键就好了,举个例吧:
var u []User
db.Find(&u, "age = ?", 18)
u[0].Name = "firec"
u[1].Age = 7
db.Save(u)
总之只要你能把握好下标对应的到底是哪一条信息,这样做也无可厚非……
3.3.2 Update
与其他的方法不同的是,用Update
之前要先用Model
指定模型(到底在那张表上修改啊),如果我按照下面方法来操作:
db.Model(&User{}).Update("age", 77)
这种情况下,很明显新创建的模型只告诉他这关于是users
表修改,但是没有其他任何信息的,因此导致Update
不知道到底要把谁的"age"
修改为77
,因此报错也是理所当然的事情了。(但是对于Model的描述中没有修改,实际上不可以通过这种方法直接全部修改。如果要全部修改还是要用Find
)
那也就是说只要模型里有相关信息(最主要的是包含主键)就可以了:
var u User
db.First(&u)
db.Model(&u).Update("age", 77)
使用Where
来进行条件修改也是可以的:
db.Model(&User{}).Where("age = 19").Update("name", "hello")
那他能不能同时修改好几个啊?试一试不就知道了:
var u []User
db.Find(&u, "age = ?", 18)
db.Model(&u).Update("age", 77)
结果是模型中所有的信息的"age"
全被修改为77
了,说明这是没有问题的!
话说为什么非要用一个Model
而不是直接和其他的一样作为Update
的参数捏?
3.3.3 Updates
这个就是同时修改多列的Update
,可以用Map也可以用结构体,不多做说明了。
3.4 删
3.4.1 Delete
说Delete
还是先说条件删除吧,根据条件来删除问题就会少很多,其实和前面的一样:
db.Where("age = ?", 18).Delete(&User{})
db.Delete(&User{},"age = ?",18)
所以说到底是用u := new(User) db.Delete(u)
还是db.Delete(&User{})
完全取决于你是不是要用这个u
(因为在函数内直接定义是匿名的,不能再次找到它了)。
显然在Delete
中我都删了我还用什么……
那再讲非条件删除的Delete
:
db.Delete(&User{})
db.Delete(&User{Name: "fire"})
这样是无法运行的WHERE conditions required
,因为之前的版本(我看的视频)用这种方法来进行Delete
会导致整张表上的所有信息都被软删除,可能是为了防止这种事情发生就干脆禁止了。
唯一可行的方法是标记信息的主键:
u := new(User)
u.ID = 1
db.Delete(u)
只能通过这种方式来进行删除。
当然你说我非要把整张表删除了,他就是跟我有仇,那你可以先Find
然后再Delete
:
u := new([]User) // 定义切片
db.Find(u) // 查询所有,信息返回到u
db.Delete(u) // 删除所有
只要是通过查询找出来要的u
就是可以这么删除,当然不限于全删,也可以是条件删除。
4. 总结
- 添加行为,就是放进去一个实例,之后要用的话就不要用匿名。
- 查询行为,和上面差不多,但是大多数情况下不会用匿名来查询;并且查询行为是可以通过
DB.Find(&User{})
来进行整张表的查询的。 - 修改行为,是不可以全局修改的(即不存在
DB.Model(&User{}).Update(...)
)这种操作的!因为修改就涉及到数据的准确性了。 - 删除行为,更是不能够直接全局修改了(
DB.Delete(&User{})
),否则很容易出事!
综上所述使用&User{}
还是u := new(User)
取决于之后会不会用到这个实例了;而没有信息的User
实例(&User{}
或者还没有赋值的u := new(User)
)所代表的是整个表(因为之前通过AutoMigrate
绑定了表和结构体)。修改和删除都可以在查询的基础上进行。