golang操作mysql利器-gorm

1、傻瓜示例

      GORM通过将数据库表中的数据映射到面向对象的模型中,简化了数据库操作,使得开发者可以很方便的使用代码来操作数据库,而无需编写SQL语句。

目前有个mysql表:miniprogram_orders,其存储了所有用户对应的订单,表结构如下:

建表sql语句:

CREATE TABLE `miniprogram_orders` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `order_sn` varchar(255) NOT NULL DEFAULT '' COMMENT '订单号',
  `uid` varchar(50) NOT NULL DEFAULT '' COMMENT '用户唯一标识',
  `name` varchar(255) NOT NULL DEFAULT '' COMMENT '姓名',
  `sex` varchar(5) NOT NULL DEFAULT '' COMMENT '性别',
  `identify_card_number` varchar(20) NOT NULL DEFAULT '' COMMENT '身份证号',
  `country` varchar(15) NOT NULL DEFAULT '0' COMMENT '国家',
  `identify_card_type` varchar(20) NOT NULL DEFAULT '' COMMENT '证件类型',
  `born_date` varchar(11) NOT NULL DEFAULT '' COMMENT '出生日期',
  `nation` varchar(10) NOT NULL DEFAULT '' COMMENT '民族',
  `phone_num` varchar(20) NOT NULL DEFAULT '' COMMENT '手机号',
  `email` varchar(255) NOT NULL DEFAULT '' COMMENT '邮箱',
  `address` varchar(255) NOT NULL DEFAULT '' COMMENT '现居地址',
  `commodity_id` varchar(255) NOT NULL DEFAULT '' COMMENT '商品id',
  `play_type` varchar(255) NOT NULL DEFAULT '' COMMENT '比赛类型:团体赛、个人赛',
  `application_pay` float NOT NULL DEFAULT '0' COMMENT '报名费用',
  `status` int NOT NULL DEFAULT '0' COMMENT '订单状态',
  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `deleted_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `uid` (`uid`),
  KEY `order_sn` (`order_sn`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

表中有一条记录如下:

mysql> select * from miniprogram_orders \G;
*************************** 1. row ***************************
                  id: 1
            order_sn: c9dbe95f51d1391622eebc08a7afa671
                 uid: liupeng20240908
                name: 棉花糖
                 sex: 男
identify_card_number: 130627202410031234
             country: 中国
  identify_card_type: 身份证
           born_date: 2024-10-03
              nation: 汉
           phone_num: 13988886666
               email: mianhuatang@163.com
             address: 北京天安门
        commodity_id: cc800badc8b91d51a40c092feb31848f
           play_type: 个人赛
     application_pay: 99
              status: 1
          created_at: 2024-09-08 21:53:19
          updated_at: 2024-09-17 18:12:05
          deleted_at: NULL
1 row in set (0.00 sec)

ERROR:
No query specified

假如有一个需求,查找某个人所有订单,代码实现如下:

  • 定义表对应结构体
type MiniprogramOrder struct {
	gorm.Model                 //gorm预定义结构体
	Uid                string  `json:"uid"`
	OrderSn            string  `json:"order_sn"`
	Name               string  `json:"name"`
	Sex                string  `json:"sex"`
	IdentifyCardNumber string  `json:"identify_card_number"`
	Country            string  `json:"country"`
	IdentifyCardType   string  `json:"identify_card_type"`
	BornDate           string  `json:"born_date"`
	Nation             string  `json:"nation"`
	PhoneNum           string  `json:"phone_number"`
	Email              string  `json:"email"`
	Address            string  `json:"address"`
	CommodityId        string  `json:"commodity_id"`
	PlayType           string  `json:"play_type"`
	ApplicationPay     float32 `json:"application_pay"`
	Status             int     `json:"status"`
}

gorm.Model为gorm预定义的结构体,其定义如下:

type Model struct {
	ID        uint `gorm:"primarykey"`
	CreatedAt time.Time
	UpdatedAt time.Time
	DeletedAt DeletedAt `gorm:"index"`
}
  • ID :每个记录的唯一标识符(主键)。
  • CreatedAt :在创建记录时自动设置为当前时间。
  • UpdatedAt:每当记录更新时,自动更新为当前时间。
  • DeletedAt:用于软删除(将记录标记为已删除,而实际上并未从数据库中删除)。

 上面为数据库表信息以及对应结构体定义,下面是代码实现部分:

创建数据库链接:

func initMysql() *gorm.DB {
	var err error
	dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
	client, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Println("failed to connect database" + err.Error())
		return nil
	}
	fmt.Println("db connect success!")
	return client
}

查找数据库记录:

// 根据uid查询订单
func Select(db *gorm.DB, uid string) []MiniprogramOrder {
	orderList := []MiniprogramOrder{}
	db.Where("uid = ?", uid).Find(&orderList)
	return orderList
}

 测试代码:

func main() {
	var json = jsoniter.ConfigCompatibleWithStandardLibrary
	db := initMysql()
	if db == nil {
		return
	}
	orderList := Select(db, "liupeng20240908")
	str, _ := json.MarshalToString(orderList)
	fmt.Println(str)
}

代码打印:

[
    {
        "ID": 1,
        "CreatedAt": "2024-09-08T21:53:19+08:00",
        "UpdatedAt": "2024-09-17T18:12:05+08:00",
        "DeletedAt": null,
        "uid": "liupeng20240908",
        "order_sn": "c9dbe95f51d1391622eebc08a7afa671",
        "name": "棉花糖",
        "sex": "男",
        "identify_card_number": "130627202410031234",
        "country": "中国",
        "identify_card_type": "身份证",
        "born_date": "2024-10-03",
        "nation": "汉",
        "phone_number": "13988886666",
        "email": "mianhuatang@163.com",
        "address": "北京天安门",
        "commodity_id": "cc800badc8b91d51a40c092feb31848f",
        "play_type": "个人赛",
        "application_pay": 99,
        "status": 1
    }
]

全部代码如下:

package main

import (
	"fmt"

	jsoniter "github.com/json-iterator/go"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type MiniprogramOrder struct {
	gorm.Model                 //gorm预定义结构体
	Uid                string  `json:"uid"`
	OrderSn            string  `json:"order_sn"`
	Name               string  `json:"name"`
	Sex                string  `json:"sex"`
	IdentifyCardNumber string  `json:"identify_card_number"`
	Country            string  `json:"country"`
	IdentifyCardType   string  `json:"identify_card_type"`
	BornDate           string  `json:"born_date"`
	Nation             string  `json:"nation"`
	PhoneNum           string  `json:"phone_number"`
	Email              string  `json:"email"`
	Address            string  `json:"address"`
	CommodityId        string  `json:"commodity_id"`
	PlayType           string  `json:"play_type"`
	ApplicationPay     float32 `json:"application_pay"`
	Status             int     `json:"status"`
}

func initMysql() *gorm.DB {
	var err error
	dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
	client, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Println("failed to connect database" + err.Error())
		return nil
	}
	fmt.Println("db connect success!")
	return client
}

// 根据uid查询订单
func Select(db *gorm.DB, uid string) []MiniprogramOrder {
	orderList := []MiniprogramOrder{}
	db.Where("uid = ?", uid).Find(&orderList)
	return orderList
}
func main() {
	var json = jsoniter.ConfigCompatibleWithStandardLibrary
	db := initMysql()
	if db == nil {
		return
	}
	orderList := Select(db, "liupeng20240908")
	str, _ := json.MarshalToString(orderList)
	fmt.Println(str)
}

2、gorm约定

2.1 使用id作为主键

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

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

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

同时也可以使用复合主键:

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

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

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

 2.2 复数表名

GORM 使用结构体名的 蛇形命名 作为表名。对于结构体MiniprogramOrder,根据约定,其表名称为:miniprogram_orders,当然你也可以修改默认表名,只需要实现Tabler接口即可:

type Tabler interface {
    TableName() string
}

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

2.3 指定表名

我们也可以通过代码指定要访问的表名:

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

2.4 列名

根据约定,数据表的列名使用的是 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`
}

