# Vodka **Repository Path**: llyb120/vodka ## Basic Information - **Project Name**: Vodka - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2024-08-12 - **Last Updated**: 2024-09-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Vodka 本分支和1.16共同维护,但最新代码位于go1.16分支。 Vodka是Go的一个轻量级的半自动化ORM框架,灵感来自MyBatis。 希望可以和go中的gin框架,与柯南中的Gin同Vodka一样,成为形影不离的好伙伴。 Banner ## 特性 - 简单,开箱即用 - 天生防注入 - SQL和代码分离,方便管理 - 支持用于复杂查询的动态SQL - 对基础查询语句直接自动装配,无需再书写xml文件 - 支持插件 - 支持缓存 (开发中) - 支持定义切面,插入自己的权限语句(开发中) ## 快速上手 ### 定义你的model ```go type User struct { Id int `vo:"id"` //vo标签表示对应数据库中的字段 Name string `vo:"name"` Age int `vo:"age"` } ``` ### 定义mapper接口 (无需实现,vodka会自动装配这些方法) ```go type UserMapper struct { // 基础查询 Select func(params interface{}) ([]*User, error) `param:"params"` //params为在xml中映射的名字 // 部分无需xml的情况,可以直接通过tag自定义sql SelectByCustomSql func(params interface{}) ([]*User, error) `param:"params" sql:"select * from user where id = #{id}"` // 插入 // insert语句最多支持3个返回值,分别为影响的行数、自增主键、错误 Insert func(user *User) (int64, int64, error) `param:"user"` InsertBatch func(users []*User) (int64, error) `param:"users"` // 更新 // 更新语句最多支持2个返回值,分别为影响的行数、错误 Update func(user *User) (int64, error) `param:"user"` // 删除 // 删除语句最多支持2个返回值,分别为影响的行数、错误 Delete func(id int) (int64, error) `param:"id"` } ``` ### 定义xml映射文件 - 可直接使用mybatis的工具生成,无需繁琐的书写步骤 - 针对复杂查询,在xml中和原生sql书写并无二致,只需要附加你的条件即可 - 支持include标签,可以引用通用的sql语句 ```xml INSERT INTO users (name, age) VALUES (#{name}, #{age}) INSERT INTO users (name, age) VALUES (#{user.name}, #{user.age}) UPDATE users SET name = #{name}, age = #{age} WHERE id = #{id} DELETE FROM users WHERE id = #{id} ``` ### 初始化 - 在系统初始化时,调用`vodka.ScanMapper("你的xml路径文件夹")`方法进行初始化 - 获取上面定义的mapper ```go var userMapper UserMapper vodka.InitMapper(&userMapper) // 使用 // 使用map作为参数进行复杂查询 userMapper.Select(map[string]interface{}{"id": 1}) // 使用struct作为参数 userMapper.Select(User{Id: 1}) // 插入 userMapper.Insert(&User{Name: "张三", Age: 18}) // 更新 userMapper.Update(&User{Id: 1, Name: "李四", Age: 20}) // 删除 userMapper.Delete(1) ``` ### 标签说明 - mapper:定义命名空间,每个xml根节点都要有,相同的命名空间会合并成一个 - select: 定义查询语句 - insert:定义插入语句 - update:定义更新语句 - delete:定义删除语句 - foreach 循环语句,分为collection/item/separator/open/close 五个属性 - where:定义查询条件,使用该标签,可直接使用and进行条件拼装,无需判断在第一个条件上不使用and - if:定义表达式判断,符合test的表达式才会生效 - set: 定义更新语句中的set部分,使用该标签,可直接在每条语句后拼装逗号,无需检查最后一个是否拼装 - sql: 定义sql语句,抽象出公共的模块,可以供include引用 - include: 引用sql语句,可以简单理解为文本替换 ### 通用Mapper - 直接继承mapper.VodkaMapper,即可拥有通用Mapper的所有功能 - 以下基础语句会自动装配,无需再书写xml文件 ```go type VodkaMapper[T any, ID any] struct { InsertOne func(params *T) (int64, int64, error) `params:"params"` InsertBatch func(params []*T) (int64, int64, error) `params:"params"` UpdateById func(params *T) (int64, error) `params:"params"` UpdateSelectiveById func(params *T) (int64, error) `params:"params"` UpdateByCondition func(condition *T, action *T) (int64, error) `params:"condition,action"` UpdateByConditionMap func(condition map[string]interface{}, action map[string]interface{}) (int64, error) `params:"condition,action"` DeleteById func(id ID) (int64, error) `params:"id"` SelectById func(id ID) (*T, error) `params:"id"` SelectAll func(params *T, order string, offset int64, limit int64) ([]*T, error) `params:"...params,order,offset,limit"` // 多个参数下,框架无法判断是否需要展开,所以使用...来表示 CountAll func(params *T) (int64, error) `params:"params"` SelectAllByMap func(params map[string]interface{}, order string, offset int64, limit int64) ([]*T, error) `params:"...params,order,offset,limit"` // 多个参数下,框架无法判断是否需要展开,所以使用...来表示 CountAllByMap func(params map[string]interface{}) (int64, error) `params:"params"` } // 示例 type UserMapper struct { mapper.VodkaMapper[User, int64] // 需要额外书写表名和主键定义 _ any `table:"user" pk:"id"` } //test var userMapper UserMapper vodka.InitMapper(&userMapper) userMapper.InsertOne(&User{Name:"张三"}) // ByMap系列方法可以使用多种策略参数,例如GT_EQ、LT_EQ、GT、LT、EQ、NE、LIKE、IN、NOT_IN、BETWEEN、NOT_BETWEEN等 userMapper.SelectAllByMap(map[string]interface{}{"GTE_age": 18, "name": "张三"}, "", 0, 10) ``` ## 插件 ### 分页插件 - Vodka内置分页插件,简单易用 - 只需要在查询语句外使用DoPage方法即可,代码和语句无需任何修改 ```go var pg page.Page[User] pg.PageNum = 1 pg.PageSize = 10 pg.Sort = "id desc" err := page.DoPage(&pg, func() { // 这里已无需返回值 userMapper.GetUsers() }) fmt.Println(pg.List) fmt.Println(pg.TotalRows) ``` ### 自定义Tag - 当现有的标签无法满足你的时候,你可以自定义tag来增加新功能 ```go plugin.RegisterTag("permission", func(builder *strings.Builder, node *xml.Node, params map[string]interface{}, resultParams *[]interface{}, root *xml.Node) { // 从属性获取key和value key := node.Attrs["key"] value := node.Attrs["value"] builder.WriteString(fmt.Sprintf(" and %s > %s", key, value)) }) ``` ```xml ``` 最终,该语句会被渲染为 ```select id,name,age from user where id > 0``` ### 自定义函数 - 在#{}中,可以使用自定义函数,函数需要先注册,然后在#{}中使用 ```go plugin.RegisterFunction("sum", func(args []interface{}) interface{} { return args[0].(int64) + args[1].(int64) + args[2].(int64) }) ``` ```xml ``` ### 其余说明 - GO中在insert语句中,无法直接使用nil,所以如果你需要在insert语句中使用自增主键,可以这么写,假如主键为int64,以下写法同时可以满足自增主键和非自增主键,当然,如果你只使用自增主键,最好的方法是不对主键写插入 ```xml insert into user (id, name, age) values ( #{id == 0 ? $AUTO : id}, #{name}, #{age} ) ```