Go语言数据库操作之GORM框架从入门到生产实战(完整版)

🏷️ 标签:Go GORM V2 MySQL ORM框架 Go后端 数据库实战📝 适用人群:Go 后端新手、需要快速上手 ORM 框架的开发者、毕业设计 / 项目开发学习者💡 核心亮点:全程实战无废话,从环境搭建到生产级封装,代码可直接复制使用,适配企业开发规范,兼顾入门与实战


一、前言

在 Go 语言后端开发中,数据库操作是核心模块。上一篇我们讲解了 Go 原生database/sql操作 MySQL,虽然可控性强,但存在代码繁琐、需手动处理连接池、防 SQL 注入、NULL 值等问题,效率较低。

而 GORM 框架作为 Go 生态中最主流、最成熟的 **ORM(对象关系映射)** 框架,完美解决了原生 SQL 的痛点 ------ 无需手写 SQL,直接操作结构体,内置连接池、事务、软删除、钩子函数等企业级功能,开发效率翻倍,同时兼容原生 SQL,兼顾灵活性与便捷性。

本文将从入门到实战,手把手讲解GORM V2(目前稳定主流版本)的全部核心用法,搭配生产级项目模板,可直接复制发布,也可直接用于实际项目开发。

二、环境准备(快速上手)

2.1 安装 GORM 与 MySQL 驱动

GORM V2 版本需要单独安装框架本身和对应数据库驱动,这里以 MySQL 为例(最常用场景),执行以下命令安装:

复制代码
# 安装GORM核心框架
go get gorm.io/gorm
# 安装MySQL驱动(GORM V2专用)
go get gorm.io/driver/mysql

注意 :GORM V2 不再依赖原生github.com/go-sql-driver/mysql,而是使用gorm.io/driver/mysql,两者底层兼容,但 API 有差异,不要混用。

2.2 测试数据表准备

为了方便后续实战,我们创建一个user表(和上一篇原生 SQL 保持一致,方便对比学习),SQL 语句如下:

