Gorm 数据库表迁移与表模型定义

文章目录

    • 一、Docker快速创建MySQL实例
      • 1.1 创建
      • 1.3 创建数据库
    • 二、AutoMigrate介绍与使用
      • 2.1 AutoMigrate介绍
      • 2.2 AutoMigrate 基本使用
    • 三、模型定义
      • 3.1 模型定义
      • 3.2 快速增删改查
      • 3.3 约定
      • 3.4 gorm.Model
    • 四、表模型主键、表名、列名的约定
      • 4.1 主键(Primary Key)
        • 4.1.1 使用 `ID` 作为主键
        • 4.1.2 复合主键
      • 4.2 表名(Table Name)
        • 4.2.1 TableName
        • 4.2.2 临时指定表名
        • 4.2.3 命名策略
      • 4.3 列名(Column Name)
      • 4.4 时间戳跟踪
        • 4.4.1 CreatedAt
        • 4.4.2 UpdatedAt
        • 4.4.3 DeletedAt
    • 五、模型定义高级选项与标签
      • 5.1 字段级权限控制
      • 5.2 创建/更新时间追踪(纳秒、毫秒、秒、Time)
      • 5.3 嵌入结构体
      • 5.4 结构体标签(tags)
      • 5.5 举个例子

一、Docker快速创建MySQL实例

1.1 创建

因为这里我们是测试学习使用,单独安装MySQL 比较费时费力,所以这里使用Docker方便快速掌握Gorm 相关知识。

如果你没有docker环境,可以参考:【一文搞定】Linux、Mac、Windows安装Docker与配置教程!

下载镜像:

docker pull mysql

运行MySQL容器:

docker run -p 3306:3306 --name mysql -v $PWD/conf/my.cnf:/etc/mysql/my.cnf -v $PWD/logs:/logs -v $PWD/data:/mysql_data -e MYSQL_ROOT_PASSWORD=123456 -d mysql

下面是对命令中参数的解释:

  • -p 3306:3306:将容器的 3306 端口映射到主机的 3306 端口

  • -v $PWD/conf/my.cnf:/etc/mysql/my.cnf:将主机当前目录下的 conf/my.cnf 挂载到容器的 /etc/mysql/my.cnf

  • -v $PWD/logs:/logs:将主机当前目录下的 logs 目录挂载到容器的 /logs

  • -v $PWD/data:/mysql_data:将主机当前目录下的 data 目录挂载到容器的 /mysql_data

  • -e MYSQL_ROOT_PASSWORD=123456:初始化 root 用户的密码

查看运行中的容器:

docker ps  

1.3 创建数据库

首先,使用Datagrip 链接数据,接着在使用GORM前手动创建数据库db1,执行如下SQL:

CREATE DATABASE db1;

二、AutoMigrate介绍与使用

2.1 AutoMigrate介绍

AutoMigrate 是 Gorm 提供的一个功能强大的数据库迁移工具,它可以自动创建或更新数据库表结构,使数据库的结构与 Golang 模型一致。使用 AutoMigrate 可以方便地进行数据库表的初始化和更新,而无需手动执行 SQL 语句。

2.2 AutoMigrate 基本使用

在 Gorm 中,你可以通过调用 db.AutoMigrate 方法来进行数据库表的自动迁移。以下是一个基本的使用示例:

package mainimport ("gorm.io/driver/mysql""gorm.io/gorm""gorm.io/gorm/logger""log""os""time"
)type Product struct {gorm.ModelCode  stringPrice uint
}func main() {// 日志配置newLogger := logger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer(日志输出的目标,前缀和日志包含的内容——译者注)logger.Config{SlowThreshold:             time.Second, // 慢 SQL 阈值LogLevel:                  logger.Info, // 日志级别为infoIgnoreRecordNotFoundError: true,        // 忽略ErrRecordNotFound(记录未找到)错误Colorful:                  true,        // 彩色打印},)dsn := "root:123456@tcp(127.0.0.1:3306)/db1?charset=utf8mb4&parseTime=True&loc=Local"db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{Logger: newLogger,})if err != nil {panic(err) // 如果数据库不存在会报错}db.AutoMigrate(&Product{}) // 可以加多个log.Println("Auto Migration Completed")
}

三、模型定义

3.1 模型定义

模型是标准的 struct,由 Go 的基本数据类型、实现了 Scanner 和 Valuer 接口的自定义类型及其指针或别名组成

例如:

type User struct {ID           uintName         stringEmail        *stringAge          uint8Birthday     *time.TimeMemberNumber sql.NullStringActivatedAt  sql.NullTimeCreatedAt    time.TimeUpdatedAt    time.Time
}

