Go语言实现ORM框架

时间:2025-02-22 07:25:43

一、项目结构

beego:请求访问控制器

controller:控制器结构体

models:定义需要映射建表的结构体

orm:实现models中结构体

routers:初始化路由入口

 

二、控制器接口设计

ControllerInterface:控制器接口,提供Get和Post函数。

Controller:公共控制器,实现ControllerInterface接口,并实现了Get和Post函数。

自定义Controller:继承Controller,根据业务需求处理不同请求。

上面三个Controller的关系如下所示:

自定义Controller(我们程序员写的)----------->Controller(公共Controller)------------->ControllerInterface(接口)

(1)定义ControllerInterface接口

// 控制器接口
type ControllerInterface interface {
	Get()
	Post()
}

(2)定义Controller结构体,并实现ControllerInterface的Get和Post方法。

// 定义一个控制器结构,实现ControllerInterface接口
type Controller struct {}

func (c *Controller) Get() {
	("GET空实现")
}

func (c *Controller) Post() {
	("Post空实现")
}

(3)定义一个LoginController和RegistController,继承Controller。

type LoginController struct {
	
}

func (this *LoginController) Get(){
	("LoginController get method is executing....")
}

func (this *LoginController) Post(){
	("LoginController post method is executing....")
}

// 自己定义方法,处理Post或者Get请求
func (this *LoginController) LoginGet(){
	("LoginController LoginGet method is executing...")
}

func (this *LoginController) LoginPost(){
	("LoginController LoginPost method is executing...")
}


type RegisterController struct {
	
}

func (this *RegisterController) Get(){
	("RegistController get method is executing....")
}

func (this *RegisterController) Post(){
	("RegistController post method is executing....")
}

func (this *RegisterController) RegistGet(){
	("RegistController RegistGet method is executing....")
}

 

三、路由设计

定义ControllerInfo结构体,用于存储路由信息 。

// 该结构体存储路由信息
type ControllerInfo struct {
	pattern        string                     // 存储请求url
	controllerType                // 存储控制器类型
	methods        map[string]string          // 存储请求方法
	initialize     func() ControllerInterface // 初始化方法
}

在文件中定义init方法,程序启动时候调用该方法初始化路由信息。

func init() {
	//将自己编写的控制器,匹配到路由上
	("/login",&{},"get:LoginGet;post:LoginPost")
	("/register",&{})
}

在main方法中导入routers包。

import (
	......
	_ "Project012/routers"
)

定义Router方法,把路由信息解析成ControllerInfo对象,并保存在上下文中。

// 该切片存储所有的路由信息
var routers []*ControllerInfo
var ctx  // context代表​

// 定义路由方法
// 参数一:访问地址
// 参数二:控制器接口对象的地址
// 参数三:访问方式和方法的映射关系
func Router(pattern string, c ControllerInterface, mappingMethods ...string) {
	// 获取控制器接口的Value对象
	cValue := (c)
	//获取控制器接口的Type对象
	cType := (cValue).Type()
	// 该map存储请求方式和方法的映射关系
	methods := make(map[string]string)
	if len(mappingMethods) > 0 {
		// 映射格式一:get:Login;post:HandleLogin
		mappings := (mappingMethods[0], ";")
		for _, v := range mappings {
			mapping := (v, ":")
			if len(mapping) != 2 {
				panic("method mapping format is invalid")
			}
			// 映射格式二:get,post:login
			requestMethods := (mapping[0], ",")
			for _, requestMethod := range requestMethods {
				// 判断请求方式是否正确和映射方法是否存在,只有两个条件同时成立,则保存在methods中
				if requestMethod == "*" || HTTPMETHOD[(requestMethod)] {
					// 判断映射方法是否在Controller中存在
					if val := (mapping[1]); () {
						// 把方法名保存在methods中
						methods[(requestMethod)] = mapping[1]
					} else {
						//异常
						panic("'" + mapping[1] + "' method doesn't exist in the controller " + ())
					}
				} else {
					panic(v + " is an invalid method mapping. Method doesn't exist " + requestMethod)
				}
			}
		}
	}
	// 创建ControllerInfo对象
	route := &ControllerInfo{}
	// 初始化ControllerInfo对象
	 = pattern
	 = methods
	 = cType
	 = func() ControllerInterface {
		// 使用放射创建Controller的Type对象
		o := ()
		// 把o转换成ControllerInterface类型
		controllerInterface, ok := ().(ControllerInterface)
		// 判断控制器的类型是否是ControllerInterface类型,如果不是抛出异常
		if !ok {
			panic("controller is not ControllerInterface")
		}
		// ----------把控制器c中的数据赋值到控制器controllerInterface中---------
		// 获取传入控制器接口的值
		controllerValueEl := (c).Elem()
		// 获取控制器的类型
		controllerTypeEl := (c).Elem()
		// 获取新创建控制器的值
		controllerInterfaceValue := (controllerInterface).Elem()
		// 获取控制器中字段个数
		numOfFields := ()
		for i := 0; i < numOfFields; i++ {
			// 根据索引获取字段类型
			fieldType := (i)
			// 判断字段是否可以修改,只有该字段可以修改的情况下才设置字段的值
			elemField := ()
			if () {
				// 设置字段的值
				fieldVal := (i) //赋值给elemField
				(fieldVal)
			}
		}
		return controllerInterface
	}
	routers = append(routers, route)
	// 将每个ControllerInfo中信息存储在routes中后,需要将routes中信息存储在上下文环境中,便于后续获取
	ctx = ((), "routers", routers)
}

