• Golang的ORM框架之gorm


    前言

    ORM全称Object Relational Mapping,是把编程语言中的Object/Struct数据类型映射到关系数据库中1张表,以下是详细映射关系。

    结构体------------     数据表
    
    结构体实例----------    表中1条记录
    
    结构体字段------------  结构体字段

    gorm简介

    面向github编程找一找Golang中比较流行的orm,

    注意Django的orm是包含在Django web框架中的,而gorm有点像Python中的sqlalchemy它不限制你必须要在某个web框架中使用它。

    gorm中文官方网站内含十分齐全的中文文档。

    本文将围绕当前最近版本gorm v1.20.8展开。

    go.mod

    module golang-gorm
    
    go 1.14
    
    require (
        github.com/go-sql-driver/mysql v1.5.0
        gorm.io/driver/mysql v1.0.3
        gorm.io/gorm v1.20.8
    )

    gorm安装

    SET GOPROXY=https://goproxy.cn 
    go get -u github.com/jinzhu/gorm

    连接数据库

    在golang中连接不同的数据就需要使用不同的driver驱动。

    GORM 官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server

    "gorm.io/driver/mysql" //MySQL驱动

    "gorm.io/driver/sqlite"//sqllite的驱动

    "gorm.io/driver/postgres" //postgres的驱动

    开始

    package main
    
    import (
    	"fmt"
    	"gorm.io/driver/mysql" //MySQL驱动
    	"gorm.io/gorm"
    	"time"
    )
    
    func init() {
    	//配置数据连接:注意设置parseTime=true否则会报错!unsupported Scan 时间类型的字段
    	dbinfo := "zhanggen:xxoo@tcp(192.168.56.18:3306)/web?charset=utf8&parseTime=true&loc=Local"
    	db, err := gorm.Open(mysql.Open(dbinfo), &gorm.Config{})
    	//配置一下数据连接参数!
    	mySQL, err := db.DB()
    	if err != nil {
    		fmt.Println(err)
    	}
    	defer mySQL.Close()
    	//设置最大空闲连接
    	mySQL.SetMaxIdleConns(10)
    	//设置最大连接数
    	mySQL.SetMaxOpenConns(100)
    	//设置连接超时时间:1分钟
    	mySQL.SetConnMaxLifetime(time.Minute)
    
    }
    

      

    Model定义

    我们在创建Table的时可以通过设置struct字段的tag,对数 表名、字段进行属性设置。

    结构体

    package book
    
    import "time"
    
    type Book struct {
    	ID          uint       `gorm:"column:id;primaryKey"`
    	Title       string     `gorm:"column:title;not null;type:varchar(100);unique_index"` //指定数据库列的数据类型
    	Author      string     `gorm:"column:author;not null;size:125"`             //设置Author在数据库中不能为空,字段大小为255
    	Publishtime *time.Time `gorm:"column:publishtime"`
    }
    // 将默认表面Book 的表名设置为book
    func (Book) TableName() string {
    	return "book"
    }
    

    --------------------------------

    package login
    
    //用户表
    type UserInfo struct {
    	ID       uint   `gorm:"column:id;primaryKey"`
    	Username string `gorm:"column:username;unique;not null;index"`
    	Password string `gorm:"column:password;not null;"`
    }
    
    // 将 UserInfos 的表名设置为 `user`
    func (UserInfo) TableName() string {
    	return "user"
    }
    

      

    Mysql

    MariaDB [web]> desc book;
    +-------------+---------------------+------+-----+---------+----------------+
    | Field       | Type                | Null | Key | Default | Extra          |
    +-------------+---------------------+------+-----+---------+----------------+
    | id          | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
    | title       | varchar(100)        | NO   |     | NULL    |                |
    | author      | varchar(125)        | NO   |     | NULL    |                |
    | publishtime | datetime(3)         | YES  |     | NULL    |                |
    +-------------+---------------------+------+-----+---------+----------------+
    4 rows in set (0.00 sec)

    -------------------------------------

    MariaDB [web]> desc user;
    +----------+---------------------+------+-----+---------+----------------+
    | Field    | Type                | Null | Key | Default | Extra          |
    +----------+---------------------+------+-----+---------+----------------+
    | id       | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
    | username | varchar(191)        | NO   | UNI | NULL    |                |
    | password | longtext            | NO   |     | NULL    |                |
    +----------+---------------------+------+-----+---------+----------------+
    3 rows in set (0.00 sec)

    Django orm的差别

    当我们Django的model中修改了表字段需要先make magrations生成操作,然后再make magirate去数据库真正执行这次magration。保证了数据库的安全。

    而gorm只要你执行db.AutoMigrate()就会实时修改表结构,但是当你使用Django orm 修改了数据库字段magration时经常报错Exist table让你不知所措。

    查看gorm操作日志 

    db.Debug可以帮助我们显示gorm对数据库的操作

    db.Debug().AutoMigrate(&User{})

    日志内容

    [1.000ms] [rows:0] ALTER TABLE `users` MODIFY COLUMN `age` bigint DEFAULT 19

    增删改查CURD

    立即执行方法(Immediate Methods):查询只有执行了立即执行函数才会去数据库查询 First/Find/FirstrOrInit/FirstOrCreate/

    内联条件 inline condition:就是把SQL查询条件当做参数传递到立即执行函数中执行。

    gorm支持使用String、Struct&Map作为条件进行查询已经链样操作。

    创建单条记录

      /1.创建单条记录
        u := User{Name: "Martin", Age: 19}
        result := Model.Create(&u)
        //返回插入数据的主键
        fmt.Println(u.ID)
        //返回的error
        fmt.Println(result.Error)
        //返回插入记录的条数
        fmt.Println(result.RowsAffected)
        //如果记录存在将不再重复创建
        result = Model.FirstOrCreate(&u)
        if result.RowsAffected == 0 {
            fmt.Println("记录已经存在!")
        }

    批量创建

    在生产环境之中如果你插入数据特别频繁,可以使用批量插入的方式进行优化。如果数据没有实时展示的要求。

        //批量创建
        var userList = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}
        Model.Debug().Create(&userList)
        for _, user := range userList {
            fmt.Println(user.ID) // 1,2,3
        }

     查询

    new和make区别:make给map/channel/slince申请内存和容量返回值类型,new初始化基本类型string/int/结构体返回指针类型的变量

    主键查询

    Objects can be retrieved using primary key by using Inline Conditions.

    Be extra careful with strings to avoid SQL Injection, check out Security section for details 

    通过内联条件的方式,我们可以根据主键获取到数据库中的1/多条记录。

    db.First(&user, 10)
    // SELECT * FROM users WHERE id = 10;
    
    db.First(&user, "10")
    // SELECT * FROM users WHERE id = 10;
    
    db.Find(&users, []int{1,2,3})
    // SELECT * FROM users WHERE id IN (1,2,3);

    一般查询

        //1.普通查询
        u := new(User)
        //查询第1条数据
        Model.Debug().First(u) //SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1
        fmt.Println(u.Name)
        Model.Debug().First(u, 10)
        fmt.Println(u)
        //随机获取1条记录
        u = new(User)         //new和make区别:make给map/channel/slince申请内存和容量返回值类型,new初始化基本类型string/int/结构体返回指针类型的变量
        Model.Debug().Take(u) //SELECT * FROM `users` LIMIT 1
        fmt.Println(u.Name)
        //获取最后1条记录
        u = new(User)
        Model.Last(u)
        fmt.Println(u.Name)
        //查询表中所有记录
        userList := []User{} //SELECT * FROM `users`
        Model.Debug().Find(&userList)
        fmt.Println(userList)

    Where 条件查询

        //查询所有匹配的记录 然后获取第1条
        u=new(User)
        Model.Debug().Where("name=?","jinzhu1").First(&u)//SELECT * FROM `users` WHERE name='jinzhu1' ORDER BY `users`.`id` LIMIT 1
        fmt.Println(u.Name)
        //查询所有匹配的记录,获取所有
        userList=[]User{}
        Model.Debug().Where("name=?","Martin").Find(&userList)  //SELECT * FROM `users` WHERE name='Martin'
        fmt.Println(userList)
    
        userList=[]User{}
        Model.Debug().Where("name <>?","Martin").Find(&userList) //SELECT * FROM `users` WHERE name <>'Martin'
        fmt.Println(userList)
    
        userList=[]User{}
        Model.Debug().Where("name in?",[]string{"jinzhu"}).Find(&userList) //SELECT * FROM `users` WHERE name in('jinzhu')
        fmt.Println(userList)

    使用Struct & Map & 切片进行查询

    我们可使用结构体、map、slice把搜索条件组合起来。

        //struct
        u = new(User)
        Model.Debug().Where(User{Name: "曹操", Age: 29}).First(&u)
        fmt.Println(u.Name)
        userList=[]User{}
        //map:我们可以使用map接收前端的json然后做组合搜索
        Model.Debug().Where(map[string]interface{}{"name": "希拉里", "age": 29}).Find(&userList) //SELECT * FROM `users` WHERE `users`.`name` = '曹操' AND `users`.`age` = 29 ORDER BY `users`.`id` LIMIT 1
        fmt.Println(userList)
        //主键的切片
        userList=[]User{}
        Model.Where([]int64{1,2,4,6}).Find(&userList)
        fmt.Println(userList)

    Not 条件查询

    我们可以使用Where指定筛选条件,也可以使用Not排除一些条件。

        //not条件查询
        u = new(User)
        userList = []User{}
        //1.不等于 <>
        //SELECT * FROM `users` WHERE `name` <> 'jinzhu' ORDER BY `users`.`id`
        Model.Debug().Not("name", "jinzhu").First(&u)
        fmt.Println(u.Name)
        //SELECT * FROM `users` WHERE `name` <> 'Martin'
        Model.Debug().Not("name", "Martin").Find(&userList)
        fmt.Println(userList)
        //2.不在x范围之类 Not In
        userList = []User{}
        //SELECT * FROM `users` WHERE `users`.`id` NOT IN (1,4)
        Model.Debug().Not([]int64{1, 4}).Find(&userList)
        fmt.Println(userList)
        //SELECT * FROM `users`
        userList = []User{}
        Model.Debug().Not([]int64{}).Find(&userList)
        fmt.Println(userList)
        //4.执行原生Sql
        userList = []User{}
        Model.Debug().Not("name=? or age<10", "Martin").Find(&userList)
        fmt.Println(userList)
        //5.not和where一样同样支持 通过struct/map/切片来组装搜索条件
        userList = []User{}
        Model.Debug().Not(User{Name: "Martin", Age: 19}).Find(&userList)
        fmt.Println(userList)
    
        userList = []User{} //SELECT * FROM `users` WHERE (`age` <> 19 AND `name` <> 'jinzhu')
        Model.Debug().Not(map[string]interface{}{"name": "jinzhu", "age": 19}).Find(&userList)
        fmt.Println(userList)
    
        userList = []User{}
        Model.Debug().Not([]int64{1, 2}).Find(&userList) //SELECT * FROM `users` WHERE `users`.`id` NOT IN (1,2)
        fmt.Println(userList)

    Or条件查询

    我可以使用Or连接2个搜索条件。

    //Or条件
        userList = []User{}
        Model.Where("name=?", "Martin").Or("name=?", "jinzhu").Find(&userList)
        fmt.Println(userList)
        //Or也支持Struct、map、slice进行条件组成搜索
        userList = []User{}
        //SELECT * FROM `users` WHERE `users`.`name` = 'Martin' AND `users`.`age` = 29 OR (`users`.`name` = 'jinzhu' AND `users`.`age` = 29)
        Model.Debug().Where(User{Name: "Martin", Age: 29}).Or(User{Name: "jinzhu", Age: 29}).Find(&userList)
        fmt.Println(userList)
        //SELECT * FROM `users` WHERE `users`.`id` IN (1,2) OR `users`.`id` IN (5,4)
        Model.Debug().Where([]int64{1, 2}).Or([]int64{5,4}).Find(&userList)
        fmt.Println(userList)

    内联条件查询

    通过以上大量的实验你会发现我总会在Where/Not/Or里面组织搜索条件,然后再 .First/Find。

    这是Where/Not/Or中的SQL需要借助立即执行方法 才真正把SQL发送到mysqld(服务端)执行,然后返回值,我们声明的变量才会获取到值。

    您会发现.Find()和.First()t这些立即执行方法可以接收2个参数(dest interface{}, conds ...interface{})

    第1个参数用于接收和保存服务端(mysqld)返回的结果、第2个用于客户端(MySQL)输入SQL搜索条件。

    什么是内联条件查询呢?

    就是我们在把接收查询结果的变量、SQL查询条件传递到.Find()/.First()/.Not()/.Or()这些立即执行函数中执行就属于内联条件查询。

    //内联条件
        //1.根据主键获取记录 (只适用于整形主键)
        u1 := new(User)
        Model.Debug().First(&u1, 1)
        fmt.Println(u1.Age)
        u1 = new(User)
        Model.Debug().First(&u1, "id = ?", 1)
        fmt.Println(u1.Name)
        //2.根据搜索条件获取多条数据
        users := []User{}
        Model.Debug().Find(&users, "name = ?", "jinzhu")
        fmt.Println(users)
        //3.使用结构体作为搜索参数
        users = []User{}
        Model.Debug().Find(&users, User{Age: 29})
        fmt.Println(users)
        //4.使用map组织搜索条件
        users = []User{}
        Model.Debug().Find(&users, map[string]interface{}{"age": "29"})
        fmt.Println(users)
        //5.使用slice作为搜索条件
        users=[]User{}
        Model.Debug().Find(&users,[]int64{1,6})
        fmt.Println(users)

    设置查询选项

    GORM 提供了 SetGetInstanceSetInstanceGet 方法来允许用户传值给 勾子 或其他方法

    Gorm 中有一些特性用到了这种机制,如迁移表格时传递表格选项。

        err = db.Debug().Set("gorm:table_options","ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1").AutoMigrate(&User{})
        if err != nil {
            fmt.Println("创建表失败",err)
        }
        fmt.Println("---------------------","创建表成功")

    我们在查询 SQL是可以设置添加1个行锁(在我查询这条记录的时候别人就无法修改这条数据了)

    users=[]User{}
    Model.Debug().Set("gorm:query_option","FOR UPDATE").Find(&users,[]int64{1,5})
    fmt.Println("--------",users)
    u1=new(User)
    Model.Set("gorm:query_option","FOR UPDATE").Debug().First(&u1,10)
    fmt.Println(u1.Name)
    

      

    FirstOrInit查询

    gorm的核心功能就是把数据库返回的1条记录转换成golang中的结构体并对字段进行赋值。

    如果在某些情况下我的SQL查询没有返回任何记录,那我的stuct变量的全部字段就会使用零值

    可以使用 Attrs和Assign方法对 FirstOrInit返回的空记录 进行struct字段进行赋值。

    attrs:获取匹配的第1条记录,否则根据给定的条件初始化一个新的对象 (仅支持 struct 和 map 条件)

    Assign:不管记录是否找到,都将参数赋值给 struct变量。

      //FirstOrint
        //如果找到了就把数据库返回的结果赋值给u2,如果没有找到也不要紧把查询条件赋值给u2进行初始化。
        u2:=new(User)
        Model.FirstOrInit(u2,User{Name: "Martin"})
        fmt.Println(u2.Name)
        //在数据库没有返回查询结果的情况下:使用Attrs指定Init时字段的值
        u2=new(User)
        Model.Attrs(User{Age:29} ).FirstOrInit(u2,User{Name: "Bob"})
        fmt.Println(u2.Name,u2.Age,u2.ID)
        //Assigin:不管数据库有没有返回查询结果,在会初始化指定的字段
        u2=new(User)
        Model.Debug().Assign(User{Age: 21}).FirstOrInit(u2,User{Name:"抱墙"})
        fmt.Println(u2.Name,u2.Age)

    FirstOrCreate插入数据

    Attrs和Assign不仅可以对声明的结构体变量进行初始化,还可以指定FirstOrCreate将要创建的记录。

    FirstOrCreate:获取匹配的第1条记录, 否则根据给定的条件创建一个新的记录 (仅支持 struct 和 map 条件)

    Attrs:如果记录未找到,将使用attr中设置的参数创建 struct 和新记录.

    Assign:不管记录是否找到,都将参数赋值给 struct 并保存至数据库.

        //FirstOrCreate
        u3 := new(User)
        //如果SQL条件匹配到了记录就返回原有记录赋值给结构体变量。
        Model.Debug().FirstOrCreate(u3, User{Name: "嬴政"})
        fmt.Println(u3.Age, u3.ID)
        //如果SQl查询没有匹配到记录就 insert 1条新记录,返回新记录赋值给结构体变量。
        u3 = new(User)
        Model.Debug().Where(User{Name: "嬴胡亥"}).FirstOrCreate(u3)
        fmt.Println(u3.ID)
    
        //Attrs
        //如果SQL匹配到记录,返回原记录并赋值给结构体变量。(不使用Attrs(User{Age: 20})中设置的参数)
        u3 = new(User)
        Model.Debug().Where(User{Name: "嬴胡亥"}).Attrs(User{Age: 20}).FirstOrCreate(u3)
        fmt.Println(u3.Name, u3.Age)
        //如果SQL没有匹配到记录,就使用Where(User{Name: "匹配不到嬴胡亥用户"})和.Attrs(User{Age: 20})中的参数, insert 1条新记录,返回新记录赋值给结构体变量。
        u3 = new(User)
        Model.Debug().Where(User{Name: "匹配不到嬴胡亥用户"}).Attrs(User{Age: 2000}).FirstOrCreate(u3)
        fmt.Println(u3.Name, u3.ID)
    
        //Assign
        //SQL匹配到记录就更新原记录(update not insert new record)
        u3 = new(User)
        Model.Debug().Where(User{Name: "嬴胡亥"}).Attrs(User{Age: 20}).FirstOrCreate(u3)
        fmt.Println(u3.Name, u3.Age)
        //没有匹配到就使用Where(User{Name: "匹配不到嬴胡亥用户"})和.Attrs(User{Age: 20})中的参数,  insert new record。
        u3 = new(User)
        Model.Debug().Where(User{Name: "我不存在"}).Assign(User{Age: 90}).FirstOrCreate(u3)
        fmt.Println(u3.Name, u3.Age)

    子查询

    db.Where("amount > ?", db.Table("orders").Select("AVG(amount)").Where("state = ?", "paid").SubQuery()).Find(&orders)
    // SELECT * FROM "orders"  WHERE "orders"."deleted_at" IS NULL AND (amount > (SELECT AVG(amount) FROM "orders"  WHERE (state = 'paid')));
    

    Select选择字段

    Select,指定你想从数据库中检索出的字段,默认会选择全部字段。

          //Select,指定你想从数据库中检索出的字段,默认会选择全部字段。
    	users := []User{}
    	//SELECT id,name FROM `users`
    	Model.Debug().Select("id,name").Find(&users)
    	fmt.Println(users)
    	users = []User{}
    	//SELECT `name`,`age` FROM `users`
    	Model.Debug().Select([]string{"name","age"}).Find(&users)
    	fmt.Println(users)
    	users=[]User{}
    	Model.Debug().Table("users").Select("COALESCE(age,?)", 200)
    	fmt.Println(users)    
    

      

    排序查询

     我们可以通过Order api对查询的数据进行排序

    	userList:=[]User{}
    	//SELECT * FROM `users` ORDER BY age desc, name
    	Model.Debug().Order("age desc, name").Find(&userList)
    	fmt.Println(userList)
    	// 多字段排序
    	userList=[]User{}
    	Model.Debug().Select("id,age").Order("id desc").Order("age").Find(&userList)
    	fmt.Println(userList)
    

      

    Limit & Offset

    分页查询Limit specify the max number of records to retrieve Offset specify the number of records to skip before starting to return the records

        //limit and offset
    	//返回3条数据
    	userList:=[]User{}
    	Model.Limit(3).Find(&userList)
    	fmt.Println(userList)
    	// 通过 -1 消除 Limit 条件
    	Model.Limit(3).Find(&userList).Limit(-1).Find(&userList)
    	fmt.Println(userList)
    	//移动到ID=2的记录获取2条,分页就是这样搞的!
    	Model.Debug().Offset(2).Limit(2).Find(&userList)
    	fmt.Println(userList)
    

      

    count获取查询总量

    Count,该 model 能获取的记录总数。Count 必须是链式查询的最后一个操作 ,因为它会覆盖前面的 SELECT,但如果里面使用了 count 时不会覆盖

    count1:=new(int64)
    Model.Debug().Find(&userList).Count(count1)
    fmt.Println(*count1)
    

      

    Group&Having 

    分组和聚合

         //group by: SELECT name,sum(age) as total FROM `users` GROUP BY `name`
    	type groupByage struct {
    		Age  int
    		Total int
    	}
    	groupList :=[]groupByage{}
    	Model.Debug().Model(&User{}).Select("age,count(name) as total").Group("age").Find(&groupList)
    	fmt.Printf("%#v
    ", groupList)
    
    
    	//haveing可以对group by 之后的结果,进行进一步筛选。
    	//SELECT name,sum(age) as total FROM `users` WHERE name LIKE '刘%' GROUP BY `name` HAVING total >= 50
    	type groupByname struct {
    		Name  string
    		Total int
    	}
    	groupList1:=[]groupByname{}
    	Model.Debug().Model(&User{}).Select("name,sum(age) as total").Where("name LIKE ?", "刘%").Group("name").Having("total >= ?",50).Find(&groupList1)
    	fmt.Println(groupList1)
    

      

    join连表

    type result struct {
      Name  string
      Email string
    }
    db.Model(&User{}).Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&result{})
    // SELECT users.name, emails.email FROM `users` left join emails on emails.user_id = users.id
    
    rows, err := db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Rows()
    for rows.Next() {
      ...
    }
    
    db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&results)
    
    // 带参数的多表连接
    db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Where("credit_cards.number = ?", "411111111111").Find(&user)
    

      

    Pluck

    Query single column from database and scan into a slice, if you want to query multiple columns, use Select with Scan instead

    Pluck可以帮助我们获取到table中的某1列数据并赋值给slice。

            //pluck获取age列做位切片返回
    	var ageList []int64
    	Model.Debug().Model(&User{}).Pluck("age",&ageList)
    	fmt.Println(ageList)
    
    	nameList:=new([]string)
    	Model.Debug().Model(&User{}).Pluck("name",&nameList)
    	fmt.Println(nameList)        
    

    Scan

    Scan results into a struct work similar to Find

    Scan的功能类似于Find,可以把从数据库中查询到的结果映射到struct中。

    //scan的用法
        type scanResult struct {
            Name string
            Age  int64
        }
        //类似于First()的功能
        scanResultObj := new(scanResult)
        Model.Table("users").Select("name,age").Where("name = ?", "嬴政").Scan(scanResultObj)
        fmt.Println(scanResultObj)
        //类似于Find()功能
        scanResultList := new([]scanResult)
        Model.Table("users").Select("name,age").Where("name like ?", "刘%").Scan(scanResultList)
        fmt.Println(scanResultList)

    Scope查询

    Scopes allow you to re-use commonly logic, the shared logic needs to defined as type func(*gorm.DB) *gorm.DB

    Scopes可以帮助我们把常用的查询逻辑封装到1个函数里面,后期使用查询时Scope这个函数。

    //名字不为空
    func NameNotNull(db *gorm.DB) *gorm.DB {
        return db.Where("name is not null")
    }
    
    //查询年龄>=18岁的用户
    func AgeLe18(db *gorm.DB) *gorm.DB {
        return db.Where("age >= ?", 18)
    }
    
    //分页查询
    func PageData(start, count int) func(db *gorm.DB) *gorm.DB {
        return func(db *gorm.DB) *gorm.DB {
            return db.Offset(start).Limit(count)
        }
    }
    
        //scope
        //查询姓名不为空 年龄>=18
        userList := new([]User)
        Model.Scopes(NameNotNull, AgeLe18).Find(userList)
        fmt.Println(userList)
        //分页查询
        Model.Scopes(PageData(5,10)).Find(userList)
        fmt.Println(userList)

    链式操作相关

     Django的Q查询可以做组合查询,gorm中的链式操作也可以。

        var userlist []User
        contionChain:=Model.Debug().Not("name is null")
        contionChain.Where("name like ?","刘%")
        contionChain.Where("age >?",10)
        contionChain.Find(&userlist)
        fmt.Println(userlist)

    更新 update

    Save更新所有字段

      var userObj User
        Model.First(&userObj)
        fmt.Println(userObj)
        //save更新: 默认会修改所有字段 
        userObj.Age=90
        //UPDATE `users` SET `id`=1,`created_at`='2020-12-24 05:29:33.978',`updated_at`='2020-12-24 13:35:08.695',`deleted_at`=NULL,`name`='特朗普',`age`=90,`active`=true WHERE `id` = 1
        Model.Debug().Save(userObj)

    Update更新1个字段

    Model(&userObj)我在Model中能传了1个userObj,gorm会根据userObj中的id字段去数据库where需要更新的记录。

      //update:更新指定的字段
        //根据结构体更新 1个字段
        Model.Debug().Model(&userObj).Update("name","唐纳德.特朗普")
        //根据给定的条件更新 1个字段
        Model.Debug().Model(&userObj).Where("name = ?","唐纳德.特朗普").Update("age",10)

    Updates同时更新多个字段

    我们可以给update方法传1个map,同时更新多个字段。

    //更新多个字段 使用struct
        m1:=map[string]interface{}{
            "name":"唐纳德.特朗普",
            "age":20,
        }
        //UPDATE `users` SET `age`=20,`name`='唐纳德.特朗普',`updated_at`='2020-12-24 13:50:07.519' WHERE `id` = 1
        //注意:Model(&userObj)我在Model中能传来1个userObj,gorm会根据userObj中的id字段去where需要更新的值
        Model.Debug().Model(&userObj).Updates(m1)

    Updates更新指定的字段

    有时我们接收到的map可能会包含一些我们不需要更新的字段,那么如何指定有效字段、排除无效字段呢?

        //updates:更新指定字段
        //指定age字段进行更新
        Model.Debug().Model(&userObj).Select("age").Updates(map[string]interface{}{"name":"唐纳德.-特朗普","age":9000,"active":false})
        //排除age字段进行更新
        Model.Debug().Model(&userObj).Omit("age").Updates(map[string]interface{}{"name":"唐纳德.-特朗普","age":9000,"active":false})
        

    UpdateColumns 

    UpdateColumns也叫无Hooks更新。

    上面的更新操作会自动运行 model 的 BeforeUpdateAfterUpdate 方法。

    更新 UpdatedAt 时间戳, 在更新时保存其 Associations, 如果你不想调用这些方法,你可以使用 UpdateColumn, UpdateColumns就不会更新UpdatedAt字段。

    //UpdateColumn
        Model.Debug().Model(&userObj).Update("name", "唐纳德.特朗普")
        Model.Debug().Model(&userObj).UpdateColumns(map[string]interface{}{
            "name": "唐纳德.-特朗普",
            "age":  200,
        })
        Model.Debug().Model(&userObj).UpdateColumns(User{Name:"唐纳德.特朗普",Active: true})
        defer connectionPool.Close()

    批量更新多条记录

     注意在批量更新的时候不会更新 UpdatedAt 字段。

        //批量更新
        AffectedRows:=Model.Debug().Table("users").Where("id in (?)", []int{1, 2}).Updates(map[string]interface{}{
            "age": 21,
        }).RowsAffected
        fmt.Println("更新的行数",AffectedRows)

    全表更新

    我想要1张表中的某个字段全部更新怎么办?还记得Django中的F在原数据的基础上+1?

      //全局更新
        //错误的全局更新方式,之前是可以的!!
        err := Model.Model(&User{}).Update("name", "二狗").Error // 错误的写法!!gorm.ErrMissingWhereClause
        fmt.Println(err)
        //全局更新方式1
        Model.Debug().Exec("UPDATE users SET age = ?", "18")
        //让user表中用户年龄在原来的基础上+2,Expr 子查询
        Model.Debug().Session(&gorm.Session{AllowGlobalUpdate: true}).Model(User{}).Update("age", gorm.Expr("age+ ?", 100))

    删除delete

    最后就是删除了~

    删除1条记录

    我跟可以根据对象删除一条记录,注意必须指定对象的主键否则会触发 批量 Delete,也可以根据主键直接删除1条记录。

      //根据对象删除:删除对象需要指定主键,否则会触发 批量 Delete
        u:=new(User)
        u.ID=1
        Model.Debug().Delete(u)
        //根据主键删除
        Model.Debug().Delete(&User{},10)
        //根据主键删除多个
        Model.Debug().Delete(&User{},[]int{4,11})    

    批量删除

    gorm中都是软删除。

        //2.批量删除
        deletedUsers:=[]User{}
        Model.Debug().Where("name like ?","唐纳德%").Delete(User{})
        Model.Debug().Delete(deletedUsers,"name like ?","嬴%")
        fmt.Println(deletedUsers)

    全表删除

     清空表中的内容

    //全局删除
        //1.错误的方式
        err:=Model.Delete(&User{}).Error
        fmt.Println(err)
        //正确方式
        Model.Where("1 = 1").Delete(&User{})
        Model.Exec("truncate table users")
        Model.Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&User{})

    从别人创建的表中导出model 

    参考

  • 相关阅读:
    js Excel导出
    计算机原理
    Sql Server 连接池
    转/ C# 托管资源和非托管资源
    求两个时间相差多少时间
    计算机基础
    MyEclipse CI 2018.9.0正式发布(附下载)
    DevExpress v18.1新版亮点——ASP.NET Bootstrap篇(二)
    DevExpress v18.1新版亮点——ASP.NET Bootstrap篇(一)
    DevExpress v18.1新版亮点——ASP.NET篇(四)
  • 原文地址:https://www.cnblogs.com/sss4/p/14163524.html
Copyright © 2020-2023  润新知