go
这些基础的东西,看起来很舒服,最起码对于写习惯java
的人来讲,go
真的很舒服,所以近一段时间可能一直连更,gin
并没有直接封装操作MySQL
的工具类,所以还是使用开源的工具
github.com/garyburd/redigo v1.6.2
github.com/go-sql-driver/mysql v1.5.0
github.com/jmoiron/sqlx v1.2.0
go
操作mysql
数据库当前代码文件结构
|- mian.go
|- app
| |- api
| |- student_api.go
| |- model
| |- student
| |- student.go
|- routers
| |- routers.go
|- util
| |- util.go
myschool
数据库,以及student
表DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` varchar(225) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`name` varchar(225) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`age` int(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
对比goframe
框架的话,gin
看起来不是很整洁,因为没有封装操作数据库的工具,所以我们只能用原生的代码来操作,需要写死配置,对比Java
的SpringBoot
确实差一点,但是对比JDBC
的话,go
可就好太多了
这里的话,新建util.go
用于获取数据库连接,将路由、功能、工具区分开
var Db *sqlx.DB
func Init() {
// 获取 MySQL 链接需要自己导入 _ "github.com/go-sql-driver/mysql"
database, err := sqlx.Open("mysql", "root:Root5683@@tcp(127.0.0.1:3306)/myschool")
//database, err := sqlx.Open("数据库类型", "用户名:密码@tcp(地址:端口)/数据库名")
if err != nil {
fmt.Println("open mysql failed,", err)
return
}
Db = database
// 关闭数据库连接
//defer database.Close() // 注意这行代码要写在上面err判断的下面
}
连接创建完成下一步,我们需要创建路由,这里选择分组路由,方便后面继续做拓展
创建routers.go
func Router() *gin.Engine {
r := gin.Default()
// 初始化数据连接
util.Init()
s := r.Group("/student")
{
s.PUT("/save", api.Save)
s.GET("/select", api.SelectById)
s.POST("/update", api.Update)
s.DELETE("/delete", api.Delete)
}
return r
}
启动类肯定是必不可少的,接下来看下一下main.go
func main() {
// 加载路由
r := routers.Router()
// 默认端口:8080
r.Run()
}
结构实体肯定是必不可少的,student.go
,这里就要夸一夸goframe
框架了,可以自动生成,但是没有的话,自己麻烦一点,也就麻烦那一下,也不是说很多个字段那么夸张
type Student struct {
Id string `orm:"id,primary" json:"id"`
Name string `orm:"name" json:"name"`
Age int `orm:"age" json:"age"`
}
crud
准备工作做好,下面就是重点了,对student
表进行操作crud
,接口测试使用Postman
// 新增学生
/*
http://127.0.0.1:8080/student/save
{
"id":"1",
"name":"chenghao_",
"age":30
}
*/
func Save(c *gin.Context) {
// 获取传递的参数 转换成 struct
var stu student.Student
if err := c.ShouldBindJSON(&stu); err != nil {
// 返回错误信息
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
result, err := util.Db.Exec("insert into student(id,name,age) values(?,?,?)", stu.Id, stu.Name, stu.Age)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
log.Println(result)
c.JSON(http.StatusOK, gin.H{"success": "添加成功"})
}
// 根据 id 查询
/*
http://127.0.0.1:8080/student/select?id=1
*/
func SelectById(c *gin.Context) {
var stu []student.Student
// 获取查询参数
id := c.Query("id")
err := util.Db.Select(&stu, "select id,name,age from student where id =?", id)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"success": stu})
}
// 修改
/*
http://127.0.0.1:8080/student/update
{
"id":"1",
"name":"chenghao_",
"age":19
}
*/
func Update(c *gin.Context) {
// 获取传递的参数 转换成你 struct
var stu student.Student
if err := c.ShouldBindJSON(&stu); err != nil {
// 返回错误信息
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
result, err := util.Db.Exec("update student set name=?,age=? where id=?", stu.Name, stu.Age, stu.Id)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
log.Println(result)
c.JSON(http.StatusOK, gin.H{"success": "修改成功"})
}
// 删除
/*
http://127.0.0.1:8080/student/select?id=1
*/
func Delete(c *gin.Context) {
// 获取查询参数
id := c.Query("id")
result, err := util.Db.Exec("delete from student where id =?", id)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"success": result})
}
go
也相应的支持MySQL事务
,看业务逻辑自行调用
1. Db.Begin() 开始事务
2. Db.Commit() 提交事务
3. Db.Rollback() 回滚事务
go
操作Redis
Redis
高性能的key-value
数据库,需要了解的自行学习,这里只做简单的使用
获取数据链接
func main() {
c, err := redis.Dial("tcp", "127.0.0.1:6379")
if err != nil {
log.Println("conn redis failed,", err)
return
}
}
String
类型使用c.Do(commandName string, args ...interface{})
向服务器发送命令并返回收到的答复,低一个是操作名称,这个和redis
原生命令有点像,只是命令首字母是大写的,java
的jedis
有点类似,只是jedis
是直接点set()
方法,且首字母小写
redis
操作String
类型,
func main() {
// 获取操作redis的链接
c, err := redis.Dial("tcp", "localhost:6379")
if err != nil {
log.Println("conn redis failed,", err)
return
}
// 设置一个key为name 值为Shao Jie
_, err = c.Do("Set", "name", "Shao Jie")
if err != nil {
log.Println(err.Error())
return
}
// 取值 取一个key为name的值
name, err := redis.String(c.Do("Get", "name"))
if err != nil {
log.Println(err.Error())
return
}
log.Println(name)
}
需要注意一个问题,c.Do
方法在操作Get
的时候,会出现乱码,这里用redis.String
转了一下类型,正常输出,如果是一个数字类型可以调用redis.Int
String
类型// 设置一个key是name一个为age 值为Shao Jie、21
_, err = c.Do("MSet", "name", "shaojie_", "age", 21)
if err != nil {
log.Println(err.Error())
return
}
// 取值 取一个key为name一个为age的值
result, err := redis.Strings(c.Do("MGet", "name", "age"))
if err != nil {
log.Println("get name failed,", err)
return
}
for _, v := range result {
log.Println(v)
}
需要注意,这里我们取值的时候取的是批量的,所以,这里要注意转型redis.Strings
,数字类型调用redis.Ints
// 设置一个key为name 值为shaojie_
_, err = c.Do("Set", "name", "shaojie_")
if err != nil {
log.Println(err.Error())
return
}
// 设置过期时间 10 为秒
_, err = c.Do("expire", "name", 10)
if err != nil {
fmt.Println(err)
return
}
// 取值 取一个key为name的值
name, err := redis.String(c.Do("Get", "name"))
if err != nil {
log.Println(err.Error())
return
}
log.Println(name)
// 休眠十秒
time.Sleep(10 * time.Second)
// 取值 十秒取一个key为name的值
name1, err := redis.String(c.Do("Get", "name"))
if err != nil {
log.Println(err.Error())
return
}
log.Println(name1)
这里显得比java
要麻烦一些,需要单独使用expire
为特定的key
设置过期时间,单位是秒
list
类型// 设置一个key为namelist 值为shaojie_ 、chenghao_
_, err = c.Do("lpush", "namelist", "shaojie_","chenghao_")
if err != nil {
log.Println(err.Error())
return
}
// 取值 获取一个值为 namelist的值 获取这个值之后会删除这个值
namelist, err := redis.String(c.Do("lpop", "namelist"))
if err != nil {
log.Println("get namelist failed,", err)
return
}
log.Println(namelist)
hash
类型// 设置 shaojie_ 的年龄age为21
_, err = c.Do("HSet", "shaojie_", "age", 21)
if err != nil {
log.Println(err)
return
}
// 获取 shaojie_ 的年龄age
age, err := redis.Int(c.Do("HGet", "shaojie_", "age"))
if err != nil {
fmt.Println("get shaojie_'s age failed,", err)
return
}
log.Println(age)
以上三种类型是我们常会用到的,后面都需要获取redis
的连接显得有些麻烦,不可能每次使用都去连接一次,如果想像操作mysql
一样只获取一次,然后直接可以在别得地方调用,那就很舒服,所以,可以借用redis连接池
redis
连接池获取redis
连接池,这个我们同样在util.go
中得Init
方法中调用
var Pool *redis.Pool //创建redis连接池
func Init() {
Pool = &redis.Pool{ //实例化一个连接池
MaxIdle: 16, //最初的连接数量
// MaxActive:1000000, //最大连接数量
MaxActive: 0, //连接池最大连接数量,不确定可以用0(0表示自动定义),按 需分配
IdleTimeout: 300, //连接关闭时间 300秒 (300秒不使用自动关闭)
Dial: func() (redis.Conn, error) { //要连接的redis数据库
return redis.Dial("tcp", "127.0.0.1:6379")
},
}
}
创建好工具后,需要在系统启动时调用,去创建连接池,然后再以hash
类型举例,获取在上面设置好的一个key
的值
func main() {
// 初始化工具的连接
util.Init()
c := util.Pool.Get() //从连接池,取一个链接
defer c.Close() //函数运行结束 ,把连接放回连接池
// 拿key为shaojie_的age的具体值
r, err := redis.Int(c.Do("HGet", "shaojie_","age"))
if err != nil {
fmt.Println("get abc faild :", err)
return
}
log.Println(r)
util.Pool.Close() //关闭连接池
}
这一整个的流程文档讲的还算比较清楚,对比java
学习事半功倍,最近学习起来感觉极其上瘾,但是平常公司有点小忙,也算是忙里偷闲去学习,多学习才会有更多的选择,学无止境,苦海无涯
借鉴一位学长的话:
每天要学两个小时。 --小石头
这句话其实给我说了很久,我很少有机会去做,但是却没有忘记,没有理由没有借口,如果习惯去找借口,那学习这件事情不如就此放弃,共勉!