Go-zero框架学习+xorm+gorm配置

news2025/1/27 9:39:33

Go-zero框架学习+xorm+gorm配置

文章目录

  • Go-zero框架学习+xorm+gorm配置
  • 一、框架介绍
  • 二、go-zero快速搭建
    • 1.下载go-zero
    • 2.安装goctl
    • 3.项目初始化
    • 4.测试
    • 5.项目结构
    • 6.快速生成rpc服务
    • 7.快速添加api接口
    • 8.快速生成model服务
    • 8.快速生成Dockerfile
    • 9.快速生成K8s部署文件
  • 三.golang的ORM框架
  • 四. go-zero配置xorm和redis
    • 1.配置文件
    • 2.修改config
    • 3.修改serviceContext文件
    • 4.初始化Init方法
    • 5.logic业务逻辑调用
  • 五.go-zero配置gorm
    • 2.修改servicecontext.go文件
    • 3.修改greet.api文件
    • 4.修改logic文件
    • 5.运行测试
  • 六、go-zero架构设计-作者万俊峰的讲解
    • go-zero架构特点
    • 其他说明


一、框架介绍

go-zero 包含极简的 API 定义和生成工具 goctl,可以根据定义的 api 文件一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码,并可直接运行。

goctl是go-zero微服务框架下的代码生成工具。使用 goctl 可显著提升开发效率,让开发人员将时间重点放在业务开发上,其功能有:api服务生成,rpc服务生成,model代码生成,模板管理等。

protoc是一款用C++编写的工具,其可以将proto文件翻译为指定语言的代码。在go-zero的微服务中,我们采用grpc进行服务间的通信,而grpc的编写就需要用到protoc和翻译成go语言rpc stub代码的插件protoc-gen-go。

二、go-zero快速搭建

这节内容部分参考资料
go-zero框架简介与基本使用 - Abel Chan的文章 - 知乎

1.下载go-zero

get -u github.com/zeromicro/go-zero

2.安装goctl

go 1.15及以前版本:

go get -u github.com/zeromicro/go-zero/tools/goctl@latest

go 1.16 及以后版本:

go install github.com/zeromicro/go-zero/tools/goctl@latest

查看goctl版本

goctl -v

3.项目初始化

// 创建API服务
goctl api new greet
cd greet
go mod init
go mod tidy
// 启动服务
go run greet.go -f etc/greet-api.yaml

4.测试

访问: http://127.0.0.1:8888/from/you

或者linux
curl -i http://127.0.0.1:8888/from/you

备注:
默认启动端口号:8888
修改配置文件/etc/greet-api.yaml 可以修改端口号

5.项目结构

|____go.mod
|____etc //存放配置文件
| |____greet-api.yaml
|____internal
| |____handler // 路由与处理器
| | |____routes.go
| | |____greethandler.go
| |____types //中间类型
| | |____types.go
| |____config // 配置-对应etc下配置文件
| | |____config.go
| |____logic //逻辑处理
| | |____greetlogic.go
| |____svc //依赖资源
| | |____servicecontext.go
|____go.sum
|____greet.api //api接口与类型定义
|____greet.go //main.go 入口

6.快速生成rpc服务

使用proto文件自动生成
方式1:创建rpc服务

goctl rpc new greet

生成greet.proto文件如下

syntax = "proto3";

package rpc-greet;
option go_package="./rpc-greet";

message Request {
  string ping = 1;
}

message Response {
  string pong = 1;
}

service Rpc-Greet {
  rpc Ping(Request) returns(Response);
}

方式2:
通过定义好的proto文件,生成相应的rpc服务

goctl rpc template -o=userinfo.proto

userinfo.proto内容如下

syntax = "proto3";  //指定版本信息,不指定会报错,默认proto2
//option go_package = "./proto;userinfo";  // 分号后面是包名
option go_package = "./userService";

message userinfo{
    string username = 1;
    int32 age = 2;
    repeated string hobby = 3; //数组,golang对应string切片
}
// 转成go文件protoc --go_out=./ *.proto 

7.快速添加api接口

修改greet.api文件