2.5 时间戳追踪

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

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

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

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

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

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

了解这些约定后,就能够直到第一章节中查询订单函数,没有指定表名的原因了。

3、数据库基本操作

3.1 写入数据

数据的写入,可以写入单条,也可以单次批量写入数据:

func Insert(db *gorm.DB, orders []MiniprogramOrder) error {
	resp := db.Create(orders)
	if resp.Error != nil {
		fmt.Println(resp)
		return resp.Error
	}
	return nil
}

也可以通过db.CreateInBatches方法来指定批量插入的批次大小:

func Insert(db *gorm.DB, orders []MiniprogramOrder, size int) error {
	if size > 0 {
		resp := db.CreateInBatches(orders, size)
		if resp.Error != nil {
			fmt.Println(resp)
			return resp.Error
		}
	} else {
		resp := db.Create(orders)
		if resp.Error != nil {
			fmt.Println(resp)
			return resp.Error
		}
	}
	return nil
}

3.2 查询

3.2.1 查询单个对象

// 获取第一条记录(主键升序)
db.First(&user)
// SELECT * FROM users ORDER BY id LIMIT 1;

// 获取一条记录,没有指定排序字段
db.Take(&user)
// SELECT * FROM users LIMIT 1;

// 获取最后一条记录(主键降序)
db.Last(&user)
// SELECT * FROM users ORDER BY id DESC LIMIT 1;

