区块链Two——数据库(3)—Gorm

 这应该是学习的第四天了,简单来说一下博客的顺序,也就是我学习的顺序:go语言->区块链(因为实习就知道会用到这个技术就直接学了,其实可以再这之前接触一下以太坊)->Gorm(在学习区块链的时候,用到数据库了所以学习了一下gorm相关知识,因为java有过ORM的基础所以个人认为还是比较好理解的)->区块链(又回到区块链,继续学习,其实在这之前简单学习了一下Rabbimq)

整体来说我的学习路程感觉杂乱无章,我也不知道对不对。明天可能就会简单看一下项目代码,那具体有什么情况可能再去学习的话再来写一写文章。


CRUD:增删改查

1.创建

(1)创建记录

user := User{Name:"Jinzhu", Age:18, Birthday: time.Now()}

db.NewRecord(user) //主键为空返回true

db.Create(&user)db.NewRecord(user) //创建user后返回false

(2)默认值(在gorm tag中定义)

插入SQL会忽略有默认值的字段,并且其值为空。记录插入数据库之后,gorm从数据库加载这些字段的值

type Animal struct{

      ID  int64

      Name  string  `gorm:"default:'默认值'"` //设置默认值

      Age  int64

}

上述会将所有的0值字段全部赋成默认值,如果你想避免这种情况可以使用指针或者Scanner/Valuer接口,例如:

<1>使用指针:

type User struct {  

     ID   int64

     Name *string `gorm:"default:'默认值'"`

     Age  int64

}

user := User{Name: new(string), Age: 18))}

db.Create(&user)  //此时数据库中该条记录name字段的值就是‘ ’

<2>使用 Scanner/Valuer:

type User struct {  // 使用 Scanner/Valuer

       ID int64

       Name sql.NullString `gorm:"default:'默认值'"` // sql.NullString 实现了Scanner/Valuer接口

       Age  int64

}

user := User{Name: sql.NullString{"", true}, Age:18}

db.Create(&user) // 此时数据库中该条记录name字段的值就是‘ ’

(3)在Callbacks(回调函数)中设置主键

func (user *User) BeforeCreate (scope *gorm.Scope) error{

            scope.SetColumn("ID", uuid.New()) //设置主键

            return  nil

}

(4)扩展创建选项(有则更新,无则插入)

 db.Set("gorm:insert_option","ON CONFLICT").Create(&product)

2.查询

(1)基本查询

db.First(&user) //获取第一条记录,按主键排序

db.Last(&user) //获取最后一条记录,按主键排序

db.Find(&users) //获取所有记录

db.First(&user,10) //使用主键获取记录,id=10

(2)Where查询条件

          <1>简单SQL

                db.Where("name = ?","memgyu").First(&user) //查询第一个匹配的数据

                db.Where("name = ?","mengyu").Find(&users) //查询所有匹配的数据

                db.Where("name <> ?","mengyu").Find(&users) //查询name不是mengyu的数据

                db.Where("name in (?)", []string{"mengyu1","mengyu 2"}).Find(&users) //查询出name在(mengyu1,mengyu2)所有数据

                db.Where("name LIKE ?","%jin%").Find(&users) //模糊查询

                db.Where("name = ? AND age >= ?","mengyu","22").Find(&users) //查询name=mengyu并且age>=22的数据

                db.Where("updated_at > ?", lastWeek).Find(&users) //查询上一周的数据

                db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users) //查询上一周到今天的数据

            <2>Struct & Map(Struct查询只查询有值的字段)

                db.Where(&User{Name:"mengyu", Age:20}).First(&user)  //Struct:查询name=mengyu,age=20,从第一条数据开始查

                db.Where(map[string]interface{}{"name":"mengyu","age":20}).Find(&users) //Map:查询name=mengyu,age=20的数据

                db.Where([]int64{20,21,22}).Find(&users) //多主键slice查询:查询id=20,21,22的数据(数据量小的时候查询速度比map快)

(3)Not条件查询  

db.Not("name","mengyu").First(&user) //查询name≠mengyu的第一条数据

db.Not("name", []string{"mengyu1","mengyu2"}).Find(&users) //查询name≠memgyu1和mengyu2的数据

db.Not([]int64{1,2,3}).First(&user) //查询id为1、2、3的数据

db.Not([]int64{}).First(&user) //查询所有数据

db.Not("name = ?","mengyu").First(&user) //查询name≠mengyu的数据

db.Not(User{Name:"mengyu"}).First(&user) //查询name≠mengyu的数据

(4)带内联条件的查询

db.First(&user,23) //查询id=23的第一条数据

db.Find(&user,"name = ?","mengyu") //查询name=mengyu的数据

db.Find(&users,"name <> ? AND age > ?","mengyu",20) //查询name≠mengyu并且age=20的数据

db.Find(&users, User{Age:20}) //struct查询:查询age=20的数据

db.Find(&users,map[string]interface{}{"age":20}) //Map查询:查询age=20的数据

(5)Or条件查询