service greet-api {
    @handler UserLogin
	post /user/login(LoginRequest) returns (LoginReply)
}
@server (
	middleware: Auth
)
service core-api{
	// 文件上传
	@handler FileUpload
	post /file/upload(FileUploadRequest) returns (FileUploadReply)	
}
type LoginRequest {
	Name     string `json:"name"`
	Password string `json:"password"`
}
	
type LoginReply {
	Token string `json:"token"`
}
type FileUploadRequest {
	Hash string `json:"hash,optional"`
	Name string `json:"name,optional"`
	Ext string `json:"ext,optional"`
	Size int64 `json:"size,optional"`
	Path string `json:"path,optional"`
}
	
type FileUploadReply {
	Identity string `json:"identity"`
	Ext string `json:"ext"`
	Name string `json:"name"`
}

自动生成代码

goctl api go -api greet.api -dir . 

或者
goctl api go -api greet.api -dir . -style go_zero

备注:
-style go_zero:自动生成的文件名使用下划线进行分隔,如:
service_context.go
user_file_delete_logic.go

生成代码后的文件结构如下:

自动生成handler文件和logic文件
|____go.mod
|____etc
| |____greet-api.yaml
|____internal
| |____handler
| | |____userhandler.go // 新增handler
| | |____routes.go
| | |____greethandler.go
| |____types
| | |____types.go // 新增type都在这里
| |____config
| | |____config.go
| |____logic
| | |____userlogic.go //新增的logic文件,处理具体业务逻辑
| | |____greetlogic.go
| |____svc
| | |____servicecontext.go
|____go.sum
|____greet.api
|____greet.go

8.快速生成model服务

方式1:通过ddl生成

goctl mysql goctl model mysql ddl -src="./*.sql" -dir="../" -c=true

备注:
-c : 是否加缓存

方式2:通过datasource生成

goctl model mysql datasource -url="user:password@tcp(127.0.0.1:3306)/database" -table="*" -dir="./"

生成文件示例
.
|____tdfsubscriptionmodel.go
|____vars.go
|____diversionrulesmodel.go
|____sql
| |____sub.sql

8.快速生成Dockerfile

goctl docker -go greet.go
.
|____go.mod
|____Dockerfile // 当前服务的Dockerfile文件
|____etc
| |____greet-api.yaml
|____internal
| |____handler
| | |____userhandler.go
| | |____routes.go
| | |____greethandler.go
| |____types
| | |____types.go
| |____config
| | |____config.go
| |____logic
| | |____userlogic.go
| | |____greetlogic.go
| |____svc
| | |____servicecontext.go
|____go.sum
|____greet.api
|____greet.go

9.快速生成K8s部署文件

goctl kube deploy -name redis -namespace adhoc -image redis:6-alpine -o redis.yaml -port 6379

生成的redis.yaml文件

三.golang的ORM框架

golang常用的几种orm框架,主流框架包括:

  • gorm
  • xorm
  • upper/db
  • gorose
  • worm
    接下来将针对如何配置xorm和gorm进行说明,新手入门项目,可以选择xorm或者gorm其中的一个作为选型;

四. go-zero配置xorm和redis

这块内容基于B站实战视频学习所得
【【项目实战】基于Go-zero、Xorm的网盘系统】

1.配置文件

/etc/core-api.yaml
增加mysql和redis的配置

Name: core-api
Host: 0.0.0.0
Port: 8888

Mysql:
  DataSource: root:password@tcp(127.0.0.1:3306)/cloud_disk?charset=utf8mb4&parseTime=True&loc=Local

Redis:
  Addr: 127.0.0.1:6379

2.修改config

/internal/config/config.go

package config

import "github.com/zeromicro/go-zero/rest"

type Config struct {
	rest.RestConf
	Mysql struct {
		DataSource string
	}
	Redis struct {
		Addr string
	}
}

3.修改serviceContext文件

/internal/svc/service_context.go
增加Engine和RDB

package svc

import (
	"cloud-disk/core/internal/config"
	"cloud-disk/core/models"
	"github.com/go-redis/redis/v8"
	"github.com/zeromicro/go-zero/rest"
	"xorm.io/xorm"
)

type ServiceContext struct {
	Config config.Config
	Engine *xorm.Engine
	RDB    *redis.Client
}

func NewServiceContext(c config.Config) *ServiceContext {
	return &ServiceContext{
		Config: c,
		Engine: models.Init(c.Mysql.DataSource),
		RDB:    models.InitRedis(c),
	}
}