3.2.2 根据主键检索

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);

3.2.3 条件检索

// Get first matched record
db.Where("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;


// IN
db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&users)
// SELECT * FROM users WHERE name IN ('jinzhu','jinzhu 2');

// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)
// SELECT * FROM users WHERE name LIKE '%jin%';

// AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;

// Time
db.Where("updated_at > ?", lastWeek).Find(&users)
// SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';

// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
// SELECT * FROM users WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00';

3.2.4 not条件查询

db.Not("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE NOT name = "jinzhu" ORDER BY id LIMIT 1;

// Not In
db.Not(map[string]interface{}{"name": []string{"jinzhu", "jinzhu 2"}}).Find(&users)
// SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2");

// Struct
db.Not(User{Name: "jinzhu", Age: 18}).First(&user)
// SELECT * FROM users WHERE name <> "jinzhu" AND age <> 18 ORDER BY id LIMIT 1;

// Not In slice of primary keys
db.Not([]int64{1,2,3}).First(&user)
// SELECT * FROM users WHERE id NOT IN (1,2,3) ORDER BY id LIMIT 1;

3.2.5 or条件查询

db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
// SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin';

// Struct
db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2", Age: 18}).Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' OR (name = 'jinzhu 2' AND age = 18);

// Map
db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2", "age": 18}).Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' OR (name = 'jinzhu 2' AND age = 18);

3.2.6 select特定字段查询

db.Select("name", "age").Find(&users)
// SELECT name, age FROM users;

db.Select([]string{"name", "age"}).Find(&users)
// SELECT name, age FROM users;

db.Table("users").Select("COALESCE(age,?)", 42).Rows()
// SELECT COALESCE(age,'42') FROM users;

3.2.7 排序

db.Order("age desc, name").Find(&users)
// SELECT * FROM users ORDER BY age desc, name;

// Multiple orders
db.Order("age desc").Order("name").Find(&users)
// SELECT * FROM users ORDER BY age desc, name;

db.Clauses(clause.OrderBy{
  Expression: clause.Expr{SQL: "FIELD(id,?)", Vars: []interface{}{[]int{1, 2, 3}}, WithoutParentheses: true},
}).Find(&User{})
// SELECT * FROM users ORDER BY FIELD(id,1,2,3)

3.2.8 limit查询

db.Limit(3).Find(&users)
// SELECT * FROM users LIMIT 3;

// Cancel limit condition with -1
db.Limit(10).Find(&users1).Limit(-1).Find(&users2)
// SELECT * FROM users LIMIT 10; (users1)
// SELECT * FROM users; (users2)

db.Offset(3).Find(&users)
// SELECT * FROM users OFFSET 3;

db.Limit(10).Offset(5).Find(&users)
// SELECT * FROM users OFFSET 5 LIMIT 10;

// Cancel offset condition with -1
db.Offset(10).Find(&users1).Offset(-1).Find(&users2)
// SELECT * FROM users OFFSET 10; (users1)
// SELECT * FROM users; (users2)

3.2.9 group by

type result struct {
  Date  time.Time
  Total int
}

db.Model(&User{}).Select("name, sum(age) as total").Where("name LIKE ?", "group%").Group("name").First(&result)
// SELECT name, sum(age) as total FROM `users` WHERE name LIKE "group%" GROUP BY `name` LIMIT 1


db.Model(&User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "group").Find(&result)
// SELECT name, sum(age) as total FROM `users` GROUP BY `name` HAVING name = "group"

rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Rows()
defer rows.Close()
for rows.Next() {
  ...
}

rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Rows()
defer rows.Close()
for rows.Next() {
  ...
}

type Result struct {
  Date  time.Time
  Total int64
}
db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Scan(&results)

3.3 更新

3.3.1 更新单个列

// 根据条件更新
db.Model(&User{}).Where("active = ?", true).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE active=true;

// User 的 ID 是 `111`
db.Model(&user).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;

// 根据条件和 model 的值进行更新
db.Model(&user).Where("active = ?", true).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true;

3.3.2 更新选定列

如果您想要在更新时选择、忽略某些字段,您可以使用 SelectOmit

/ 选择 Map 的字段
// User 的 ID 是 `111`:
db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET name='hello' WHERE id=111;

db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;

// 选择 Struct 的字段(会选中零值的字段)
db.Model(&user).Select("Name", "Age").Updates(User{Name: "new_name", Age: 0})
// UPDATE users SET name='new_name', age=0 WHERE id=111;

// 选择所有字段(选择包括零值字段的所有字段)
db.Model(&user).Select("*").Updates(User{Name: "jinzhu", Role: "admin", Age: 0})

// 选择除 Role 外的所有字段(包括零值字段的所有字段)
db.Model(&user).Select("*").Omit("Role").Updates(User{Name: "jinzhu", Role: "admin", Age: 0})

3.3.4 批量更新

如果没有通过 Model 指定一个含有主键的记录,GORM 会执行批量更新

// Update with struct
db.Model(User{}).Where("role = ?", "admin").Updates(User{Name: "hello", Age: 18})
// UPDATE users SET name='hello', age=18 WHERE role = 'admin';

// Update with map
db.Table("users").Where("id IN ?", []int{10, 11}).Updates(map[string]interface{}{"name": "hello", "age": 18})
// UPDATE users SET name='hello', age=18 WHERE id IN (10, 11);

3.4 删除

3.4.1 删除单条记录

删除一条记录时,删除对象需要指定主键,否则会触发批量删除,例如:

// Email 的 ID 是 `10`
db.Delete(&email)
// DELETE from emails where id = 10;

// 带额外条件的删除
db.Where("name = ?", "jinzhu").Delete(&email)
// DELETE from emails where id = 10 AND name = "jinzhu";

3.4.2 批量删除

db.Where("email LIKE ?", "%jinzhu%").Delete(&Email{})
// DELETE from emails where email LIKE "%jinzhu%";

db.Delete(&Email{}, "email LIKE ?", "%jinzhu%")
// DELETE from emails where email LIKE "%jinzhu%";

3.4.3 逻辑删除

如果你的模型包含了 gorm.DeletedAt字段(该字段也被包含在gorm.Model中),那么该模型将会自动获得软删除的能力!

当调用Delete时,GORM并不会从数据库中删除该记录,而是将该记录的DeleteAt设置为当前时间,而后的一般查询方法将无法查找到此条记录。

// user's ID is `111`
db.Delete(&user)
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;

// Batch Delete
db.Where("age = ?", 20).Delete(&User{})
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;

// Soft deleted records will be ignored when querying
db.Where("age = 20").Find(&user)
// SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;

被逻辑删除的数据,也能通过api可以查找出来:

db.Unscoped().Where("age = 20").Find(&users)
// SELECT * FROM users WHERE age = 20;

4、几个钩子函数

Hook 是在创建、查询、更新、删除等操作之前、之后调用的函数。

4.1 创建hook

GORM允许用户通过实现这些接口 BeforeSaveBeforeCreateAfterSaveAfterCreate来自定义钩子。 这些钩子方法会在创建一条记录时被调用:

func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
  u.UUID = uuid.New()

    if u.Role == "admin" {
        return errors.New("invalid role")
    }
    return
}

