go的orm框架-Gorm

news2024/11/28 6:32:01

官网文档

特点

  • 全功能 ORM
  •  关联 (拥有一个,拥有多个,属于,多对多,多态,单表继承)
  •  Create,Save,Update,Delete,Find 中钩子方法
  •  支持 Preload、Joins 的预加载
  •  事务,嵌套事务,Save Point,Rollback To to Saved Point
  •  Context、预编译模式、DryRun 模式
  •  批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD
  •  SQL 构建器,Upsert,锁,Optimizer/Index/Comment Hint,命名参数,子查询
  •  复合主键,索引,约束
  •  自动迁移
  •  自定义 Logger
  •  灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…
  •  每个特性都经过了测试的重重考验
  •  开发者友好

约定(重点)

主键

GORM 使用一个名为ID每个模型的默认主键的字段。

type User struct {
  ID   string // 默认情况下,名为 `ID` 的字段会作为表的主键
  Name string
}

可以通过标签 primaryKey 将其它字段设为主键 

// 将 `UUID` 设为主键
type Animal struct {
  ID     int64
  UUID   string `gorm:"primaryKey"`
  Name   string
  Age    int64
}

表名称

默认情况下,GORM 将结构名称转换为snake_case表名称并将其复数化。例如,一个User结构体出现users在数据库中。

您可以实现 Tabler 接口来更改默认表名,例如:

type Tabler interface {
    TableName() string
}

// TableName 会将 User 的表名重写为 `profiles`
func (User) TableName() string {
  return "profiles"
}

注意: TableName 不支持动态变化,它会被缓存下来以便后续使用。想要使用动态表名,你可以使用 Scopes,例如: 

func UserTable(user User) func (tx *gorm.DB) *gorm.DB {
  return func (tx *gorm.DB) *gorm.DB {
    if user.Admin {
      return tx.Table("admin_users")
    }

    return tx.Table("users")
  }
}

db.Scopes(UserTable(user)).Create(&user)

临时指定表名

您可以使用 Table 方法临时指定表名,例如:

// 根据 User 的字段创建 `deleted_users` 表
db.Table("deleted_users").AutoMigrate(&User{})

// 从另一张表查询数据
var deletedUsers []User
db.Table("deleted_users").Find(&deletedUsers)
// SELECT * FROM deleted_users;

db.Table("deleted_users").Where("name = ?", "jinzhu").Delete(&User{})
// DELETE FROM deleted_users WHERE name = 'jinzhu';

列名

GORM 自动将结构体字段名转换为snake_case数据库中的列名。

type User struct {
  ID        uint      // 列名是 `id`
  Name      string    // 列名是 `name`
  Birthday  time.Time // 列名是 `birthday`
  CreatedAt time.Time // 列名是 `created_at`
}

您可以使用 column 标签或 命名策略 来覆盖列名 

type Animal struct {
  AnimalID int64     `gorm:"column:beast_id"`         // 将列名设为 `beast_id`
  Birthday time.Time `gorm:"column:day_of_the_beast"` // 将列名设为 `day_of_the_beast`
  Age      int64     `gorm:"column:age_of_the_beast"` // 将列名设为 `age_of_the_beast`
}

 你可以通过将 autoCreateTime 标签置为 false 来禁用时间戳追踪,例如:

type User struct {
  CreatedAt time.Time `gorm:"autoCreateTime:false"`
}

时间戳字段

GORM 使用名为CreatedAt和的字段UpdatedAt来自动跟踪记录的创建和更新时间。

对于有 CreatedAt 字段的模型,创建记录时,如果该字段值为零值,则将该字段的值设为当前时间

db.Create(&user) // 将 `CreatedAt` 设为当前时间

user2 := User{Name: "jinzhu", CreatedAt: time.Now()}
db.Create(&user2) // user2 的 `CreatedAt` 不会被修改