4.初始化Init方法

/models/init.go

package models

import (
	"cloud-disk/core/internal/config"
	"log"

	"github.com/go-redis/redis/v8"
	_ "github.com/go-sql-driver/mysql"
	"xorm.io/xorm"
)

func Init(dataSource string) *xorm.Engine {
	engine, err := xorm.NewEngine("mysql", dataSource)
	if err != nil {
		log.Printf("Xorm New Engine Error : %v", err)
		return nil
	}
	return engine
}

func InitRedis(c config.Config) *redis.Client {
	return redis.NewClient(&redis.Options{
		Addr:     c.Redis.Addr,
		Password: "", // no password set
		DB:       0,  // use default DB
	})
}

5.logic业务逻辑调用

func (l *UserRegisterLogic) UserRegister(req *types.UserRegisterRequest) (resp *types.UserRegisterReply, err error) {
	// 调用redis获取验证码
	// 判断code是否一致
	code, err := l.svcCtx.RDB.Get(l.ctx, req.Email).Result()
	if err != nil {
		err = errors.New("该邮箱验证码为空")
		return nil, err
	}

	if code != req.Code {
		err = errors.New("验证码错误")
		return
	}

    // 使用xorm查询数据库
	// 判断用户名是否存在
	cnt, err := l.svcCtx.Engine.Where("name = ?", req.Name).Count(new(models.UserBasic))
	if err != nil {
		return nil, err
	}

	if cnt > 0 {
		err = errors.New("用户名已存在")
		return nil, err
	}

	user := &models.UserBasic{
		Identity: helper.UUID(),
		Name:     req.Name,
		Password: helper.Md5(req.Password),
		Email:    req.Email,
	}

	n, err := l.svcCtx.Engine.Insert(user)
	if err != nil {
		return nil, err
	}
	log.Println("insert user row:", n)
	res := &types.UserRegisterReply{}
	res.Message = "注册成功"
	return
}


五.go-zero配置gorm

这节内容参考了博客
【go-zero之gozero+gorm】

1、配置文件
/etc/greet-api.yaml增加数据库连接配置DataSourceName

Name: greet-api
Host: 0.0.0.0
Port: 8888

DataSourceName: root:password@tcp(127.0.0.1:3306)/cloud_disk?charset=utf8mb4&parseTime=True&loc=Local

/internal/config/config.go增加DataSourceName

type Config struct {
	rest.RestConf
	DataSourceName string
}

2.修改servicecontext.go文件

/internal/svc/servicecontext.go

package svc

import (
	"greet/internal/config"
	"greet/internal/models"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/schema"
)

type ServiceContext struct {
	Config  config.Config
	DbEngine *gorm.DB
}

func NewServiceContext(c config.Config) *ServiceContext {
	db, err := gorm.Open(mysql.Open(c.DataSourceName), &gorm.Config{
		NamingStrategy: schema.NamingStrategy{
			//TablePrefix:   "tech_", // 表名前缀,`User` 的表名应该是 `t_users`
			SingularTable: true,    // 使用单数表名,启用该选项,此时,`User` 的表名应该是 `t_user`
		},
	})
	//如果出错就GameOver了
	if err != nil {
		panic(err)
	}
	//自动同步更新表结构,不要建表了O(∩_∩)O哈哈~
	db.AutoMigrate(&models.User{})

	return &ServiceContext{
		Config:  c,
		DbEngine: db,
	}
}

3.修改greet.api文件

type RegisterRequest {
	Mobile string `json:"mobile"`
	Password string `json:"password"`
}
type RegisterResponse {
	Id int `json:"id"`
}

service greet-api {
	@handler Register
	post /register(RegisterRequest) returns (RegisterResponse)
}

使用代码生成

goctl api go -api greet.api -dir .

生成/logic/registerlogic.go

4.修改logic文件

/logic/registerlogic.go

func (l *RegisterLogic) Register(req *types.RegisterRequest) (resp *types.RegisterResponse, err error) {
	user := models.User{
		Mobile:   req.Mobile,
		Password: req.Password,
	}
	result := l.svcCtx.DbEngine.Create(&user)
	return &types.RegisterResponse{
		Id: user.Id,
	}, result.Error
}