4.2 更新hook

GORM 支持的 hook 包括:BeforeSaveBeforeUpdateAfterSaveAfterUpdate. 更新记录时将调用这些方法:

func (u *User) BeforeUpdate(tx *gorm.DB) (err error) {
    if u.Role == "admin" {
        return errors.New("admin user not allowed to update")
    }
    return
}

4.3 删除hook

对于删除操作,GORM 支持 BeforeDeleteAfterDelete Hook,在删除记录时会调用这些方法:

func (u *User) BeforeDelete(tx *gorm.DB) (err error) {
    if u.Role == "admin" {
        return errors.New("admin user not allowed to delete")
    }
    return
}

5、事务

Gorm 支持直接调用事务控制方法(commit、rollback),例如:

// 开始事务
tx := db.Begin()

// 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db')
tx.Create(...)

// ...

// 遇到错误时回滚事务
tx.Rollback()

// 否则,提交事务
tx.Commit()

示例:

func CreateAnimals(db *gorm.DB) error {
  // 再唠叨一下,事务一旦开始,你就应该使用 tx 处理数据
  tx := db.Begin()
  defer func() {
    if r := recover(); r != nil {
      tx.Rollback()
    }
  }()

  if err := tx.Error; err != nil {
    return err
  }

  if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
     tx.Rollback()
     return err
  }

  if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
     tx.Rollback()
     return err
  }

  return tx.Commit().Error
}

