Giter Club home page Giter Club logo

gomybatis's People

Contributors

alingse avatar lzm420241 avatar zhuxiujia avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gomybatis's Issues

传入入一个有int字段的struct到查询方法,int=22变成了26.

func main() {
arg := 22
ConvertTest(arg)
}

func ConvertTest(arg interface{}) {
fmt.Println(strconv.FormatInt(int64(arg.(int)), 8)) //截取了下述方法:
}

==============================
func caseType(arg interface{}, types reflect.Type) string {
switch types.Kind() {
case reflect.String:
return arg.(string)
case reflect.Int:
return strconv.FormatInt(int64(arg.(int)), 8)
case reflect.Int8:
return strconv.FormatInt(int64(arg.(int8)), 8)
case reflect.Int16:
return strconv.FormatInt(int64(arg.(int16)), 16)
case reflect.Int32:
return strconv.FormatInt(int64(arg.(int32)), 32)
case reflect.Int64:
return strconv.FormatInt(int64(arg.(int64)), 64)
case reflect.Float32:
return strconv.FormatFloat(float64(arg.(float32)), 'f', -1, 32)
case reflect.Float64:
return strconv.FormatFloat(float64(arg.(float64)), 'f', -1, 64)
case reflect.Bool:
return strconv.FormatBool(arg.(bool))
case reflect.Uint:
return strconv.FormatUint(uint64(arg.(uint)), 2)
case reflect.Uint8:
return strconv.FormatUint(uint64(arg.(uint8)), 8)
case reflect.Uint16:
return strconv.FormatUint(uint64(arg.(uint16)), 16)
case reflect.Uint32:
return strconv.FormatUint(uint64(arg.(uint32)), 32)
case reflect.Uint64:
return strconv.FormatUint(uint64(arg.(uint64)), 64)
case reflect.Struct:
if types.String() == "time.Time" {
return arg.(time.Time).Format(2006-01-02 15:04:05)
} else {
return fmt.Sprint(arg)
}
default:
return fmt.Sprint(arg)
}
return fmt.Sprint(arg)
}

映射字段对应顺序不对应

func(name string, pageIndex, pageSize int64) ([]models.DownloadInfo, error) mapperParams:"name,pageIndex,pageSize"

发现在有时pageIndex与pageSize 数据在数据库查询时交换了,不知道是什么原因。重启项目就正常了。

PostgreSQL预处理问题

PostgreSQL预处理的占位符应该是$1$2这种,不是?这里有说明,所以,GoMybatis是否需要为此做针对性处理呢?

存在SQL注入风险,建议改善

测试方法
执行Example_test.go中TestSelectTemplete 测试;
运行测试[不注入]
var result, err = exampleActivityMapper.SelectTemplete("hello")
所得结果
=== RUN TestSelectTemplete 2019/09/07 18:13:52 [GoMybatis] [bbb4b0b9-1366-4475-9c96-185ed12366b8] Query ==> select * from biz_activity where delete_flag = 1 and name = 'hello' 2019/09/07 18:13:52 [GoMybatis] [bbb4b0b9-1366-4475-9c96-185ed12366b8] Close session 2019/09/07 18:13:52 [GoMybatis] [bbb4b0b9-1366-4475-9c96-185ed12366b8] ReturnRows <== 1 result= [{167 hello ceshi 3 0001-01-01 00:00:00 +0000 UTC 0}] --- PASS: TestSelectTemplete (0.00s)

运行测试[注入]
`
var result, err = exampleActivityMapper.SelectTemplete("hello' OR 'A'!='")

**所得结果**:=== RUN TestSelectTemplete
2019/09/07 17:58:23 [GoMybatis] [72d49711-1f09-4cfe-a298-acbef3872781] Query ==> select * from biz_activity where delete_flag = 1 and name = 'hello' OR 'A'!=''
2019/09/07 17:58:23 [GoMybatis] [72d49711-1f09-4cfe-a298-acbef3872781] Close session
2019/09/07 17:58:23 [GoMybatis] [72d49711-1f09-4cfe-a298-acbef3872781] ReturnRows <== 7
result= [{165 安利一波大表哥 ceshi 1 0001-01-01 00:00:00 +0000 UTC 0} {166 注册送好礼 测试 2 0001-01-01 00:00:00 +0000 UTC 0} {167 hello ceshi 3 0001-01-01 00:00:00 +0000 UTC 0} {168 rs168 4 0001-01-01 00:00:00 +0000 UTC 0} {169 rs168 5 0001-01-01 00:00:00 +0000 UTC 0} {170 rs168-8 6 0001-01-01 00:00:00 +0000 UTC 0} {171 rs168 0 0001-01-01 00:00:00 +0000 UTC 0}]
--- PASS: TestSelectTemplete (0.01s)
`