/internal/models/user.go

package models

import (
	"errors"
	"greet/internal/utils"

	"gorm.io/gorm"
)

type User struct {
	gorm.Model
	Id       int `gorm:"type:int(64)"`
	Mobile   string `gorm:"index:mobile;type:varchar(13)"`
	Password string `gorm:"type:varchar(64)"`
}

// 在创建前检验验证一下密码的有效性
func (u *User) BeforeCreate(db *gorm.DB) error {
	if len(u.Password) < 6 {
		return errors.New("密码太简单了")
	}
	//对密码进行加密存储
	u.Password = utils.Password(u.Password)
	return nil
}

/internal/utils/password.go 工具方法

package utils

import (
	"fmt"

	"golang.org/x/crypto/bcrypt"
)

// 密码加密
func Password(plainpwd string) string {
	//谷歌的加密包
	hash, err := bcrypt.GenerateFromPassword([]byte(plainpwd), bcrypt.DefaultCost) //加密处理
	if err != nil {
		fmt.Println(err)
	}
	encodePWD := string(hash) // 保存在数据库的密码,虽然每次生成都不同,只需保存一份即可
	return encodePWD
}

// 密码校验
func CheckPassword(plainpwd, cryptedpwd string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(cryptedpwd), []byte(plainpwd)) //验证(对比)
	return err == nil
}

数据表设计如下
user表
在这里插入图片描述

5.运行测试

运行服务

go mod tidy
go run greet.go -f etc/greet-api.yaml

postman发送请求
http://127.0.0.1:8888/register
参数

{
    "mobile": "123233123123",
    "password":"111234311"
}

在这里插入图片描述

查看数据表
在这里插入图片描述
数据添加成功


六、go-zero架构设计-作者万俊峰的讲解

原视频链接:
【#101 晓黑板 go-zero 微服务框架的架构设计】

go-zero架构特点

1、进程内限流

  • 控制并发请求
  • 简单高效
  • 可配置,有默认值
  • 有效防止突发恶意流量
  • 第一道防护(WAF等除外)

2、分布式限流

  • 基于redis/lua
  • 令牌桶
  • 漏桶
  • 广泛使用,短信、推送等

3、基于优先级进行分级降载保护

  • K8S的HPA 80%触发
  • CPU>90%开始拒绝低优先级请求
  • CPU>95%开始拒绝高优先级请求
  • http/rpc框架内建
  • 基于滑动窗口,防止毛刺
  • 有冷却时间,防止抖动
  • 实践检验,配合K8S弹性伸缩
  • 第二道防护

4、路径级别的自适应熔断

  • 自动触发,自动恢复
  • http/rpc框架内建
  • Google SRE算法
  • 放弃了Netfix Hystrix算法
  • 基于滑动窗口(10秒/40秒窗口)
  • 支持自定义触发条件
  • 支持自定义fallback
  • 第三道防护

其他说明

1、多重防护,保障高可用
请求–》并发控制、限流–》自适应降载、K8S弹性伸缩 --》自适应熔断、负载均衡

2、重试机制
不自动重试

重试注意事项:

  • 指数退避
  • 流量quota
  • 超时相关性

3、缓存设计三要点

  • 缓存穿透,不存在的数据
  • 缓存击穿,热点key过期
  • 缓存雪崩,大量缓存同时过期

4、缓存解决办法

  • 缓存穿透:即使查不到,也自动缓存,短过期时间,1分钟
  • 缓存击穿:确保一个进程同时只拿一次数据,并共享结果
  • 缓存雪崩:针对缓存过期时间设置随机偏差,确保分散过期
  • 缓存基于非主键查询:转换为主键做缓存;获取复杂,查询->主键->缓存
  • 分布式缓存:多虚拟节点一致性hash,避免降级后过多cache miss

5、可观测性

  • 链路跟踪
  • Logging
  • Metrics
  • 监控报警

(1)链路跟踪

  • 框架自建,context传递
  • Trace id, 贯穿整个调用链
  • Span id, 有层级和时序关系
  • 记录起止时间
  • 记录调用关系,client/server

(2)Logging

  • 自动rotate
  • 多模式支持,console,file,volume
  • 自动压缩
  • 自动删除过期日志