db.Where("role = ?","admin").Or("role = ?","super_admin").Find(&users) //查询角色为admin或者super_admin的角色

db.Where("name = 'mengyu1'").Or(User{Name:"mengyu2"}).Find(&users) ///struct查询:查询name=mengyu1或mengyu2的数据

db.Where("name = 'mengyu1'").Or(map[string]interface{}{"name":"mengyu 2"}).Find(&users) //Map查询:查询name=mengyu1或mengyu2的数据

(6)查询链(使用gorm自带的api)

db.Where("条件","条件的值").Where/Or/Not(...).Find(&表名)

(7)扩展查询选项

db.Set("gorm:query_option","FOR UPDATE").First(&user,10) // 若id=10有数据,则row lock(行锁)

(8)FirstOrInit(只适用Struct和Map)

db.FirstOrInit(&user, User{Name: "non_existing"}) //没查询到匹配的数据则初始化

db.FirstOrInit(&user,map[string]interface{}{"name":"mengyu"}) //获取匹配到的第一条数据

(9)Attrs

db.Where(User{Name:"mengyu"}).Attrs(User{Age:18}).FirstOrInit(&user)//没找到数据则初始化,找到则显示

(10)Assign

db.Where(User{Name:"mengyu"}).Assign(User{Age:20}).FirstOrInit(&user) //将age=20分配给name=mengyu的数据(不论找没找到)

(11)FirstOrCreate(只适用Struct和Map)

db.FirstOrCreate(&user, User{Name:"mengyu"}) //没有获取到,则创建新信息

db.Where(User{Name:"mengyu"}).FirstOrCreate(&user) //找到了则获取第一条name=mengyu的数据

(12)Select

db.Table("users").Select("COALESCE(age,?)",42).Rows() //返回第一个age=42并且非空的值

(13)Order

db.Order("age desc").Order("name").Find(&users) //按照年龄降序(asc为升序;desc为降序)查询数据的age和name

(14)Limit

db.Limit(10).Find(&users1).Limit(-1).Find(&users2) //查询user1的前10条数据;查询user2所有数据(Limit(-1)用来取消之前的限制条件)

(15)Offset

db.Offset(10).Find(&users1).Offset(-1).Find(&users2) ///查询user1的跳过前10条数据;查询user2所有数据(Offset(-1)用来取消之前的限制条件)

(16)Count

db.Model(&User{}).Where("name = ?","mengyu").Count(&count) //查询name=mengyu的数据有多少条

(17)还有一部分关键字,我没怎么太接触,打算以后遇到了再仔细研究。想看的小伙伴可以看原版

(17)指定表名

db.Table("users1").CreateTable(&User{}) //使用user结构体定义创建users1表

var users1 []User //来一个user1

db.Table("users1").Where("name = ?","mengyu").Delete() //查询name=mengyu的数据

3.预加载

(1)预加载

预加载是指暂时下载,是储存在你电脑的隐藏文件夹里的,当它的储存量超过一定数值时,会抵消最开始的缓存

db.Preload("Orders","state NOT IN (?)","cancelled").Find(&users) //从表Orders找到id=1,2,3,4并且列(state)≠cancelled进行预加载

(2)自定义预加载SQL

可以通过传递func(db *gorm.DB) *gorm.DB来自定义预加载SQL

db.Preload("Orders",func(db *gorm.DB)*gorm.DB{returndb.Order("orders.amount DESC")}).Find(&users) //从表Orders查询user_id=1,2,3,4并且降序排列进行预加载

(3)嵌套加载

db.Preload("Orders.OrderItems").Find(&users)

db.Preload("Orders","state = ?","paid").Preload("Orders.OrderItems").Find(&users)

4.更新

(1)更新全部字段
db.First(&user) //获取第一条信息

user.Name ="mengyu"  //更新name=mengyu

user.Age =100 //更新age=100

db.Save(&user) //更新全部字段,即使有些字段没有更改

(2)更新更改字段

db.Model(&user).Update("name","hello") //更新name=hello(单个属性)

db.Model(&user).Where("active = ?",true).Update("name","hello") //使用组合条件更新单个属性

db.Model(&user).Updates(map[string]interface{}{"name":"hello","age":18,"actived":false}) //使用`map`更新多个属性,只会更新这些更改的字段

db.Model(&user).Updates(User{Name:"hello", Age:18}) //使用`struct`更新多个属性,只会更新这些更改的和非空白字段

db.Model(&user).Updates(User{Name:"", Age:0, Actived:false}) //struct是不会更新 “ ”、0、false的,因为他们属于类型的空白值。struct不会更新空白值

(3)更新选择的字段

db.Model(&user).Select("name").Updates(map[string]interface{}{"name":"hello","age":18,"actived":false}) //也可以将Select改为Omit,再更新时更新或者忽略

(4)更新更改字段但不进行Callbacks

前面所说的更新操作会执行模型的BeforeUpdate, AfterUpdate方法,更新其UpdatedAt时间戳,在更新时保存它的Associations,如果不想调用它们,可以使用UpdateColumn, UpdateColumns