注:这两天终于有空来学习您的源码,收获不少,特别感谢;感觉这个SQL注入风险主要是引起原因是以字符中形式直接拼SQL所至,可在session, sql, err = buildSql(proxyArg, nodes, sessionEngine.SqlBuilder())过程中对string类型的Args作一下特别处理,当然也可以参照java mybatis
那样采用先PreparedStatement,然后再传参执行。

installation doesn't suppport go mod.

Hi Zhuxiujia,
I add reference of gomybatis like the instruction,
_ "github.com/go-sql-driver/mysql" //Mysql驱动程序,必须使用 _ 下划线导入
github.com/zhuxiujia/GoMybatis"
and run go mod tidy, it tells me the error as below:
---> go mod tidy
go: finding github.com/zhuxiujia/GoMybatis/lib/github.com/google/uuid latest
go: github.com/zhuxiujia/GoMybatis/lib/github.com/google/[email protected]
7e339b0: parsing go.mod: unexpected module path "github.com/google/uuid"
go: error loading module requirements
obviously, it can not find the dependency packages google/uuid, because you did not organize them by go mod mode.
If i install the package by command,
--->go get github.com/zhuxiujia/GoMybatis
it returns same error and fail in finding dependency.
could you please fix it?

线程安全问题

这里建议改用线程安全的sync.Map
涉及文件:
SessionFactory.go
SessionFactorySession.go
image

请问NewSession是否能够提到类似Java的父类中?用隐式字段的方式。

类似如此

type BaseDao struct {
	NewSession func(config *GoMybatis.TransationRMClientConfig) (GoMybatis.Session, error)
}
type UserDao struct {
	BaseDao
	Insert func(user User) (int64, error)
	SelectById func(id int) (User, error) `mapperParams:"id"`
}

var session, err = userDao.NewSession(nil)

这样避免每一个Mapper都去定义一次NewSession,节省代码。
实际测试中发现 NewSession 并没有被注入代理方法。

阅读源码发现

if f.CanSet() {
	switch ft.Kind() {
	case reflect.Struct:
	case reflect.Func:
		if buildFunc != nil {
			buildRemoteMethod(f, ft, sf, buildFunc(sf))
		}
	}
}

GoMybatisProxy.go: 48行,case reflect.Struct里没有任何实现。

是暂未支持该功能,还是说有什么其他问题导致不可以这样做呢?
又或者有更好的实现方式?

多次打扰,真不好意思。

希望事务能做成类似Spring的那种统一事务管理。

目前在逻辑较为复杂项目中使用的话,事务管理这块完全靠人工手写错误率太高了。

举例:
假设有函数A和函数B, 函数A中调用了函数B,实际运行中,可能调用函数A,也可能单独调用函数B,两个函数内皆包含多表修改,所以都需要使用事务。

就目前从源码和样例中了解到需要使用事务每个Mapper都需要添加NewSession支持。
当函数A被调用时,函数A内开启了一个NewSession,执行到调用函数B是,因为是被A函数调用的,所以B函数不能自己开启事务,需要接收从函数A传过来的Session,但是B函数因为也可以被独立调用,所以其中必定包含了Session的Rollback和Commit操作,此时因为是从A函数传递过来的Session。所以,这里必须判断如果不是B函数自己创建的Session则不能进行Rollback和Commit操作,如果进行了Commit操作,当B函数执行结束返回函数A时,函数A再继续进行数据库编辑操作再Commit虽然不报错,但操作的数据不会得到保存。所以函数B在被函数A调用时,执行顺利与否都只能通过返回值告诉函数A,由函数A最终决定是Rollback或者Commit,但当函数B是被独立调用时,则又需要自己决定Rollback或者Commit。