// 想要修改该值,您可以使用 `Update`
db.Model(&user).Update("CreatedAt", time.Now())

 对于有 UpdatedAt 字段的模型,更新记录时,将该字段的值设为当前时间。创建记录时,如果该字段值为零值,则将该字段的值设为当前时间

db.Save(&user) // 将 `UpdatedAt` 设为当前时间

db.Model(&user).Update("name", "jinzhu") // 会将 `UpdatedAt` 设为当前时间

db.Model(&user).UpdateColumn("name", "jinzhu") // `UpdatedAt` 不会被修改

user2 := User{Name: "jinzhu", UpdatedAt: time.Now()}
db.Create(&user2) // 创建记录时,user2 的 `UpdatedAt` 不会被修改

user3 := User{Name: "jinzhu", UpdatedAt: time.Now()}
db.Save(&user3) // 更新时,user3 的 `UpdatedAt` 会修改为当前时间

 你可以通过将 autoUpdateTime 标签置为 false 来禁用时间戳追踪,例如:

type User struct {
  UpdatedAt time.Time `gorm:"autoUpdateTime:false"`
}

安装

要在有mod文件的文件夹下面执行下面的命令

go get -u gorm.io/gorm

连接到数据库(mysql)

安装mysql驱动

go get -u gorm.io/driver/mysql

 

编写测试连接 

基础版

package main
import (
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)
func main() {
	// 参考 root:123456@tcp(192.168.31.131:3306)/gotest
	dsn := "root:123456@tcp(192.168.31.131:3306)/gotest?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Println("连接失败")
		return
	}
	fmt.Println("连接成功", db)
}

高级版

package main
import (
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)
func main() {
	// 参考 root:123456@tcp(192.168.31.131:3306)/gotest
	dsn := "root:123456@tcp(192.168.31.131:3306)/gotest?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.New(mysql.Config{
		DSN:                       dsn,   // DSN data source name
		DefaultStringSize:         256,   // string 类型字段的默认长度
		DisableDatetimePrecision:  true,  // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
		DontSupportRenameIndex:    true,  // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
		DontSupportRenameColumn:   true,  // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
		SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
	}), &gorm.Config{})
	if err != nil {
		fmt.Println("连接失败")
		return
	}
	fmt.Println("连接成功", db)
}

 crud

准备数据库和结构体

package main
import (
	"errors"
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)
type Stu struct {
	Id      int `gorm:"primaryKey"`
	Name    string
	Age     int
	Address string
}

func getDb() *gorm.DB {
	dsn := "root:123456@tcp(192.168.31.131:3306)/gotest?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Println("连接失败")
		panic(errors.New("连接失败"))
	}
	fmt.Println("连接成功", db)
	return db
}

注意结构体中的属性首字母要大写,否则就不可见,还要指定主键 

查询

需要带条件的可以自己查看api

查询一个

first
 获取第一条记录(主键升序)
func main() {
	db := getDb()
	testFirst(db)
}
func testFirst(db *gorm.DB) {
	var stu Stu
	res := db.Table("stu").First(&stu)
	if res.Error != nil {
		fmt.Println("查询数据失败")
		return
	}
	fmt.Println(stu)
}

Take
获取一条记录,没有指定排序字段
func testTake(db *gorm.DB) {
	var stu Stu
	res := db.Table("stu").Take(&stu)
	if res.Error != nil {
		fmt.Println("查询数据失败")
		return
	}
	fmt.Println(stu)
}
Last
获取最后一条记录(主键降序)
func testLast(db *gorm.DB) {
	var stu Stu
	res := db.Table("stu").Last(&stu)
	if res.Error != nil {
		fmt.Println("查询数据失败")
		return
	}
	fmt.Println(stu)
}

批量查询

func testMany(db *gorm.DB) {
	var stus = make([]Stu, 0)
	//相当于条件是id为10
	_ = db.Table("stu").Find(&stus)
	fmt.Println(stus)
}

新增

如果表不存在,会创建表

新增一个