3.2 快速增删改查

package mainimport ("fmt""gorm.io/driver/mysql""gorm.io/gorm""gorm.io/gorm/logger""log""os""time"
)// UserInfo 用户信息
type UserInfo struct {ID     uintName   stringGender stringHobby  string
}func main() {// 日志配置newLogger := logger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer(日志输出的目标,前缀和日志包含的内容——译者注)logger.Config{SlowThreshold:             time.Second, // 慢 SQL 阈值LogLevel:                  logger.Info, // 日志级别为infoIgnoreRecordNotFoundError: true,        // 忽略ErrRecordNotFound(记录未找到)错误Colorful:                  true,        // 彩色打印},)dsn := "root:123456@tcp(127.0.0.1:3306)/db1?charset=utf8mb4&parseTime=True&loc=Local"db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{Logger: newLogger,})if err != nil {panic(err) // 如果数据库不存在会报错}// 自动迁移db.AutoMigrate(&UserInfo{})u1 := UserInfo{1, "贾维斯", "男", "篮球"}u2 := UserInfo{2, "荆轲", "女", "足球"}// 创建记录db.Create(&u1)db.Create(&u2)// 查询var u = new(UserInfo)db.First(u)fmt.Printf("%#v\n", u)var uu UserInfodb.Find(&uu, "hobby=?", "足球")fmt.Printf("%#v\n", uu)// 更新db.Model(&u).Update("hobby", "双色球")// 删除db.Delete(&u)
}

3.3 约定

GORM 倾向于约定优于配置 默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAtUpdatedAt 字段追踪创建、更新时间

如果您遵循 GORM 的约定,您就可以少写的配置、代码。 如果约定不符合您的实际要求,GORM 允许你配置它们

3.4 gorm.Model

GORM 定义一个 gorm.Model 结构体,其包括字段 IDCreatedAtUpdatedAtDeletedAt

// gorm.Model 的定义
type Model struct {ID        uint           `gorm:"primaryKey"`CreatedAt time.TimeUpdatedAt time.TimeDeletedAt gorm.DeletedAt `gorm:"index"`
}

您可以将它嵌入到您的结构体中,以包含这几个字段,详情请参考 嵌入结构体

四、表模型主键、表名、列名的约定

4.1 主键(Primary Key)

4.1.1 使用 ID 作为主键

默认情况下,GORM 会使用 ID 作为表的主键。

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

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

// 将 `UUID` 设为主键
type Animal struct {ID     int64UUID   string `gorm:"primaryKey"`Name   stringAge    int64
}
4.1.2 复合主键

通过将多个字段设为主键,以创建复合主键,例如:

type Product struct {ID           string `gorm:"primaryKey"`LanguageCode string `gorm:"primaryKey"`Code         stringName         string
}

**注意:**默认情况下,整型 PrioritizedPrimaryField 启用了 AutoIncrement,要禁用它,您需要为整型字段关闭 autoIncrement

type Product struct {CategoryID uint64 `gorm:"primaryKey;autoIncrement:false"`TypeID     uint64 `gorm:"primaryKey;autoIncrement:false"`
}

4.2 表名(Table Name)

GORM 使用结构体名的 蛇形命名 作为表名。对于结构体 User,根据约定,其表名为 users

4.2.1 TableName

您可以实现 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)
4.2.2 临时指定表名

您可以使用 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';

查看 from 子查询 了解如何在 FROM 子句中使用子查询

4.2.3 命名策略

GORM 允许用户通过覆盖默认的命名策略更改默认的命名约定,命名策略被用于构建: TableNameColumnNameJoinTableNameRelationshipFKNameCheckerNameIndexName。查看 GORM 配置 获取详情

4.3 列名(Column Name)

根据约定,数据表的列名使用的是 struct 字段名的 蛇形命名

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`
}

4.4 时间戳跟踪

4.4.1 CreatedAt

对于有 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())

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

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

对于有 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"`
}
4.4.3 DeletedAt
// 只要使用了gorm.Model结构体继承,DeletedAt DeletedAt `gorm:"index"`  字段
// 执行删除是其实是update语句,并没有真正的删除

五、模型定义高级选项与标签

5.1 字段级权限控制

可导出的字段在使用 GORM 进行 CRUD 时拥有全部的权限,此外,GORM 允许您用标签控制字段级别的权限。这样您就可以让一个字段的权限是只读、只写、只创建、只更新或者被忽略