(3)监控报警

  • 自动聚合汇报异常,比如http code 5xx
  • 自动控制频率并汇总异常

6、数据上报

  • 上报到控制台服务
  • 上报到prometheus

7、其他

  • 基于JWT的自动鉴权
  • MapReduce
  • Graceful shutdown
  • 并发控制工具箱
  • 资源控制工具箱,比如多个线程同时创建同一个数据库链接
  • 分布式高可用延迟任务框架
  • 极简Kafka Pub/Sub框架
  • Logstash 5倍性能的go-stash框架

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

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

相关文章

[go学习笔记.第十七章.redis的使用] 1.redis的使用

1.redis基本介绍 (1).Redis 是 NoSQL 数据库&#xff0c;不是传统的关系型数据库,官网: https://redis.io/ 和http://redis.cn/ (2).Redis: REmote Dlctionary Sever&#xff08;远程字典服务器&#xff09;, Redis 性能非常高&#xff0c;单机能够达到 15w qps,通常适合做缓存…

刷爆力扣之盛最多水的容器

刷爆力扣之盛最多水的容器 HELLO&#xff0c;各位看官大大好&#xff0c;我是阿呆 &#x1f648;&#x1f648;&#x1f648; 今天阿呆继续记录下力扣刷题过程&#xff0c;收录在专栏算法中 &#x1f61c;&#x1f61c;&#x1f61c; 该专栏按照不同类别标签进行刷题&#xff…

安卓的分区一点有用知识:super、lpunpack、lpdump

我们知道这个安卓的镜像分区有很多个。 那么这个文章要介绍什么呢&#xff1f; 三个点&#xff1a; 一是现在的android支持动态分区&#xff0c;很多的东西都被放到super分区里面了&#xff0c;这个应该是可以配置的。然后super里面有比如system、vendor这种比较大的分区。那…

教务排课系统毕业设计,大学排课系统设计与实现,排课系统论文作品参考

功能清单 【后台管理员功能】 录入分院&#xff1a;录入分院名称&#xff0c;简介&#xff0c;详情 分院管理&#xff1a;管理已经录入分院&#xff0c;支持修改和删除 老师录入&#xff1a;录入老师姓名、联系方式、头像、老师简介 老师管理&#xff1a;管理所有已经录入老师…

基于Matlab模拟用于海况海洋学研究的 X 波段雷达系统(附源码)

目录 一、定义雷达系统参数 二、对海面进行建模 三、配置雷达收发器 四、生成数据多维数据集 五、处理海面回波 六、总结 七、程序 海事雷达系统在充满挑战的动态环境中运行。为了改进对感兴趣目标的检测并评估系统性能&#xff0c;必须了解海面返回的性质。 在本例中&a…

【操作系统】2.4 死锁

这一节也非常重要 2.4.1 死锁的概念 2.4.1 死锁的概念_StudyWinter的博客-CSDN博客 在并发环境下&#xff0c;各种进程因竞争资源而造成的一种互相等待对方手里的资源&#xff0c;导致各进程都阻塞&#xff0c;都无法向前推进的现象。这就是死锁&#xff0c;死锁发生后&#…

Jest API使用方法

如上面的知识图谱所示&#xff0c;一个常见的测试框架通常需要实现这些功能: ● before/after 钩子函数: 如beforeEach&#xff0c;afterEach&#xff0c; ● Mock方法&#xff1a; 函数Mock&#xff0c;时间mock等。 ● 断言: 判断一个描述是否正确&#xff0c;在Jest中常为 e…

你心心念念的RabbitMQ个人实践来了来了它来了

前言 MQ&#xff08;Message Queue&#xff09;就是消息队列&#xff0c;其有点有很多&#xff1a;解耦、异步、削峰等等&#xff0c;本文来聊一下RabbitMQ的一些概念以及使用。 RabbitMq 案例 Springboot整合RabbitMQ简单案例 基本概念 Exchange&#xff1a;消息交换机&a…

云原生系列 【基于CCE Kubernetes编排实战二】

✅作者简介&#xff1a; CSDN内容合伙人&#xff0c;全栈领域新星创作者&#xff0c;阿里云专家博主&#xff0c;阿里云问答板块版主&#xff0c;华为云享专家博主&#xff0c;掘金后端评审团成员 &#x1f495;前言&#xff1a; 最近云原生领域热火朝天&#xff0c;那么云原生…