这还仅仅只是一层嵌套,在复杂的业务场景中,3到5层的各种组合嵌套是非常常见的。这样人工写下来,Bug率实在不敢想象。

而在Java的Spring中,这一切都交给了统一的中心事务管理,通过使用ThreadLocal来判断当前线程是否已经开启事务,然后根据当前需要执行的方法配置的事务类型,决定采用哪种方式。

 1、PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。

2、PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。‘

3、PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。

4、PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。

5、PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

6、PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。

7、PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

以上是Spring支持的事务类型,有很多是不常用,其实如果能实现第一种事务类型,就已经方便太多了,这是最常用的,刚刚讲的函数A函数B就是属于这个类型的事务。

<where> bug

SELECT

FROM sys_role_info_t

and role_name like #{name}

name==""时,生成的sql 为
select
FROM sys_role_info_t where

关于order by的问题

xml文件中:

  1. order by id desc
  2. order by #{orders}
    以上两种用法生成的日志都包含『order by』部分,但是第一种排序生效,第二种没有生效。
    求助:是什么问题?应该怎么解决

请问Mapper支持selectKey标签吗?

<selectKey resultType="int64" order="AFTER">
            SELECT LAST_INSERT_ID() AS id
</selectKey>

请问GoMybatis支持用于数据库Insert时返回当前插入数据的自增主键的selectKey标签吗?
检查dtd文件看到里面是包含该标签的,但是实际使用中返回值永远是1, 传入的User对象里Id的值则始终为0,并没有反向注入。
如果支持的话,请问我是哪里弄错了吗?

我的测试代码如下

<insert id="insert">
        <selectKey resultType="int64" order="AFTER">
            SELECT LAST_INSERT_ID() AS id
        </selectKey>
        INSERT INTO
        sys_user
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="name != nil">name,</if>
            <if test="sex != nil">sex,</if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="name != nil">#{name},</if>
            <if test="sex != nil">#{sex},</if>
        </trim>
    </insert>
import "time"

type User struct {
	Id int64 `json:"id"`
	Name string `json:"name"`
	Sex int `json:"sex"`
	CreateDate time.Time `json:"createTime"`
	UpdateTime time.Time `json:"updateTime"`
}
type UserDao struct {
	Insert func(user User) (int64, error)
	//SelectById func(id int64) (User, error) `mapperParams:"id"`
}

如果不支持的话,请问这个需求有替代的实现方案吗?

这个框架测试过高并发情况吗?

拜读过框架源码,发现源码很少加锁和原子操作,问一下这个是怎么避免高并发下的资源竞争和出错的?感觉代码有一些写java的思维在里面

请问目前不支持Mysql的timestamp类型吗

问题又来了。

<resultMap id="userResultMap" tables="sys_user">
        <id property="id" column="id" langType="int"/>
        <result property="name" column="name" langType="string"/>
        <result property="sex" column="sex" langType="int"/>
        <result property="createDate" column="create_date" langType="time.Time"/>
        <result property="updateTime" column="update_time" langType="time.Time"/>
</resultMap>

我的createDate和updateTime在数据库中都是timestamp,执行查询后返回都是默认值。

查看样例中关于日期都是使用的datetime。

Two tips~

1、资料首页的地点,不是hangzou是hangzhou,当然如果是有意而为之,还请忽略
2、源码上.idea文件夹依旧存在,应该是添加忽略前传上去的,建议删除

select sql resultMap 不起作用

这个dic_name是有值的,model里面有字段为HonorLevelName,数据不能映射上去,
但把HonorLevelName 属性修改为DicName就正常了

猜测:查询得到数据没有根据resultMap 来处理,我用的v6.2.3+incompatible

我的微信:1521510339,有空加我

建议开发时,日志里面不输出DecoderTemplete

1、当xml多了,DecoderTemplete 显示太多了,建议去掉

2、warning ======================== mapper.InsertTemplete() have not define tag mapperParams:"",maybe can not get param value! 这种日志也是交意义的建议去掉

3、对模板sql 建议是直接生成sql 文件在xml ,而不是只使用标签,只使用标签不灵活,尤其是做update