注意: 使用 GORM Migrator 创建表时,不会创建被忽略的字段

type User struct {Name string `gorm:"<-:create"` // allow read and createName string `gorm:"<-:update"` // allow read and updateName string `gorm:"<-"`        // allow read and write (create and update)Name string `gorm:"<-:false"`  // allow read, disable write permissionName string `gorm:"->"`        // readonly (disable write permission unless it configured)Name string `gorm:"->;<-:create"` // allow read and createName string `gorm:"->:false;<-:create"` // createonly (disabled read from db)Name string `gorm:"-"`            // ignore this field when write and read with structName string `gorm:"-:all"`        // ignore this field when write, read and migrate with structName string `gorm:"-:migration"`  // ignore this field when migrate with struct
}

5.2 创建/更新时间追踪(纳秒、毫秒、秒、Time)

GORM 约定使用 CreatedAtUpdatedAt 追踪创建/更新时间。如果您定义了这种字段,GORM 在创建、更新时会自动填充 当前时间

要使用不同名称的字段,您可以配置 autoCreateTimeautoUpdateTime 标签

如果您想要保存 UNIX(毫/纳)秒时间戳,而不是 time,您只需简单地将 time.Time 修改为 int 即可

type User struct {CreatedAt time.Time // 在创建时,如果该字段值为零值,则使用当前时间填充UpdatedAt int       // 在创建时该字段值为零值或者在更新时,使用当前时间戳秒数填充Updated   int64 `gorm:"autoUpdateTime:nano"` // 使用时间戳填纳秒数充更新时间Updated   int64 `gorm:"autoUpdateTime:milli"` // 使用时间戳毫秒数填充更新时间Created   int64 `gorm:"autoCreateTime"`      // 使用时间戳秒数填充创建时间
}

5.3 嵌入结构体

对于匿名字段,GORM 会将其字段包含在父结构体中,例如:

type User struct {gorm.ModelName string
}
// 等效于
type User struct {ID        uint           `gorm:"primaryKey"`CreatedAt time.TimeUpdatedAt time.TimeDeletedAt gorm.DeletedAt `gorm:"index"`Name string
}

对于正常的结构体字段,你也可以通过标签 embedded 将其嵌入,例如:

type Author struct {Name  stringEmail string
}type Blog struct {ID      intAuthor  Author `gorm:"embedded"`Upvotes int32
}
// 等效于
type Blog struct {ID    int64Name  stringEmail stringUpvotes  int32
}

并且,您可以使用标签 embeddedPrefix 来为 db 中的字段名添加前缀,例如:

type Blog struct {ID      intAuthor  Author `gorm:"embedded;embeddedPrefix:author_"`Upvotes int32
}
// 等效于
type Blog struct {ID          int64AuthorName  stringAuthorEmail stringUpvotes     int32
}

5.4 结构体标签(tags)

声明 model 时,tag 是可选的,GORM 支持以下 tag: tag 名大小写不敏感,但建议使用 camelCase 风格,Gorm支持以下标记:

标签名说明
column指定 db 列名
type列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not nullsize, autoIncrement… 像 varbinary(8) 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSIGNED not NULL AUTO_INCREMENT
serializer指定如何将数据序列化和反序列化到数据库中的序列化程序,如: serializer:json/gob/unixtime
size指定列数据大小/长度, 如: size:256
primaryKey指定列作为主键
unique指定列作为unique
default指定列的默认值
precision指定列的精度
scale指定列的比例
not null指定列不为空
autoIncrement指定列自增
autoIncrementIncrement自动递增步长,控制连续列值之间的间隔
embedded嵌入字段
embeddedPrefix嵌入嵌入字段的字段列名前缀
autoCreateTime跟踪当前时间创建时,对于’int’字段,它将跟踪unix秒,使用值’nano/'milli跟踪unix nano/milli秒,如: autoCreateTime:nano
autoUpdateTime在创建/更新时跟踪当前时间,对于’int’字段,它将跟踪unix秒,使用值’nano/'milli跟踪unix nano/milli秒, 如: autoUpdateTime:milli
index使用选项创建索引,对多个字段使用相同的名称创建复合索引, 详情参照 Indexes
uniqueIndex与’index’相同,但创建唯一索引
check创建检查约束, 如: check:age > 13, 参照 Constraints
<-设置字段的写入权限, <-:create 仅创建字段, <-:update 仅更新字段, <-:false 没有写权限, <- 创建和更新权限
->设置字段读权限, ->:false 没有读权限
-忽略该字段, - 没有读写权限, -:migration 没有迁移权限, -:all 没有 read/write/migrate 权限
comment迁移时为字段添加注释