func testAddOne(db *gorm.DB) {
	stu := Stu{Name: "新增名称", Age: 11, Address: "新增地址"}
	tx := db.Table("stu").Create(&stu)
	if tx.Error != nil {
		fmt.Println("新增失败")
		return
	}
	fmt.Println(stu.Id)
}

批量新增

func testAddMany(db *gorm.DB) {
	stus := []Stu{{Name: "批量新增名称1", Age: 11, Address: "批量新增地址1"}, {Name: "批量新增名称2", Age: 11, Address: "批量新增地址2"}}
	tx := db.Table("stu").Create(&stus)
	if tx.Error != nil {
		fmt.Println("新增失败")
		return
	}
	fmt.Println(stus)
}

更新

更新一个

func testUpdateOne(db *gorm.DB) {
	var stu Stu
	_ = db.Table("stu").First(&stu)
	stu.Name = "更新后名字"
	db.Table("stu").Save(&stu)
}
func testUpdateOne1(db *gorm.DB) {
	db.Table("stu").Where("id=?", 72).Update("name", "更新").Update("address", "跟新地址")
}

批量更新

func testUpdateMany(db *gorm.DB) {
	db.Table("stu").Where("id in (?)", []int{1,2,3,43}).Update("name", "更新").Update("address", "跟新地址")
}

删除