自己有空写个小项目,使用这个项目到开发,你会发现有些地方使用起来有点不方便。

非空的判断逻辑

我看sql对于一个字符串是否非空的判断就是判断这个参入的指针是否为空,但如果一个string的值为""的话,还是会执行。

这样虽然也没有问题,但是用起来很不方便,建议对于字符串的非空判断改成字符串不为nil,且字符串不为""

代码生成

后续会有根据表名生成代码的方式吗
类似mybatis generate
还有分布插件,java开发常用插件
持续关注大神中

如何方便地在项目初始化时实现所有mapper?

看作者提供的例子都是通过扫描单一文件然后写入结构体的,这样的话意味着我项目初始化的时候需要手动进行结构体的初始化并扫描mapper目录,然后通过判断文件名的形式写入,一旦mapper变多就会让代码看着十分的臃肿,每次添加新的mapper也需要去修改代码。

这是我目前的实现方式,不知道项目是否已经有统一的写入方式,或者更好的解决方案能在每次添加新的mapper时不对这块代码进行维护?

func initMapperImpl() {
	mapperImpl = make(map[string]interface{})
	mapperImpl["doorMapperImpl"] = new(DoorMapperImpl)
	mapperImpl["roomMapperImpl"] = new(RoomMapperImpl)
	mapperImpl["customerMapperImpl"] = new(CustomerMapperImpl)
	mapperImpl["communityMapperImpl"] = new(CommunityMapperImpl)

	var mapperPaths []string

	mapperPaths, _ = util.GetAllFile("/mnt/code/reinhardtv1/mapper", mapperPaths)
	for _, str := range mapperPaths {
		c, _ := ioutil.ReadFile(str)
		name := str[strings.LastIndex(str, "/")+1:len(str)-4] + "Impl"
		fmt.Println(name)
		switch name {
		case "doorMapperImpl":
			mysql.GoMybatisEngine.WriteMapperPtr(mapperImpl[name].(*DoorMapperImpl), c)
		case "roomMapperImpl":
			mysql.GoMybatisEngine.WriteMapperPtr(mapperImpl[name].(*RoomMapperImpl), c)
		case "communityMapperImpl":
			mysql.GoMybatisEngine.WriteMapperPtr(mapperImpl[name].(*CommunityMapperImpl), c)
		case "customerMapperImpl":
			mysql.GoMybatisEngine.WriteMapperPtr(mapperImpl[name].(*CustomerMapperImpl), c)
		}
	}
}

照着示例写结果运行不了

panic: runtime error: invalid memory address or nil pointer dereference
`func init() {
if MysqlUri == "*" {
println("GoMybatisEngine not init! because MysqlUri is * or MysqlUri is ''")
return
}
engine = GoMybatis.GoMybatisEngine{}.New()

//mysql链接格式为         用户名:密码@(数据库链接地址:端口)/数据库名称   例如root:123456@(***.mysql.rds.aliyuncs.com:3306)/test
_, err := engine.Open("mysql", MysqlUri) //此处请按格式填写你的mysql链接,这里用*号代替
if err != nil {
	panic(err.Error())
}
//读取mapper xml文件
bytes, _ := ioutil.ReadFile("Example_ActivityMapper.xml")
//设置对应的mapper xml文件
engine.WriteMapperPtr(&exampleActivityMapper, bytes) 这一步报错

}
`

直接使用mapper查询无法映射,需要手动增加resultMap

一、type Activity struct {
Id string json:"id"
Name string json:"name"
}
select * from users
exampleActivityMapperImpl.SelectAll()
修改为:
select * from users

<resultMap id="BaseResultMap" tables="users">
    <result column="id" property="Id" />
    <result column="name" property="Name"/>
</resultMap>

或者 select id as Id,name as Name from users 才行

二、模板标签默认指向于resultMap标签(resultMap="BaseResultMap"),因此先定义resultMap标签
这个有问题,也必须手动加上 resultMap="BaseResultMap" 才行

项目中的example中SelectByIds 方法好像有问题

select id,name,age from tb_user where id in #{item} 发送的sql语句,select id,name,age from tb_user where id in ( 1 2),我传递的是 ids := []int{1,2},而ids的打印结果是[1 2],好像separator没有排上用场

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.