目录架构雏形
main.go:程序入口,路由管理。
storage目录:存放MySQL等数据库 *** 作。
user:用户模块。
后期可以继续新增一些模块:比如common,存放公共方法的包。
main.go代码
package main
import (
"net/http"
//内部包
// mysql "etcd_admin/storage"
user_edit "etcd_admin/user"
)
func main() {
// 当访问 http://127.0.0.1:9527/hello 时,进入这个方法
http.HandleFunc("/hello", HelloHandler)
//设置管理员(方法提取到etcd_admin/user目录下的文件)
http.HandleFunc("/addAdmin", user_edit.AddAdmin)
http.HandleFunc("/updateAdmin", user_edit.UpdateAdmin)
http.HandleFunc("/delAdmin", user_edit.DelAdmin)
http.HandleFunc("/getAdminList", user_edit.GetAdminList)
// 启动一个Web服务,并监听9527端口
http.ListenAndServe(":9527",nil)
}
//单纯作为测试演示方法
func HelloHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World"))
}
mysql.go代码
package storage
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
"fmt"
"errors"
)
type Storage struct {
db *sql.DB // 创建连接对象
}
func NewMySqlInit() (*Storage,error){
//Open函数可能只是验证其参数,而不创建与数据库的连接。如果要检查数据源的名称是否合法,应调用返回值的Ping方法。
//返回的DB可以安全的被多个go程同时使用,并会维护自身的闲置连接池。这样一来,Open函数只需调用一次。很少需要关闭DB。
db,err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/shop")
if err != nil {
// fmt.Println("数据源的名称不合法!")
return nil,err
}
//Ping检查与数据库的连接是否仍有效,如果需要会创建连接。
connect_err := db.Ping()
if connect_err != nil {
// fmt.Printf("连接失败!error:%v",connect_err)
return nil,err
}
// fmt.Println("连接数据库成功")
//结构体实例化
myStorage := &Storage {db:db}
return myStorage,nil
}
// 使用Exec()完成对INSERT、UPDATE、DELETE *** 作
// 调用这个方法的方式就是storage.insert()
func (s *Storage) Exec(sql string,args...interface{}) (int64,error){
//预编译语句(PreparedStatement)提供了诸多好处。PreparedStatement可以实现自定义参数的查询,通常会比手动拼接字符SQL串高效;
//还可以防止SQL注入攻击。因此一般情况下用PreparedStatement和Exec()完成对INSERT、UPDATE、DELETE *** 作。
stm, pre_err := s.db.Prepare(sql);
if pre_err != nil {
return 0,errors.New("prepare err: " + pre_err.Error())
}
defer stm.Close()
res,exec_err := stm.Exec(args...)
if exec_err != nil {
return 0,errors.New("exec err: " + exec_err.Error())
}
return res.RowsAffected()
}
//查询方法
func (s *Storage) Query(sql string,args...interface{}) (*sql.Rows,error){
//预编译语句(PreparedStatement)提供了诸多好处。PreparedStatement可以实现自定义参数的查询,通常会比手动拼接字符SQL串高效;
//还可以防止SQL注入攻击。因此一般情况下用PreparedStatement和Exec()完成对INSERT、UPDATE、DELETE *** 作。
stm, pre_err := s.db.Prepare(sql);
if pre_err != nil {
return nil,errors.New("Query prepare err: " + pre_err.Error())
}
defer stm.Close()
rows,exec_err := stm.Query(args...)
if exec_err != nil {
return nil,errors.New("Query exec err: " + exec_err.Error())
}
return rows,exec_err
}
//*Row类型返回值的Scan方法会返回ErrNoRows;否则,Scan方法会扫描结果第一行并丢弃其余行。
func (s *Storage) QueryRow(sql string,args...interface{}) (*sql.Row,error){
//预编译语句(PreparedStatement)提供了诸多好处。PreparedStatement可以实现自定义参数的查询,通常会比手动拼接字符SQL串高效;
//还可以防止SQL注入攻击。因此一般情况下用PreparedStatement和Exec()完成对INSERT、UPDATE、DELETE *** 作。
stm, pre_err := s.db.Prepare(sql);
if pre_err != nil {
return nil,errors.New("Query prepare err: " + pre_err.Error())
}
defer stm.Close()
row := stm.QueryRow(args...)
return row,nil
}
user_edit.go代码
package user
import (
"net/http"
"fmt"
"log"
sql1 "database/sql"
_ "github.com/go-sql-driver/mysql"
//内部包
mysql "etcd_admin/storage"
)
type Admin struct {
Id int
User_name string
Prefix_key string
Is_write int
}
//新增管理员
func AddAdmin(w http.ResponseWriter, r *http.Request) {
var sql string
//获取参数
user_name := r.FormValue("user_name")
password := r.FormValue("password")
prefix_key := r.FormValue("prefix_key")
is_write := r.FormValue("is_write")
//参数判断
if user_name == "" {
w.Write([]byte(fmt.Sprintf("用户名不能为空")))
return
}
if password == "" {
w.Write([]byte(fmt.Sprintf("密码不能为空")))
return
}
//默认是1可读可写,2代表只读
if is_write == "" {
is_write = "1";
}
//获取数据库连接
mysqlConnect,err := mysql.NewMySqlInit();
if err != nil {
w.Write([]byte(fmt.Sprintf("连接出错了:%v",err)))
return
}
//判断用户名是否已经存在
sql = "select id from etcd_admin where user_name=? limit 1";
row,err := mysqlConnect.QueryRow(sql,user_name)
var id int
//如果id为默认值0,则代表这个数据不存在,大于0,则代表已存在
err = row.Scan(&id)
if err != sql1.ErrNoRows && id >0 {
w.Write([]byte(fmt.Sprintf("数据已存在了:%v",err)))
return
}
//新增数据
sql = "insert into etcd_admin(user_name,password,prefix_key,is_write) values(?,?,?,?)"
//调用结构体的方法
rows,err := mysqlConnect.Exec(sql,user_name,password,prefix_key,is_write);
if err != nil {
w.Write([]byte(fmt.Sprintf("新增出错了:%v",err)))
return
}
w.Write([]byte(fmt.Sprintf("新增了 %d 行数据",rows)))
}
//更新管理员
func UpdateAdmin(w http.ResponseWriter, r *http.Request) {
var sql string
//获取参数
id := r.FormValue("id")
user_name := r.FormValue("user_name")
password := r.FormValue("password")
prefix_key := r.FormValue("prefix_key")
is_write := r.FormValue("is_write")
//参数判断
if id == "" {
w.Write([]byte(fmt.Sprintf("用户id不能为空")))
return
}
if user_name == "" {
w.Write([]byte(fmt.Sprintf("用户名不能为空")))
return
}
if password == "" {
w.Write([]byte(fmt.Sprintf("密码不能为空")))
return
}
//默认是1可读可写,2代表只读
if is_write == "" {
is_write = "1";
}
//获取数据库连接
mysqlConnect,err := mysql.NewMySqlInit();
if err != nil {
w.Write([]byte(fmt.Sprintf("连接出错了:%v",err)))
return
}
//新增数据
sql = "update etcd_admin set user_name=?,password=?,prefix_key=?,is_write=? where id=?"
//调用结构体的方法
rows,err := mysqlConnect.Exec(sql,user_name,password,prefix_key,is_write,id);
if err != nil {
w.Write([]byte(fmt.Sprintf("更新出错了:%v",err)))
return
}
w.Write([]byte(fmt.Sprintf("更新了 %d 行数据",rows)))
}
//删除管理员
func DelAdmin(w http.ResponseWriter, r *http.Request) {
var sql string
//获取参数
id := r.FormValue("id")
//参数判断
if id == "" {
w.Write([]byte(fmt.Sprintf("用户id不能为空")))
return
}
//获取数据库连接
mysqlConnect,err := mysql.NewMySqlInit();
if err != nil {
w.Write([]byte(fmt.Sprintf("连接出错了:%v",err)))
return
}
//新增数据
sql = "delete from etcd_admin where id=?"
//调用结构体的方法
rows,err := mysqlConnect.Exec(sql,id);
if err != nil {
w.Write([]byte(fmt.Sprintf("删除出错了:%v",err)))
return
}
w.Write([]byte(fmt.Sprintf("删除了 %d 行数据",rows)))
}
//获取管理员列表
func GetAdminList(w http.ResponseWriter, r *http.Request) {
var sql string
//获取参数
// user_id := r.FormValue("user_id")
//获取数据库连接
mysqlConnect,err := mysql.NewMySqlInit();
if err != nil {
w.Write([]byte(fmt.Sprintf("连接出错了:%v",err)))
return
}
//参数判断
// if user_id == "" {
// fmt.Println("all")
// sql = "select * from etcd_admin"
// rows,err := mysqlConnect.Query(sql);
// } else {
// fmt.Println("one")
// sql = "select * from etcd_admin where id=?"
// //调用结构体的方法
// rows,err := mysqlConnect.Query(sql,user_id);
// }
sql = "select id,user_name,prefix_key,is_write from etcd_admin"
rows,err := mysqlConnect.Query(sql);
defer rows.Close()
if err != nil {
w.Write([]byte(fmt.Sprintf("查询出错了:%v",err)))
return
}
var id,is_write int
var user_name,prefix_key string
var adminList []Admin
for rows.Next() {
//Scan的字段要与select筛选的字段数量和顺序一致
err := rows.Scan(&id,&user_name,&prefix_key,&is_write)
if err != nil {
log.Fatal(err)
}
u := Admin{
Id: id,
User_name: user_name,
Prefix_key: prefix_key,
Is_write: is_write,
}
adminList = append(adminList,u)
}
fmt.Println(adminList)
err = rows.Err()
if err != nil {
log.Fatal(err)
}
// fmt.Println(rows)
// fmt.Printf("类型是%T",rows)
// rows := 6777;
w.Write([]byte(fmt.Sprintf("name=%s ",user_name)))
//
}
不要把所有方法(接口)都写在main.go里面,将方法提取到其他模块下(比如user模块下),然后在main.go通过import引入外部包。为什么go的SQL查询那么繁琐呢???查询数据需要next(),scan()去处理数据。无语……修改代码后,需要杀掉端口占用的进程,再重新执行 go run main.go才能生效。我索性写个shell脚本,每次修改代码后,手动执行这个脚本,就会帮我找到端口占用的进程id并杀死它。然后我就能愉快的执行 go run main.go。(听起来我的脚本好像可有可无?如果是linux环境确实一行命令行就能完成了,可是我在window环境调试的,所以暂时用脚本代替)经验总结
不过,我更想知道,怎么才能不用每次杀死进程id以及重新执行main.go。mysql.go在storage包下,所以要写成package storage,不要写成package mysql。(一个目录下的同级文件归属一个包)
附录“举足轻重”的shell脚本。注:window环境下。
#!bin/bash
#获取占用9527端口的进程id。等号中间不能有空格!!!!
PID=`netstat -aon|findstr 9527 |findstr LISTENING | awk '{print }' | head -n1`
echo "pid is $PID"
#杀死这个进程id
echo `taskkill -pid $PID -f`
linux环境下杀死927端口占用的进程的命令:
netstat -antp | grep ::9527 | awk '{print }'|cut -d '/' -f 1 | xargs kill -9
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)