db.Model(&user).UpdateColumn("name","hello") //更新单/多个属性时与update类似

(5)Batch Updates 批量更新

Callbacks(回调函数在最后面做了笔记)在批量更新时不会运行

db.Table("users").Where("id IN (?)", []int{10,11}).Updates(map[string]interface{}{"name":"hello","age":18}) //就正常更新

db.Model(User{}).Updates(User{Name:"hello", Age:18}) //Struct更新:适用非零值,也可以用map[string]interface{}

db.Model(User{}).Updates(User{Name:"hello", Age:18}).RowsAffected //RowsAffected获取更新条数

(6)在Callbacks中更改更新值

如果要使用BeforeUpdate, BeforeSave更改回调中的更新值,可以使用scope.SetColumn,例如

func(user *User)BeforeSave(scope *gorm.Scope)(err error){

    if  pw, err := bcrypt.GenerateFromPassword(user.Password,0); err ==nil{

        scope.SetColumn("EncryptedPassword", pw)

    }

}

(7)额外更新选项

db.Model(&user).Set("gorm:update_option", "OPTION (OPTIMIZE FOR UNKNOWN)").Update("name, "hello") //添加额外的SQL

5.删除/软删除

(1)单个删除

删除记录时,需要确保其主要字段有值,GORM将使用主键删除记录,如果主要字段为空,GORM将删除模型的所有记录

db.Delete(&email) //删除存在的记录

(2)批量产出

db.Where("email LIKE ?","%mengyu%").Delete(Email{}) //删除所有email=mengyu的数据

db.Delete(Email{},"email LIKE ?","%jinzhu%") //也可以这么表达

(3)软删除

如果模型有DeletedAt字段,它将自动获得软删除功能。在调用Delete时不会从数据库中永久删除,只将字段DeletedAt的值设置为当前时间。

db.Where("age = ?",20).Delete(&User{}) //软删除了所有age=20的数据

db.Where("age = 20").Find(&user) //查询软删除(查询结果为null),软删除的数据查询时候被忽略

db.Unscoped().Where("age = 20").Find(&users) // Unscoped查找软删除记录

db.Unscoped().Delete(&order) //Unscoped永久删除

6.关联

默认情况下,当创建/更新记录时,GORM将保存其关联,如果关联具有主键,GORM将调用Update来保存它,否则将被创建。

(1)tag设置跳过保存关联

type User struct{ 

    gorm.Model

    Name  string

    CompanyID  uint

    Company  Company  `gorm:"save_associations:false"` //跳过保存关联

}

type Company struct{ 

      gorm.Model

      Name  string

}

7.Callbacks

回调方法:模型结构的指针,在创建,更新,查询,删除时将被调用,如果任何回调返回错误,gorm将停止未来操作并回滚所有更改。

(1)创建对象过程中可用的回调

// begin transaction 

开始事物BeforeSaveBeforeCreate

//save before associations 保存前关联

//update timestamp `CreatedAt`, `UpdatedAt` 更新`CreatedAt`, `UpdatedAt`时间戳

//save self 保存自己

//reload fields that have default value and its value is blank 重新加载具有默认值且其值为空的字段

//save after associations 保存后关联

AfterCreate

AfterSave

//commit or rollback transaction 提交或回滚事务

(2)更新对象过程中可用的回调

//begin transaction 

开始事物BeforeSaveBeforeUpdate

//save before associations 保存前关联

//update timestamp `UpdatedAt` 更新`UpdatedAt`时间戳

//save self 保存自己

//save after associations 保存后关联

AfterUpdate

AfterSave

//commit or rollback transaction 提交或回滚事务

(3)删除对象过程中可用的回调

//begin transaction 

开始事物BeforeDelete

//delete self 删除自己

AfterDelete

//commit or rollback transaction 提交或回滚事务

(4)查询对象过程中可用的回调

//load data from database 从数据库加载数据

//Preloading (edger loading) 预加载(加载)

AfterFind

(5)举个栗子~

func (u *User) BeforeUpdate() (err error){

       if u.readonly() { 

            err = errors.New("read only user")

       }

      return

}

func (u *User) AfterCreate() (err error) { //如果用户ID大于1000,则回滚插入

       if(u.Id >1000) {  

              err = errors.New("user id is already greater than 1000") 

        }

       return

}

gorm中的保存/删除操作正在事务中运行,因此在该事务中所做的更改不可见,除非提交。 如果要在回调中使用这些更改,则需要在同一事务中运行SQL。 所以你需要传递当前事务到回调,像这样:

func (u *User) AfterCreate (tx *gorm.DB) (err error) {

      tx.Model(u).Update("role","admin")

      return

}

func (u *User) AfterCreate (scope *gorm.Scope) (err error) {

       scope.DB().Model(u).Update("role","admin")

       return

}

你可能感兴趣的:(区块链Two——数据库(3)—Gorm)