5.5 举个例子

package mainimport ("gorm.io/driver/mysql""gorm.io/gorm""gorm.io/gorm/logger""log""os""time"
)// Teacher 定义一个模型
type Teacher struct {UserId uint   `gorm:"primaryKey"`                           // 设置主键Name   string `gorm:"column:teacher_name;type:varchar(60)"` // 设置字段名和类型Gender uint   `gorm:"index"`                                // 设置索引
}func main() {// 日志配置newLogger := logger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer(日志输出的目标,前缀和日志包含的内容——译者注)logger.Config{SlowThreshold:             time.Second, // 慢 SQL 阈值LogLevel:                  logger.Info, // 日志级别为infoIgnoreRecordNotFoundError: true,        // 忽略ErrRecordNotFound(记录未找到)错误Colorful:                  true,        // 彩色打印},)dsn := "root:123456@tcp(127.0.0.1:3306)/db1?charset=utf8mb4&parseTime=True&loc=Local"db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{Logger: newLogger,})if err != nil {panic(err) // 如果数据库不存在会报错}db.AutoMigrate(&Teacher{})// 如果表之前存在会修改,但是只会修改之前存在的字段,有问题// 改了字段属性,再执行AutoMigrate,字段属性会变,设置default测试看
}

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

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

相关文章

动态规划Day02

62.不同路径 力扣题目链接(opens new window) 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。…

WPF XAML(一)

一、XAML的含义 问&#xff1a;XAML的含义是什么&#xff1f;为什么WPF中会使用XAML&#xff1f;而不是别的&#xff1f; 答&#xff1a;在XAML是基于XML的格式&#xff0c;XML的优点在于设计目标是具有逻辑性易读而且简单内容也没有被压缩。 其中需要提一下XAML文件在 Visu…

HR_net逐步实现过程(2.2)

1.numpy实现数组卷积操作 这里是一个矩阵&#xff0c;换个图试一下 import numpy as np #输入数组 input_imagenp.array([[1,2,3],[4,5,6],[7,8,9]]) #卷积核 kernelnp.array([[1,0],[0,1]]) #卷积操作 output_imagenp.zeros((2,2)) for i in range(2):for j in range(2):out…

1886_emacs_v29中的行号配置

Grey 全部学习内容汇总&#xff1a; GitHub - GreyZhang/editors_skills: Summary for some common editor skills I used. emacs 29中的行号显示配置 行号显示 行号显示是一个编辑器中很常见的功能&#xff0c;我觉得这个功能的需求度可能因人群或者个人习惯而不同。对于只…

【python、pytorch】NLP模型

自然语言处理&#xff08;Natural Language Processing&#xff0c;简称NLP&#xff09;是计算机科学与语言学中关注于计算机与人类语言间转换的领域。 1. 文本预处理 文本预处理详情 4. RNN模型

如何给字符串字段添加索引

MySQL是支持前缀索引的&#xff0c;可以定义字符串的一部分作为索引&#xff0c;如果创建索引的语句不指定前缀长度&#xff0c;那么索引就会包含整个字符串。 alter table SUser add index index1(email);alter table SUser add index index2(email(6)); 如上两个创建索引的语…

完整的模型训练套路(一、二、三)

搭建神经网络 model import torch from torch import nn#搭建神经网络 class Tudui(nn.Module):def __init__(self):super(Tudui, self).__init__()self.model nn.Sequential(nn.Conv2d(3, 32, 5, 1, 2),nn.MaxPool2d(2),nn.Conv2d(32, 32, 5, 1, 2),nn.MaxPool2d(2),nn.Conv…

Godot游戏引擎有啥优势

游戏开发是一个充满创造力和技术挑战的领域。选择适合的游戏引擎是开发过程中至关重要的决策。在众多的游戏引擎中&#xff0c;Godot以其许多的优势吸引着越来越多的开发者。下面简单的介绍一下Godot游戏引擎的优势。 免费和开源 Godot游戏引擎的免费和开源特性是优势之一。开…

动态规划day03

343. 整数拆分(第二次做还是没弄明白) 力扣题目链接(opens new window) 给定一个正整数 n&#xff0c;将其拆分为至少两个正整数的和&#xff0c;并使这些整数的乘积最大化。 返回你可以获得的最大乘积。 示例 1: 输入: 2输出: 1解释: 2 1 1, 1 1 1。 示例 2: 输入: …

redis 相关面试题(一)