6、总结

        总之,gorm是一个对go开发者非常友好且使用简单的db操作库,非常容易上手,学习成本很低,推荐使用。

        上面对操作数据库的基本操作已经介绍的七七八八了,完全可以满足日常开发,总结出来共享给更多的小伙伴,希望对大家有所帮助。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android SystemUI组件(07)锁屏KeyguardViewMediator分析

该系列文章总纲链接&#xff1a;专题分纲目录 Android SystemUI组件 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;本章节持续迭代之前章节的思维导图&#xff0c;主要关注左侧上方锁屏分析部分即可。 为了更好理解本文的内容&#xff0c;优先说明下SystemUI中与Ke…

CoreDNS实现跨集群service解析实践

CoreDNS实现跨集群service解析实践 背景介绍使用条件实现方案 CoreDNS是一款使用Go语言实现的专为云原生应用而生的DNS服务器。本文介绍CoreDNS在特定实际场景下的一种进阶使用实践&#xff0c;也许能为其他也在使用CoreDNS做服务发现的同学提供一些启发和思考。 背景介绍 在…

luceda ipkiss教程 76:设计光栅耦合器

案例分享&#xff1a;设计光栅耦合器 全部代码如下&#xff1a; from si_fab import all as pdk from ipkiss3 import all as i3 import numpy as npclass grating_coupler(i3.PCell):"""SOI grating coupler."""_name_prefix "grating_c…

mysql 05 InnoDB数据页结构

01.数据页结构的快速浏览 02.记录在页中的存储 在页的7个组成部分中&#xff0c;我们自己存储的记录会按照我们指定的 行格式 存储到 User Records 部分。但是在一开始生成页的时候&#xff0c;其实并没有 User Records 这个部分&#xff0c;每当我们插入一条记录&#xff0c…

单词记忆的化境:用思想的流水去淹没坚硬的石块

其实&#xff0c;鹅卵石通常都是很硬的。但是河底的石子&#xff0c;几乎大多都成了鹅卵石&#xff0c;它们被流水淹没&#xff0c;日复一日、夜以继日的冲刷着&#xff0c;没有了棱角。 在单词的记忆过程中&#xff0c;我们有太多的人&#xff0c;都有着不堪回首的往事&#x…

面试系列-携程暑期实习一面

Java 基础 1、Java 中有哪些常见的数据结构&#xff1f; 图片来源于&#xff1a;JavaGuide Java集合框架图 Java 中常见的数据结构包含了 List、Set、Map、Queue&#xff0c;在回答的时候&#xff0c;只要把经常使用的数据结构给说出来即可&#xff0c;不需要全部记住 如下&…

爬虫逆向学习(九):记录一个集cookie、请求参数、请求体、响应文本加密的站点反爬

此分享只用于学习用途&#xff0c;不作商业用途&#xff0c;若有冒犯&#xff0c;请联系处理 反爬前置信息 站点&#xff1a;aHR0cHM6Ly96d2Z3LmNxLmdvdi5jbi9pY2l0eS9pY2l0eS9lbmdpbmVlcmluZy9uYXZpZ2F0aW9u 接口&#xff1a;/icity/api-v2/cq.app.icity.engineering.Engine…

江科大51单片机

文章目录 led灯led点亮led闪烁流水灯 独立按键按键点灯按键消抖按键实现二进制流水灯按键实现流水灯 数码管静态数码管显示动态数码管显示 矩阵键盘定时器/中断串口通信led点阵屏DS1302实时时钟蜂鸣器AT24C02DS18B20LCD1602直流电机驱动AD/DA红外遥控 led灯 创建项目&#xff…