func testDeleteMany(db *gorm.DB) {
	db.Table("stu").Where("id in (?)", []int{1, 2, 3, 43}).Delete(&Stu{})
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1576991.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue.js【组件基础(上)】

选项式API和组合式API 选项式API 选项式API是一种通过包含多个选项的对象来描述组件逻辑的API,其常用的选项包括data、methods、computed、watch等。 组合式API 相比于选项式API,组合式API是将组件中的数据、方法、计算属性、侦听器等代码全部组合在…

sigmoid函数实例

sigmoid实例案例:(sigmoid作用就是把传统线性回归中的z 投射到分类问题(0,1)的概率中) 交叉熵损失函数扩充解释: 为什么二分类和多分类的不一样 因为在二分类问题中 每个样本得值是0或1 一组数据…

使用spring模拟转账,并实现异常事务回滚

1、数据库准备 使用配置类配置数据源、模板、事务回滚 package cn.edu.aaa.utils;import java.beans.PropertyVetoException;import javax.sql.DataSource;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotati…

iterrows方法使dataframe显示完整

这样一个excel文件,由于行数太多显示不全。 直接读取显示: 该如何处理?方法有很多,这次讲解用dataframe.iterrows()方法。 DataFrame.iterrows()方法: 返回值是一个由索引和Series组成的元组。 关于这个方法的两个注…

程序汪10万接的多平台视频分发项目,模拟人工发视频

本项目来自程序汪背后的私活小团队,开发了一个多平台分发视频项目,给粉丝分享一下解决方案和具体项目分开情况付款情况等等细节,希望给想接私活的朋友一些经验参考 程序汪10万接的多平台视频分发项目,模拟人工发视频 视频版本 在 …

【网站项目】自助购药小程序

🙊作者简介:拥有多年开发工作经验,分享技术代码帮助学生学习,独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。🌹赠送计算机毕业设计600个选题excel文件,帮助大学选题。赠送开题报告模板&#xff…

VSCODE目录树缩进调整

VSCode默认的缩进太小了,简直看不出来,很容易弄混目录。在设置里修改就行了。 修改后效果:

代码随想录算法训练营第48天|198.打家劫舍|213.打家劫舍II| 337.打家劫舍III

代码随想录算法训练营第48天|198.打家劫舍|213.打家劫舍II| 337.打家劫舍III 今天就是打家劫舍的一天,这个系列不算难,大家可以一口气拿下。 198.打家劫舍 视频讲解:https://www.bilibili.com/video/BV1Te411N7SX https://programmercarl.c…

合并主分支到子分支

参考:【Git】合并分支出现 Please enter a commit message to explain why this merge is necessary.-CSDN博客 git 如何将主分支(master)合并到子分支上_git 将主分支合并到子分支-CSDN博客 1、先切换到主分支master git checkout master 2、把主分支代码拉到本地…

T-Mamba:用于牙齿 3D CBCT 分割的频率增强门控长程依赖性

T-Mamba:用于牙齿 3D CBCT 分割的频率增强门控长程依赖性 摘要Introduction方法T-Mamba architectureTim block T-Mamba: Frequency-Enhanced Gated Long-Range Dependendcy for Tooth 3D CBCT Segmentation 摘要 三维成像中的高效牙齿分割对于正畸诊断至关重要&am…

机器学习 —— 数据分析与图表绘制

本文使用工具 Anaconda下载安装与使用 Jupyter Notebook的使用 本文使用数据集 机器学习实验所需内容.zip 以朝阳医院2018年销售数据为例,目的是了解朝阳医院在2018年里的销售情况,这就需要知道几个业务指标,本次的分析…

Netty学习——源码篇13 命中缓存的分配

上一篇分析了DirectArena内存分配大小的大概流程(Netty池化内存管理机制),知道了其先命中缓冲,如果没有命中,再去分配一款连续内存。现在分析命中缓存的相关逻辑。前面说到PoolThreadCache中维护了三个缓存数组(实际上是6个,这里仅以Direct为…

matlab/simulink仿真全合集---电力电子的simulink仿真

simulink仿真新手大礼包,共整理了9份simulink仿真模型,每一份都是完美运行,适合电气工程专业/电力电子专业的新手学习。 1、Boost电路 simulink 仿真,boost 电路模块搭建和用传递函数进行验证, 电流开环控制 、电流闭…

基于Spring boot+Vue的业余排球俱乐部会员管理系统

5 系统功能模块的具体实现 5.1超级会员角色 5.1.1 登录 超级管理员登录通过用户名和密码去数据库查询用户表,该名称是否在用户表中存在,如果存在,则通过用户名和密码查询密码是否正确,然后吧用户的信息存在jwt的负载里&#xf…

C语言 | Leetcode C语言题解之第14题最长公共前缀

题目&#xff1a; 题解&#xff1a; char* longestCommonPrefix(char** strs, int strsSize) {if (strsSize 0) {return ""; } for (int i 0; i < strlen(strs[0]); i) {for (int j 1; j < strsSize; j) {if (strs[0][i] ! strs[j][i]){strs[0][i] \0;ret…

ics-05-攻防世界

题目 点了半天只有设备维护中心能进去 御剑扫一下 找到一个css 没什么用 再点击云平台设备维护中心url发生了变化 设备维护中心http://61.147.171.105:65103/index.php?pageindex试一下php伪协议 php://filter/readconvert.base64-encode/resourceindex.php base64解一下…

【二分查找】Leetcode 山脉数组的峰顶索引

题目解析 852. 山脉数组的峰顶索引 这到题使用暴力枚举的查找方法发现这段数组是有二段性的&#xff0c;峰顶左边的一段区间是一段递增区间&#xff0c;右边的一段区间是一段递减区间 算法讲解 class Solution { public:int peakIndexInMountainArray(vector<int>&am…

vue快速入门(十二)v-key索引标志

注释很详细&#xff0c;直接上代码 新增内容 v-key的使用场景数组筛选器的使用 源码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-…

漫谈:“标准”是一种幻觉 C++语言标准的意义

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 “标准”这个词很迷惑&#xf…

1688详情API接口:解锁多元化应用场景java php c++

随着互联网的快速发展&#xff0c;数据交换和信息共享已成为企业日常运营不可或缺的一部分。在这样的背景下&#xff0c;API&#xff08;应用程序接口&#xff09;接口作为实现数据互通的重要工具&#xff0c;受到了越来越多企业的青睐。1688详情API接口作为阿里巴巴旗下的重要…