redis 相关面试题&#xff08;一&#xff09; 文章目录 redis 相关面试题&#xff08;一&#xff09;**面试官**&#xff1a;什么是缓存穿透 ? 怎么解决 ?**面试官**&#xff1a;好的&#xff0c;你能介绍一下布隆过滤器吗&#xff1f;**面试官**&#xff1a;什么是缓存击穿 …

Vue2-导航守卫

在Vue中&#xff0c;导航守卫是一组用于控制路由导航的钩子函数。这些钩子函数允许你在路由导航的不同阶段执行一些操作&#xff0c;比如在路由切换之前进行验证、处理路由变化等。 Vue提供了三种类型的导航守卫&#xff1a; 全局前置守卫&#xff1a;在路由切换之前执行&…

C语言通过ODBC函数操作Access数据库(mdb和accdb格式)(char字符数组)

编译环境&#xff1a;Windows XP Visual Studio 2010 数据库&#xff1a;Access 2010&#xff0c;accdb格式 本例程只使用char[]字符数组&#xff0c;不使用wchar_t[]字符数组&#xff0c;更适合C语言初学者。 如果读取字符串时&#xff0c;提供的字符数组空间小了&#xff0c…

【Linux】Linux 系统编程——tree 命令

文章目录 1. 命令概述2. 命令格式3. 常用选项4. 相关描述4.1 tree 命令安装 5. 参考示例5.1 创建树形目录5.2 使用 tree 命令查看树形目录 1. 命令概述 tree 命令用于在命令行界面以树状图形式显示目录及其子目录的内容。这个命令递归地列出所有子目录&#xff0c;并可选择显示…

如何设置 QTableView 表格每列数据的对齐方式?

要设置 QTableView 表格每列数据的对齐方式&#xff0c;您可以使用 QStandardItemModel 的 setData() 方法来为每个单元格设置对齐方式。以下是一个示例&#xff1a; // 创建数据模型和设置表头略...// 设置列对齐方式 model->setData(model->index(0, 0), Qt::AlignLef…

C#,迭代深化搜索(IDS)或迭代深化深度优先搜索(IDDFS)算法的源代码

摘要&#xff1a;本文介绍适合于大数据规模情况下的&#xff0c;新型的迭代深化深度优先搜索(IDDFS)算法的原理、实例及实现的C#源代码。 引言 常用的树&#xff08;或图&#xff09;遍历算法是两种&#xff1a; 广度优先搜索算法&#xff08;BFS&#xff09; 和 深度优先搜索…

EasyExcel的追加写入(新增POI、CSV)

总结&#xff1a;目前市面上流行的2种 EasyExcel和POI都不是真正的对物理excel文件进行追加导入。只是在缓存里面追加&#xff0c;最后一次性写入&#xff0c;并不能解决内存占用问题。 1.EasyExcel2.POI3.CSV 无非就是下面两种逻辑&#xff1a; 1.for循环查询数据&#xff0c;…

js中对数字,超大金额(千位符,小数点)格式化处理

前言 这个问题的灵感来自线上一个小bug&#xff0c;前两天刚看完同事写的代码&#xff0c;对数字类型处理的很好&#xff0c;之前一直都是用正则和toFixed(2)处理数字相关&#xff0c;后面发现使用numeral.js处理更完美。 对于下面这种数据的处理&#xff0c;你能想到几种方法…

燃情瞬间,智能酒精壁炉点亮户外聚会新潮流

在户外聚会中&#xff0c;一种备受瞩目的装饰品和功能性家居设备正逐渐崭露头角&#xff0c;那就是智能酒精壁炉。这种独特的户外装置不仅为聚会场合带来独特的氛围&#xff0c;还具有许多引人注目的优势。 其明亮的火焰不仅照亮整个场所&#xff0c;还散发出温暖迷人的光芒&am…

Git 大量log查看:git log --pretty=oneline

git log 是 Git 版本控制系统中的一个命令&#xff0c;用于展示一个或多个分支的提交历史记录。 当你在 git log 命令后面添加 --prettyoneline 选项时&#xff0c;它会以单行的形式显示提交历史。这意味着每个提交将会在一行内显示&#xff0c;而不是默认的多行显示格式。 具…

浅谈WPF之Popup弹出层

在日常开发中&#xff0c;当点击某控件时&#xff0c;经常看到一些弹出框&#xff0c;停靠在某些页面元素的附近&#xff0c;但这些又不是真正的窗口&#xff0c;而是页面的一部分&#xff0c;那这种功能是如何实现的呢&#xff1f;今天就以一个简单的小例子&#xff0c;简述如…