打印沙漏(最蠢的办法)

直接给代码&#xff0c;很好理解的 #include<bits/stdc.h> using namespace std; int s(int b){if(b<1)return 0;if(b2)return 1;for(int i3;i<sqrt(b);i){if(b%i0)return 0;}return 1; } int main(){int n;cin>>n;char c;cin>>c;vector<int>s;…

网络原理之IP协议(网络层)

目录 前言 什么是IP协议&#xff1f; IP协议的协议头格式 16位总长度&#xff08;字节数&#xff09; 16位标识、3位标志位和13位片偏移 8位生存时间 IP地址管理 1.动态分配IP 2.NAT机制&#xff08;网络地址转换&#xff09; NAT机制是如何工作的 NAT机制的优缺点…

解决启动docker desktop报The network name cannot be found的问题

现象 deploying WSL2 distributions ensuring main distro is deployed: checking if main distro is up to date: checking main distro bootstrap version: getting main distro bootstrap version: open \wsl$\docker-desktop\etc\wsl_bootstrap_version: The network name…

Windows驱动调试方法

单步调试驱动 驱动的调试不能直接在本机上进行&#xff0c;而是要放在虚拟机&#xff08;或其它设备&#xff09;中。这是因为在内核模式下&#xff0c;一个断点的触发将会停下整个系统而不只是单个进程。 在前面的文章里&#xff0c;使用了DbgPrint函数来进行日志的输出&…

基于python+django+vue的旅游景点数据分析系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…

Elasticsearch 分片迁移与移除集群节点操作

Elasticsearch 分片迁移与移除集群节点操作 问题背景 在单台服务器上部署了 7 个 Elasticsearch 节点&#xff0c;分别为 es-node1 到 es-node7&#xff0c;端口从 9201 到 9207。每个节点都承载大量数据&#xff0c;但没有设置副本分片。由于多个节点共享同一台服务器的硬件…

论文阅读 - SWATTING Spambots: Real-time Detection of Malicious Bots on X

https://web.archive.org/web/20240523035749id_/https://dl.acm.org/doi/pdf/10.1145/3589335.3651564 目录 ABSTRACT INTRODUCTION METHODOLOGY 3 RESULTS ABSTRACT 在 X&#xff08;前身为 Twitter&#xff09;等社交网络平台上&#xff0c;垃圾邮件机器人的活动日益…

【SpringBoot整合Redis测试Redis集群案例】

1、第一步&#xff0c;创建springboot项目&#xff0c;并导入依赖 如图&#xff0c;创建项目遇到的第一个问题就是&#xff0c;当type选择maven&#xff0c;jdk选择1.8时&#xff0c;java部分没办法选择1.8的版本&#xff0c;这怎么办呢&#xff1f; 原因&#xff1a;搜了一下…

【windows 11 安装maven】

从下载网址下载maven 解压&#xff08;路径无中文&#xff09; 配置本地仓库&#xff0c;用来存储jar包 配置仓库路径 配置文件路径&#xff1a;./conf/settings.xml(conf文件夹中的settings.xml文件中&#xff09; 定位到53行修改 <!-- localRepository| The path to the l…

第 1 章:Vue 核心

1. Vue 简介 1.1. 官网 英文官网: https://vuejs.org/中文官网: https://cn.vuejs.org/&#xff1a;中文官网里面【教程】和【API】是比较重要的。用到api就去查询&#xff0c;实践当中记忆更牢靠。 风格指南&#xff1a;官方推荐写的一个代码风格cookbook&#xff1a;编写v…

从更底层的角度理解网站的访问过程

文章目录 1.示例&#xff0c;访问www.baidu.com是如何返回数据的1.输入www.baidu.com回车2.检查本机的C:\Windows\System32\drivers\etc\hosts配置文件夹下有没有这个域名对应的映射&#xff1a; 1.示例&#xff0c;访问www.baidu.com是如何返回数据的 1.输入www.baidu.com回车…

光伏开发:一分钟生成光伏项目报告

传统光伏项目报告的编制往往需要收集大量数据、进行复杂计算与分析&#xff0c;耗时长且易受人为因素影响。自动生成光伏项目报告&#xff0c;依托大数据、云计算、人工智能等先进信息技术&#xff0c;实现了对光伏项目关键参数的快速分析、评估与预测。 一、核心功能与流程 1…