Hystirx限流:信号量隔离和线程池隔离

背景&#xff1a; 最近工作中要处理服务高并发的问题&#xff0c;大流量场景下限流熔断降级可以说是必不可少的&#xff0c;打算对限流做一次改造&#xff0c;所以要先了解一下hytrix相关内容&#xff0c;比如了解一下线程池隔离和信号量隔离的区别。 **信号量&#xff1a;**信…

[网络工程师]-应用层协议-DHCP

BOOTP是最早的主机配置协议&#xff0c;动态主机配置协议&#xff08;Dynamic Host Configuration Protocol&#xff0c;DHCP&#xff09;则是在其基础上进行了改良的协议&#xff0c;是一种用于简化主机IP配置管理的IP管理标准。通过DHCP协议&#xff0c;DHCP服务器为DHCP客户…

集合学习笔记——Collection 全家桶

Collection是我们日常开发中使用频率非常高的集合&#xff0c;它的主要实现有List和Set,区别是List是有序的&#xff0c;元素可以重复;Set是无序的&#xff0c;元素不可以重复&#xff0c;我们简单看下继承关系&#xff1a; List的实现类主要线程不安全的ArrayList和LinkedList…

推挽输出和开漏输出-三极管-mos管

一、推挽输出 1.1推挽输出的概念 推挽&#xff08;push-pull&#xff09;输出是由两个MOS或者三极管组成&#xff0c;两个管子始终保持一个导通&#xff0c;另一个截止的状态。 图1 推挽电路示意图 当输入高电平时&#xff0c;叫做推&#xff1b; 上管Q1导通&#xff0c;下管…

【目标检测】Faster R-CNN论文的讲解

目录&#xff1a;Faster R-CNN论文的讲解一、前言二、回顾Fast R-CNN三、引入Faster R-CNN四、Faster R-CNN的介绍4.1 框架结构4.2 RPN如何产生候选区域的4.3 损失函数4.4 训练候选框提取网络4.5 RPN和Fast R-CNN共享特征的方法4.5.1 交替训练法4.5.2 近似联合训练法一、前言 …

C语言——学生信息管理系统

目录 功能展示 界面展示 所有功能模块&#xff1a; 功能1&#xff1a;菜单模块&#xff08;显示功能菜单&#xff09; 功能2&#xff1a;增加学生信息 功能3&#xff1a;输出学生信息&#xff08;查看所有学习信息&#xff09; 功能4&#xff1a;修改学生信息 功能5&a…

python3-GUI概述及应用

目录一、什么是GUI二、Python GUIPySimpleGUI概述一、PySimpleGUI简介二、PySimpleGUI特征三、输出设备hello,world猜数字一、玩家猜数字二、电脑猜数字21点游戏一、21点游戏简介二、程序代码一、什么是GUI 图形用户界面&#xff08;Graphical User Interface&#xff0c;简称…

十六、CANdelaStudio深入-CDD与CDDT的差异(新建自定义服务)

本专栏将由浅入深的展开诊断实际开发与测试的数据库编辑,包含大量实际开发过程中的步骤、使用技巧与少量对Autosar标准的解读。希望能对大家有所帮助,与大家共同成长,早日成为一名车载诊断、通信全栈工程师。 本文介绍CANdelaStudio的CDD与CDDT的差异与新建自定义服务,欢迎…

数字图像处理(一)——什么是数字图像

一、什么是数字图像处理&#xff1f; 一副图像可以被定义为一个二维函数f(x,y)&#xff0c;其中x和y是空间平面坐标&#xff0c;而对任意一对空间坐标(x,y)处幅值f称为图像在该点的强度或者灰度。当x和y以及灰度值f是有限的离散数值时&#xff0c;我们称该图像为数字图像。像素…

排序算法简述

一、概述 常见的排序算法有冒泡排序、插入排序、选择排序、快速排序、归并排序、桶排序、基数排序&#xff0c;这些排序各自有各自的特点。按照时间时间复杂度可以分为 O(n^2):冒泡、插入、选择排序&#xff1b;O(nlogn):归并、快速排序&#xff1b;O(n):桶排序、计数排序、基…

[附源码]java毕业设计自治小区物业设备维护管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…