复制代码
CREATE TABLE `user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL COMMENT '用户名',
  `age` int NOT NULL COMMENT '年龄',
  `email` varchar(64) DEFAULT NULL COMMENT '邮箱',
  `created_at` datetime NOT NULL COMMENT '创建时间',
  `updated_at` datetime NOT NULL COMMENT '更新时间',
  `deleted_at` datetime DEFAULT NULL COMMENT '删除时间(软删除)',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

说明 :新增created_atupdated_atdeleted_at字段,适配 GORM 内置的时间戳和软删除功能,无需手动维护。

三、核心基础:连接数据库与配置连接池

3.1 核心知识点

GORM 的数据库连接基于原生database/sql封装,核心对象是*gorm.DB,和原生*sql.DB一样,全局单例使用,内置连接池,无需手动管理连接。

连接流程:拼接 DSN → 初始化 GORM DB 对象 → 配置连接池 → 校验连接

3.2 代码实现(生产级配置)

我们沿用原生 SQL 的项目结构,将配置分离,便于维护,分为 3 个文件:配置文件、数据库初始化文件、入口测试文件。

3.2.1 config/db.go(配置分离)
复制代码
package config

import "time"

// MySQLConfig MySQL配置结构体(和原生SQL完全兼容,可直接复用)
type MySQLConfig struct {
	User     string        // 数据库用户名
	Passwd   string        // 数据库密码
	Host     string        // 数据库地址
	Port     string        // 数据库端口
	DBName   string        // 数据库名称
	Charset  string        // 字符集
	ParseTime bool         // 是否解析时间(必须设为true,否则时间字段无法映射)
	Loc      string        // 时区

	// 连接池配置(核心,生产环境必须设置)
	MaxOpenConns    int           // 最大打开连接数
	MaxIdleConns    int           // 最大空闲连接数
	ConnMaxLifetime time.Duration // 连接最大生命周期
	ConnMaxIdleTime time.Duration // 空闲连接最大存活时间
}

// DefaultMySQLConfig 默认配置(可直接修改为自己的数据库信息)
func DefaultMySQLConfig() *MySQLConfig {
	return &MySQLConfig{
		User:            "root",
		Passwd:          "root", // 替换为自己的数据库密码
		Host:            "127.0.0.1",
		Port:            "3306",
		DBName:          "testdb", // 替换为自己的数据库名称
		Charset:         "utf8mb4",
		ParseTime:       true,
		Loc:             "Local", // 本地时区
		MaxOpenConns:    20,      // 生产环境建议20-50
		MaxIdleConns:    10,      // 建议为最大打开连接数的一半
		ConnMaxLifetime: 3 * time.Minute, // 连接3分钟后自动关闭
		ConnMaxIdleTime: 1 * time.Minute, // 空闲连接1分钟后自动关闭
	}
}

// DSN 拼接(GORM V2 DSN格式和原生一致)
func (c *MySQLConfig) DSN() string {
	return c.User + ":" + c.Passwd + "@tcp(" + c.Host + ":" + c.Port + ")/" +
		c.DBName + "?charset=" + c.Charset + "&parseTime=" + bool2Str(c.ParseTime) + "&loc=" + c.Loc
}

// bool2Str 辅助函数:将bool转为字符串(适配DSN拼接)
func bool2Str(b bool) string {
	if b {
		return "true"
	}
	return "false"
}
3.2.2 db/mysql.go(数据库初始化,全局单例)
复制代码
package db

import (
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gosql-gorm-demo/config" // 替换为自己的项目模块名
	"log"
	"time"
)

// DB 全局GORM DB对象(单例,全程使用此对象操作数据库)
var DB *gorm.DB

// InitMySQL 初始化MySQL数据库(生产级封装)
func InitMySQL(cfg *config.MySQLConfig) error {
	var err error

	// 1. 打开数据库连接(GORM自动初始化连接池)
	DB, err = gorm.Open(mysql.Open(cfg.DSN()), &gorm.Config{
		// 日志配置(生产环境可根据需求调整,开发环境开启详细日志)
		Logger: logger.Default.LogMode(logger.Info), // 显示所有SQL语句,便于调试
	})
	if err != nil {
		return err
	}

	// 2. 获取原生sql.DB对象,配置连接池(和原生SQL配置一致)
	sqlDB, err := DB.DB()
	if err != nil {
		return err
	}
	// 设置连接池参数
	sqlDB.SetMaxOpenConns(cfg.MaxOpenConns)
	sqlDB.SetMaxIdleConns(cfg.MaxIdleConns)
	sqlDB.SetConnMaxLifetime(cfg.ConnMaxLifetime)
	sqlDB.SetConnMaxIdleTime(cfg.ConnMaxIdleTime)

	// 3. 校验连接(可选,但建议加上,确保连接成功)
	err = sqlDB.Ping()
	if err != nil {
		return err
	}

	log.Println("GORM 连接MySQL成功!")
	return nil
}

// Close 关闭数据库连接(程序退出时调用)
func Close() {
	sqlDB, err := DB.DB()
	if err != nil {
		log.Printf("关闭数据库失败:%v", err)
		return
	}
	_ = sqlDB.Close()
	log.Println("GORM 数据库连接已关闭")
}
3.2.3 main.go(入口测试)
复制代码
package main

import (
	"gosql-gorm-demo/config"
	"gosql-gorm-demo/db"
	"log"
)

func main() {
	// 1. 初始化数据库配置
	cfg := config.DefaultMySQLConfig()
	// 2. 初始化GORM连接
	if err := db.InitMySQL(cfg); err != nil {
		log.Printf("GORM连接失败:%v", err)
		return
	}
	// 3. 延迟关闭数据库
	defer db.Close()

	// 后续所有数据库操作,都使用 db.DB 对象
	log.Println("GORM初始化完成,可开始操作数据库")
}

3.3 关键注意事项

  1. ParseTime=true 必须设置,否则 GORM 无法映射time.Time类型字段(如created_at)。
  2. *gorm.DB 是全局单例,不要每次操作都重新初始化,否则会导致连接池泄漏。
  3. 连接池参数必须设置,否则 GORM 会使用默认值(默认连接数较小,高并发下会报错)。
  4. 开发环境开启logger.Info日志,便于查看 GORM 自动生成的 SQL 语句,生产环境可改为logger.Error,只打印错误日志。

四、核心核心:GORM 模型定义(表与结构体映射)

4.1 模型定义规则

GORM 的核心是模型映射,即结构体对应数据库表,结构体字段对应数据库列,默认遵循以下约定(可自定义):

  • 结构体名采用大驼峰,对应数据库表名采用小写蛇形(如UserusersUserInfouser_infos)。
  • 结构体字段采用大驼峰,对应数据库列名采用小写蛇形(如UserNameuser_name)。
  • 字段ID(首字母大写)默认是主键,且为自增(AUTO_INCREMENT)。
  • 字段CreatedAt 默认对应created_at,自动记录创建时间;UpdatedAt 默认对应updated_at,自动记录更新时间。
  • 字段DeletedAt 默认对应deleted_at,开启软删除功能(删除时不真正删除数据,只是设置该字段值)。

4.2 模型实现(model/user.go)

复制代码
package model

import (
	"gorm.io/gorm"
	"time"
)

// User 用户模型(对应数据库users表,GORM默认表名是结构体名小写复数)
type User struct {
	ID        int            `gorm:"primaryKey;autoIncrement;comment:用户ID"` // 主键、自增、备注
	Name      string         `gorm:"type:varchar(32);not null;comment:用户名"` // 字段类型、非空、备注
	Age       int            `gorm:"type:int;not null;comment:年龄"`
	Email     *string        `gorm:"type:varchar(64);default:null;comment:邮箱"` // 允许为NULL,用指针接收
	CreatedAt time.Time      `gorm:"type:datetime;not null;comment:创建时间"`
	UpdatedAt time.Time      `gorm:"type:datetime;not null;comment:更新时间"`
	DeletedAt gorm.DeletedAt `gorm:"index;comment:软删除时间"` // 软删除字段,index表示建立索引
}

// TableName 自定义表名(可选,默认是users,这里指定为user,和我们创建的表一致)
func (u *User) TableName() string {
	return "user"
}

4.3 模型标签详解(常用)

通过结构体标签(gorm:"xxx")可以自定义字段映射规则,常用标签如下(必记):

标签 作用 示例
primaryKey 指定为主键 gorm:"primaryKey"
autoIncrement 主键自增 gorm:"autoIncrement"
type 指定字段类型 gorm:"type:varchar(32)"
not null 字段非空 gorm:"not null"
default 设置默认值 gorm:"default:0"
comment 字段备注 gorm:"comment: 用户名"
index 建立普通索引 gorm:"index"
uniqueIndex 建立唯一索引 gorm:"uniqueIndex"
column 自定义列名 gorm:"column:user_name"

4.4 关键注意事项

  1. 允许为 NULL 的字段,建议用指针类型 (如*string)接收,否则 GORM 会将空值映射为对应类型的默认值(如空字符串、0),无法区分 "默认值" 和 "NULL"。
  2. 软删除字段必须是gorm.DeletedAt类型,且加上index标签(提高查询效率),开启软删除后,删除操作会自动变为 "更新 deleted_at 字段",查询操作会自动过滤已删除数据。
  3. 如果数据库表名和 GORM 默认约定不一致,必须实现TableName()方法,指定自定义表名。
  4. 模型字段名不要用 GORM 关键字(如orderdesc),否则会导致 SQL 生成错误。

五、核心操作:CRUD 实战(最常用)

GORM 的 CRUD 操作极其简洁,无需手写 SQL,直接调用db.DB的内置方法即可,以下是企业开发中最常用的场景,全部基于上面的User模型实现。

我们创建dao/user.go,封装所有用户相关的数据库操作(生产级规范,业务与数据分离)。

5.1 新增数据(Create)

复制代码
package dao

import (
	"gosql-gorm-demo/db"
	"gosql-gorm-demo/model"
)

// AddUser 新增用户(单条)
func AddUser(user *model.User) error {
	// 方式1:直接创建
	return db.DB.Create(user).Error

	// 方式2:批量创建(批量新增时用)
	// users := []*model.User{user1, user2}
	// return db.DB.Create(&users).Error
}

说明 :GORM 的Create方法会自动填充CreatedAtUpdatedAt字段,无需手动赋值。

5.2 查询数据(Retrieve)

查询是最复杂的场景,GORM 提供了丰富的查询方法,覆盖所有常用场景,以下是高频用法:

复制代码
// GetUserByID 根据ID查询单个用户(最常用)
func GetUserByID(id int) (*model.User, error) {
	var user model.User
	// 方式1:根据主键查询
	err := db.DB.First(&user, id).Error
	// 方式2:根据条件查询(等价于 WHERE id = ?)
	// err := db.DB.Where("id = ?", id).First(&user).Error
	if err != nil {
		return nil, err
	}
	return &user, nil
}

// ListUser 查询用户列表(支持条件、分页)
func ListUser(page, pageSize int, name string) ([]model.User, int64, error) {
	var (
		users []model.User
		total int64
	)
	// 1. 统计总数(where条件可选)
	tx := db.DB.Model(&model.User{})
	if name != "" {
		tx = tx.Where("name LIKE ?", "%"+name+"%") // 模糊查询
	}
	err := tx.Count(&total).Error
	if err != nil {
		return nil, 0, err
	}

	// 2. 分页查询(offset:跳过多少条,limit:查询多少条)
	offset := (page - 1) * pageSize
	err = tx.Offset(offset).Limit(pageSize).Find(&users).Error
	if err != nil {
		return nil, 0, err
	}
	return users, total, nil
}

// GetUserByEmail 根据邮箱查询用户(唯一索引场景)
func GetUserByEmail(email string) (*model.User, error) {
	var user model.User
	err := db.DB.Where("email = ?", email).First(&user).Error
	return &user, err
}

5.3 更新数据(Update)

更新分为 "全量更新" 和 "部分更新",企业开发中优先使用部分更新(避免覆盖未修改的字段):

复制代码
// UpdateUser 部分更新用户信息(推荐)
func UpdateUser(id int, updateData map[string]interface{}) error {
	// 方式1:根据ID更新,只更新指定字段
	return db.DB.Model(&model.User{}).Where("id = ?", id).Updates(updateData).Error

	// 方式2:更新单个字段
	// return db.DB.Model(&model.User{}).Where("id = ?", id).Update("age", 20).Error

	// 方式3:全量更新(不推荐,会覆盖所有字段,除了CreatedAt)
	// user := &model.User{Name: "新名字", Age: 20}
	// return db.DB.Model(&model.User{}).Where("id = ?", id).Save(user).Error
}

说明Updates 接收map[string]interface{},key 对应数据库列名(小写蛇形),value 为要更新的值,只会更新非空字段。

5.4 删除数据(Delete)

GORM 支持 "软删除" 和 "硬删除",默认是软删除(开启DeletedAt字段后):

复制代码
// DeleteUser 软删除用户(默认,推荐)
func DeleteUser(id int) error {
	// 软删除:UPDATE user SET deleted_at = ? WHERE id = ?
	return db.DB.Delete(&model.User{}, id).Error
}

// HardDeleteUser 硬删除用户(不推荐,除非特殊场景)
func HardDeleteUser(id int) error {
	// 硬删除:DELETE FROM user WHERE id = ?(真正删除数据)
	return db.DB.Unscoped().Delete(&model.User{}, id).Error
}

说明 :软删除后,所有查询方法(FirstFind等)会自动过滤已删除数据;如果需要查询已删除数据,需加上Unscoped()

5.5 关键注意事项

  1. 查询时,First方法会返回 "第一条数据",如果没有数据,会返回gorm.ErrRecordNotFound错误,可根据业务需求判断是否处理。
  2. 更新时,Model方法用于指定要更新的模型,Where方法用于指定条件,避免 "批量更新全表"(如忘记加 Where,会更新所有数据)。
  3. 软删除是企业开发的最佳实践,可保留数据历史,便于后续恢复;硬删除需谨慎使用。
  4. 批量操作(批量新增、批量更新、批量删除)时,建议使用事务包裹,保证原子性。

六、高级特性:事务、钩子函数、原生 SQL 兼容

6.1 事务操作(企业级必备)

GORM 内置事务封装,比原生 SQL 更简洁,核心方法:Begin()Commit()Rollback(),支持手动事务和自动事务。

复制代码
// TxAddTwoUser 事务示例:同时新增两个用户,要么都成功,要么都失败
func TxAddTwoUser(user1, user2 *model.User) error {
	// 1. 开启事务
	tx := db.DB.Begin()
	if tx.Error != nil {
		return tx.Error
	}

	// 2. 延迟处理:出错回滚,成功提交
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback() //  panic时回滚
		}
	}()

	// 3. 执行事务操作
	if err := tx.Create(user1).Error; err != nil {
		tx.Rollback() // 第一个用户新增失败,回滚
		return err
	}
	if err := tx.Create(user2).Error; err != nil {
		tx.Rollback() // 第二个用户新增失败,回滚
		return err
	}

	// 4. 提交事务
	return tx.Commit().Error
}

说明 :GORM 还支持 "自动事务"(Transaction方法),无需手动 Begin/Commit/Rollback,更简洁,适合简单事务场景。

6.2 钩子函数(数据生命周期拦截)

钩子函数是 GORM 的高级特性,用于在 "新增、更新、删除" 等操作的前后,执行自定义逻辑(如数据校验、字段赋值),常用钩子如下:

复制代码
// 在model/user.go中添加钩子函数
import "errors"
import "log"

// BeforeCreate 新增前钩子(新增用户前,自动校验年龄)
func (u *User) BeforeCreate(tx *gorm.DB) error {
	if u.Age < 0 || u.Age > 150 {
		return errors.New("年龄必须在0-150之间")
	}
	return nil
}

// AfterUpdate 更新后钩子(更新用户后,记录日志)
func (u *User) AfterUpdate(tx *gorm.DB) error {
	log.Printf("用户ID:%d 已更新,新年龄:%d", u.ID, u.Age)
	return nil
}

常用钩子:BeforeCreateAfterCreateBeforeUpdateAfterUpdateBeforeDeleteAfterDelete

6.3 原生 SQL 兼容(灵活性保障)

虽然 GORM 自动生成 SQL,但对于复杂查询(如多表联查、复杂子查询),仍可直接使用原生 SQL,兼顾便捷性和灵活性:

复制代码
// RawSQLDemo 原生SQL查询示例
func RawSQLDemo() ([]model.User, error) {
	var users []model.User
	// 原生SQL查询
	err := db.DB.Raw("SELECT id, name, age FROM user WHERE age > ?", 18).Scan(&users).Error
	if err != nil {
		return nil, err
	}
	return users, nil
}

// ExecRawSQL 原生SQL执行增删改
func ExecRawSQL() error {
	// 原生SQL更新
	return db.DB.Exec("UPDATE user SET age = age + 1 WHERE id = ?", 1).Error
}

七、生产级项目完整结构(可直接复制使用)

复制代码
gosql-gorm-demo/
├── config/
│   └── db.go        # 数据库配置(和原生SQL结构一致,可复用)
├── dao/
│   └── user.go      # 用户表CRUD封装(业务与数据分离)
├── model/
│   └── user.go      # 模型定义(含钩子函数、软删除)
├── db/
│   └── mysql.go     # GORM初始化、连接池配置、全局单例
├── main.go          # 入口测试、业务调用
└── go.mod           # 依赖管理

7.1 go.mod 依赖配置

复制代码
module gosql-gorm-demo

go 1.21

require (
	gorm.io/driver/mysql v1.5.2
	gorm.io/gorm v1.25.4
)

7.2 main.go 完整测试代码

复制代码
package main

import (
	"gosql-gorm-demo/config"
	"gosql-gorm-demo/dao"
	"gosql-gorm-demo/db"
	"gosql-gorm-demo/model"
	"log"
)

func main() {
	// 1. 初始化数据库
	cfg := config.DefaultMySQLConfig()
	if err := db.InitMySQL(cfg); err != nil {
		log.Printf("GORM初始化失败:%v", err)
		return
	}
	defer db.Close()

	// 2. 新增用户
	user := &model.User{
		Name: "张三",
		Age:  18,
	}
	email := "zhangsan@qq.com"
	user.Email = &email
	if err := dao.AddUser(user); err != nil {
		log.Printf("新增用户失败:%v", err)
	} else {
		log.Printf("新增用户成功,ID:%d", user.ID)
	}

	// 3. 查询单个用户
	getUser, err := dao.GetUserByID(user.ID)
	if err != nil {
		log.Printf("查询用户失败:%v", err)
	} else {
		log.Printf("查询用户成功:%+v", getUser)
	}

	// 4. 更新用户
	updateData := map[string]interface{}{
		"age": 20,
	}
	if err := dao.UpdateUser(user.ID, updateData); err != nil {
		log.Printf("更新用户失败:%v", err)
	} else {
		log.Println("更新用户成功")
	}

	// 5. 查询用户列表
	list, total, err := dao.ListUser(1, 10, "张")
	if err != nil {
		log.Printf("查询用户列表失败:%v", err)
	} else {
		log.Printf("查询用户列表成功,总数:%d,列表:%+v", total, list)
	}

	// 6. 事务测试
	user1 := &model.User{Name: "事务1", Age: 22}
	user2 := &model.User{Name: "事务2", Age: 23}
	if err := dao.TxAddTwoUser(user1, user2); err != nil {
		log.Printf("事务执行失败:%v", err)
	} else {
		log.Println("事务执行成功")
	}

	// 7. 删除用户
	if err := dao.DeleteUser(user.ID); err != nil {
		log.Printf("删除用户失败:%v", err)
	} else {
		log.Println("删除用户成功")
	}
}

八、避坑指南(生产环境必看)

  1. 连接池配置不当导致高并发报错 :必须设置MaxOpenConnsMaxIdleConns等参数,避免默认连接数不足,导致 "too many connections" 错误。
  2. 软删除未开启却使用删除方法 :如果模型没有DeletedAt字段,Delete方法会执行硬删除,需注意区分。
  3. 查询时未处理 "无数据" 错误First方法查询不到数据会返回gorm.ErrRecordNotFound,未处理会导致程序 panic。
  4. 更新时忘记加 Where 条件:忘记加 Where 会批量更新全表,造成数据灾难,开发时务必检查。
  5. 模型字段与数据库列名不匹配 :如果未遵循 GORM 约定,且未使用column标签,会导致字段映射失败,查询 / 更新无效果。
  6. 日志配置不当 :生产环境开启logger.Info会打印大量 SQL 语句,影响性能,建议改为logger.Error
  7. 敏感配置硬编码:数据库账号密码不要硬编码在代码中,建议使用环境变量或配置文件(如 viper)读取。

九、GORM vs 原生 SQL(选型建议)

对比维度 GORM 框架 原生 SQL
开发效率 高(无需手写 SQL,自动生成) 低(需手动写 SQL、处理连接池等)
可控性 中等(复杂 SQL 需兼容原生) 高(完全掌控 SQL 语句)
防 SQL 注入 自动防护(参数化查询) 需手动处理(使用?占位符)
功能丰富度 高(内置事务、软删除、钩子等) 低(需手动封装所有功能)
性能 略低(多一层封装,影响极小) 高(无额外封装)

选型建议

  • 90% 的业务场景(如后台管理系统、中小型 API):用 GORM,提升开发效率,降低出错概率。
  • 高并发、复杂查询场景(如电商核心业务):核心模块用原生 SQL,非核心模块用 GORM,兼顾性能与效率。
  • 毕业设计、新手入门:优先用 GORM,快速上手,专注业务逻辑开发。

十、知识图谱(文字版)

复制代码
Go语言GORM框架
├── 基础准备
│   ├── 安装GORM与MySQL驱动
│   ├── 数据表准备
│   └── 连接数据库(配置连接池)
├── 核心核心:模型定义
│   ├── 模型与表映射约定
│   ├── 常用模型标签
│   ├── 软删除配置
│   └── 钩子函数
├── 核心操作:CRUD
│   ├── 新增(单条/批量)
│   ├── 查询(单条/列表/条件/分页)
│   ├── 更新(部分/全量)
│   └── 删除(软删除/硬删除)
├── 高级特性
│   ├── 事务(手动/自动)
│   ├── 钩子函数
│   └── 原生SQL兼容
├── 生产级封装
│   ├── 项目结构(config/dao/model/db)
│   ├── 配置分离
│   └── 业务与数据分离
├── 避坑指南
└── 选型建议(GORM vs 原生SQL)

版权声明

本文为原创 Go 后端技术文章,CSDN 首发,全程实战无废话,包含 GORM 从入门到生产的全部核心用法,禁止未经授权转载、抄袭与搬运,侵权必究!

相关推荐
2303_821287381 小时前
Redis如何监控系统QPS的变化趋势
jvm·数据库·python
dinglu1030DL1 小时前
uni-app怎么接极光推送 uni-app消息推送App端接入【教程】
jvm·数据库·python
神明9311 小时前
Go语言如何用logrus_Go语言logrus日志框架教程【技巧】
jvm·数据库·python
数据库小学妹1 小时前
企业级数据库迁移实践:从Oracle到国产数据库的兼容性与实施策略
数据库·mysql·oracle·dba
2301_779622411 小时前
PHP处理Codex安全漏洞检测【解答】
jvm·数据库·python
一 乐1 小时前
学院教学工作量统计|基于java+ vue学院教学工作量统计管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·学院教学工作量统计系统
六月雨滴1 小时前
Oracle 安全架构概述
数据库·oracle·dba·安全架构
迷藏4941 小时前
【无标题】
java·数据库·oracle
cndes2 小时前
Pycharm的虚拟环境设置问题
开发语言·python