​

定义请求结构体。

var (
	HTTPMETHOD = map[string]bool{
		"GET":  true,
		"POST": true,
	}
)

 

四、监听浏览器请求

(1)定义Run函数,项目启动时候调用该函数。

func Run() {
	//监听网络请求
	(":8080", HttpHandler{})
}

(2)定义HttpHandler,该对象提供了ServeHTTP方法处理请求。

type HttpHandler struct {
	// 实现Handler接口
}

func (HttpHandler) ServeHTTP(respWriter , request *) {
	//在此处去获取请求方式,请求链接,根据这2部分信息,匹配routes中的路由信息,调用相应方法
	//获取Get或者Post方法,拿到方法以后,首先匹配路由,然后看看此路由,是否重新定义Get或者Post方法
	method :=      // 请求方式
	path :=  // 请求路径
	// 过滤请求,google浏览器默认会请求这个图片,所以过滤掉
	if path != "/" {
		var controllerInterface ControllerInterface
		// 从上下文中获取routers
		routers := ("routers").([]*ControllerInfo)
		if routers != nil {
			// 遍历routers
			for _, controllerInfo := range routers {
				// 判断请求路径是否匹配
				if  == path {
					// 如果路径匹配,再判断initialize方法是否存在。如果存在则调用该方法执行初始化操作
					if  != nil{
						controllerInterface = ()
						// 遍历controllerInfo中的methods
						for key, value := range  {
							if key == method {
								method = value //获取控制器中处理请求的方法名
								break
							}
						}
						break
					}
				}
			}
		}
		//("controllerInterface = ", controllerInterface)
		switch method {
			case :
				()
			case :
				()
			default:
				// 调用控制器的处理方法
				vc := (controllerInterface)
				method := (method)
				in := make([], 0)
				(in)
		}
	}
}

(3)修改main函数。

func main() {
	()
}

 

五、建表

(1)表设计

type User struct {
   Id       int
   Name     string `orm:"size(20)"` //用户名
   Age        int    `orm:"size(10)"`        //登陆密码
   Email    string `orm:"size(50)"`        //邮箱
}

type Goods struct { //商品SPU表
   Id        int
   Name   string `orm:"size(20)"`  //商品名称
   Detail     string `orm:"size(200)"` //详细描述
}

(2)在models包下定义init方法,程序启动时候自动调用该方法进行建表操作。

func init(){
   ("mysql", "root:root@tcp(127.0.0.1:3306)/user?charset=utf8")
   (new(User),new(Goods))
}

(3)修改文件,导入models包。

import (
	......
	_ "Project012/models"
)

(4)在orm包下定义RegisterDataBase和RegisterModel函数。

var db *
var err error

// 连接数据库
func RegisterDataBase(driverName ,dataSourceName string) {
	db, err := (driverName,dataSourceName)
	if err != nil {
		("数据库开启失败")
		return
	}
	errPing := ()
	if errPing!=nil {
		("数据库链接失败")
		return
	}
}

//在数据库中建表方法
func RegisterModel(models ...interface{}){
	//根据传递进来的多个结构体的对象,进行建表操作
	//1.因为models中有多个对象,所以在此处需要对models进行循环遍历,每遍历一次,就需要创建一个sql
	//2.根据遍历的sql,进行sql语句触发,相应进行建表操作
	sqlInfo := getModelsInfo(models)
	for _, sql := range sqlInfo {
		pstmt, err := (sql)
		if err != nil {
			return
		}
		()
	}
	defer ()
}

var sqlStr []string

// 通过反射获取结构体中字段,拼接sql
func getModelsInfo(models []interface{}) []string {
	for index, model := range models {
		modelType := (model) //*结构体指针
		tableName := ().Name()
		sql := ""
		for i := 0; i < ().NumField(); i++ {
			curFieldName := ().Field(i).Name
			curFieldTag := ().Field(i).Tag
			curFieldType := ().Field(i).()
			tagValue := ().Field(i).("orm") // 获取指定名字的标签的内容

			if (curFieldName) == "id" {
				//得到id
				sql = "CREATE TABLE "+(tableName)+" (id int(10) unsigned NOT NULL AUTO_INCREMENT,"
				continue
			}
			sql = ([]string{sql,(curFieldName)},"")

			split := (tagValue, ";")
			sizeNumber := "10"
			fileType := "undefined Type"
			for _, value := range split {
				indexStart := (value,"(")
				indexEnd := (value,")")

				if (value,"size") {
					sizeNumber = value[indexStart+1 : indexEnd]
					("sizeNumber = ",sizeNumber)
				}
				if curFieldType == "int" {
					fileType ="int("+sizeNumber+")"
				}

				if curFieldType == "string"{
					fileType ="varchar("+sizeNumber+")"
				}
			}
			sql = ([]string{sql," ",fileType},"")
			if i == ().NumField()-1 {
				sql = ([]string{sql," ","DEFAULT NULL,PRIMARY KEY (`id`)"},"")
			}else{
				sql = ([]string{sql," ","DEFAULT NULL,"},"")
			}
		}
		sql = ([]string{sql,") ENGINE=InnoDB DEFAULT CHARSET=utf8;"},"")
		sqlStr = append(sqlStr, sql)
	}
	return sqlStr
}

(5)导入mysql驱动。

import (
	......
	_ "/go-sql-driver/mysql"
)