新版本用什么方式代替 session.Clone #1798

Closed
opened 2020-09-18 13:47:24 +00:00 by yoke_snowwolf · 10 comments

与 v0.8.x 相比,v1.x 缺少 session.Clone, engin.Clone 方法,

请问要如何兼容实现,

按原来xorm自身的实现,用 sessionNew := *session 来操作,

session.statement.RefTable 会受影响,

比如 sessionNew.Select(""),

之后 session.statement.RefTable 会为空,

由于 session.statement 外部无法访问,备份 RefTable 也做不到。

与 v0.8.x 相比,v1.x 缺少 session.Clone, engin.Clone 方法, 请问要如何兼容实现, 按原来xorm自身的实现,用 sessionNew := *session 来操作, session.statement.RefTable 会受影响, 比如 sessionNew.Select(""), 之后 session.statement.RefTable 会为空, 由于 session.statement 外部无法访问,备份 RefTable 也做不到。
Owner

You could do that via two methods:

  1. Always define a function to do that
func createMySession(opts MyOpts) *xorm.Session {
}
  1. Use gitea.com/xorm/builder
func createMyBuilder(opts MyOpts) builder.Cond {
}

xorm.Where(createMyBuilder(opts MyOpts)).Find()
You could do that via two methods: 1) Always define a function to do that ```go func createMySession(opts MyOpts) *xorm.Session { } ``` 2) Use `gitea.com/xorm/builder` ```go func createMyBuilder(opts MyOpts) builder.Cond { } xorm.Where(createMyBuilder(opts MyOpts)).Find() ```
lunny added the
kind
question
label 2020-09-19 15:26:15 +00:00
Author

感谢,
但是我想要的是 clone 的效果,并不是不知道怎么创建一个新的 session 或 cond来执行。

我原来用到 session.clone 的地方如下:

	// xorm FindAndCount has bug when cache on,find alone
	if reqCommon.PageDisable <= 0 {
		sessionCnt := session.Clone()		
		//sessionCnt := *session
		sessionCnt.Select("")
		switch reqVal.Interface().(type) {
		case map[string]interface{}, map[string]string:
			total64, err = sessionCnt.NoAutoCondition(true).CountExt()
		default:
			total64, err = sessionCnt.Count(req)
		}
		if err != nil {
			return 0, err
		}
		total = cast.ToInt(cast.ToString(total64))
		if total <= 0 {
			return 0, err
		}
	}

这是对 xorm 做二次封装的一处代码,
这里 session 来自外参,并不知道也不想关心已经有哪些条件表达式被设置,
但是需要用同样的条件做个 Count。

注:FindAndCount 暂时也并不满足我的需求,一是老版本在 cache开启时应该是有 bug 的(具体不太记得了,或许已修复),二是 session.Count 也不支持 group by 子句(CountExt 做了相关简单扩展支持),三是兼容考虑不想大动干戈

感谢, 但是我想要的是 clone 的效果,并不是不知道怎么创建一个新的 session 或 cond来执行。 我原来用到 session.clone 的地方如下: ``` // xorm FindAndCount has bug when cache on,find alone if reqCommon.PageDisable <= 0 { sessionCnt := session.Clone() //sessionCnt := *session sessionCnt.Select("") switch reqVal.Interface().(type) { case map[string]interface{}, map[string]string: total64, err = sessionCnt.NoAutoCondition(true).CountExt() default: total64, err = sessionCnt.Count(req) } if err != nil { return 0, err } total = cast.ToInt(cast.ToString(total64)) if total <= 0 { return 0, err } } ``` 这是对 xorm 做二次封装的一处代码, 这里 session 来自外参,并不知道也不想关心已经有哪些条件表达式被设置, 但是需要用同样的条件做个 Count。 注:FindAndCount 暂时也并不满足我的需求,一是老版本在 cache开启时应该是有 bug 的(具体不太记得了,或许已修复),二是 session.Count 也不支持 group by 子句(CountExt 做了相关简单扩展支持),三是兼容考虑不想大动干戈
Author

再次稍微看了下 v1.x的实现,觉得问题可能还不是这么简单。

v1.x 造成我前面所说 clone 无法正常使用的原因其实是 statement 被自动 reset 了,

v1.x 现在会根据 session.autoResetStatement 来决定是否 执行 session.resetStatement(),

但是 session.autoResetStatement 并不受外部控制,默认在内部实现执行结束时都会为 true,就是说现在执行后都是会被 reset,

这个逻辑对于现在想同步升级的我来说,觉得是不太合适的,除了兼容性的问题外,往往很多地方都会复用同一个 session 执行一系列操作,特别是 table 引用,我真不想去执行一个select 后又再次 setup 一次 tableName,
现在外部又无法控制 autoResetStatement, 并且开放该项貌似也没用,内部实现某些时候会直接改变这个值(内部实现保证自身多个序列正常执行,先设为 false,执行完直接置为 true)。

这个逻辑是必须的吗。。。?
如果前面我看得没错的话,我觉得这个对于老版本兼容是毁灭性的,相信稍微复杂点的应用都会有不需要 reset 的需求。就算不升级直接用v1也会带来更麻烦的使用方式,真的无法接受。

我很奇怪为什么没人提出这个问题,是我理解错误还是没人从 v0.8.x升级上来过。

再次稍微看了下 v1.x的实现,觉得问题可能还不是这么简单。 v1.x 造成我前面所说 clone 无法正常使用的原因其实是 statement 被自动 reset 了, v1.x 现在会根据 session.autoResetStatement 来决定是否 执行 session.resetStatement(), 但是 session.autoResetStatement 并不受外部控制,默认在内部实现执行结束时都会为 true,就是说现在执行后都是会被 reset, 这个逻辑对于现在想同步升级的我来说,觉得是不太合适的,除了兼容性的问题外,往往很多地方都会复用同一个 session 执行一系列操作,特别是 table 引用,我真不想去执行一个select 后又再次 setup 一次 tableName, 现在外部又无法控制 autoResetStatement, 并且开放该项貌似也没用,内部实现某些时候会直接改变这个值(内部实现保证自身多个序列正常执行,先设为 false,执行完直接置为 true)。 这个逻辑是必须的吗。。。? 如果前面我看得没错的话,我觉得这个对于老版本兼容是毁灭性的,相信稍微复杂点的应用都会有不需要 reset 的需求。就算不升级直接用v1也会带来更麻烦的使用方式,真的无法接受。 我很奇怪为什么没人提出这个问题,是我理解错误还是没人从 v0.8.x升级上来过。
Owner

@yoke_snowwolf I think you are wrong. The logic about session.autoResetStatement and session.resetStatement() haven't been changed.

From v0.8 -> v1.0, most changes are internal refactors.

Clone is not a right place to reuse the conditions. That's why I removed it.

@yoke_snowwolf I think you are wrong. The logic about `session.autoResetStatement` and `session.resetStatement()` haven't been changed. From v0.8 -> v1.0, most changes are internal refactors. `Clone` is not a right place to reuse the conditions. That's why I removed it.
Author

好的,谢谢

resetStatement 机制是看错了,v0.8-v1一直都有,只会原来是叫 Init(),没细看 reset时也调用了。有这机制好像问题也不是太大。。。

前面说的按老方式Clone 导致原session丢失信息 是因为 session.statement v1 改成指针引用了,
我这暂时加了个 session statement 的浅拷贝扩展来兼容,

func (session *Session) CopyStatementFrom(src *Session) *Session {

	if session.statement == nil {
		return session
	}

	var statementNew = *src.statement
	if src.statement.LimitN != nil {
		var LimitNNew = *src.statement.LimitN
		statementNew.LimitN = &LimitNNew
	}
	session.statement = &statementNew
	return session
}

要维护完整的 session clone 或 engine clone 确实比较麻烦且容易出问题,但是 statement 变化应该不会太大,能否考虑直接提供一个类似的 statement 拷贝实现,还是有一定需求的。

好的,谢谢 resetStatement 机制是看错了,v0.8-v1一直都有,只会原来是叫 Init(),没细看 reset时也调用了。有这机制好像问题也不是太大。。。 前面说的按老方式Clone 导致原session丢失信息 是因为 session.statement v1 改成指针引用了, 我这暂时加了个 session statement 的浅拷贝扩展来兼容, ``` func (session *Session) CopyStatementFrom(src *Session) *Session { if session.statement == nil { return session } var statementNew = *src.statement if src.statement.LimitN != nil { var LimitNNew = *src.statement.LimitN statementNew.LimitN = &LimitNNew } session.statement = &statementNew return session } ``` 要维护完整的 session clone 或 engine clone 确实比较麻烦且容易出问题,但是 statement 变化应该不会太大,能否考虑直接提供一个类似的 statement 拷贝实现,还是有一定需求的。

chunk 的业务逻辑需求是需要clone的。。用iterate会有性能问题,这是全表搜索了。

如我想封装一个chunk函数。

chunk( table.join().where(...), func(bean){...} )

func chunk(sess, fn) {

    for {
      // sess.clone().sql => select * from xxx join xxx on xxxx=xxx  where (xxx1=11 and xxx2>22)  
      
      newsql = oldSql  + " and xxx_id > {{last_id}} limit 1"
      
      // rslist from .find()
      for(_, rs := range rslist){
         fn(rs) 
      }
      last_id = last(rslist)["xxx_id"]
    }
}


chunk 的业务逻辑需求是需要clone的。。用iterate会有性能问题,这是全表搜索了。 如我想封装一个chunk函数。 ```go chunk( table.join().where(...), func(bean){...} ) func chunk(sess, fn) { for { // sess.clone().sql => select * from xxx join xxx on xxxx=xxx where (xxx1=11 and xxx2>22) newsql = oldSql + " and xxx_id > {{last_id}} limit 1" // rslist from .find() for(_, rs := range rslist){ fn(rs) } last_id = last(rslist)["xxx_id"] } } ```
Owner

@RelicOfTesla You can also use builder and session function to resovle your problems.

@RelicOfTesla You can also use `builder` and `session function` to resovle your problems.

@RelicOfTesla You can also use builder and session function to resovle your problems.

这是一个通用函数。。不是特定表的函数。。如

chunk( tableUser.join().where(...), func(bean){...} )
chunk( tableAdmin.join().where(...), func(bean){...} )
chunk( tableLevel.where(...), func(bean){...} )
chunk( tableHelp.select(...).where(...), func(bean){...} )
chunk( tableList.table(...), func(bean){...} ) // not where

使用builder我理解,就是直接抛弃xorm,直接用builder生成语句。(结构体查询语句法不能用,field、table name、中间件、类型转换等特性都不能用,难受。。还得自己写builder.Clone的深层拷贝函数(selects/joins/...是数组变量)。何苦呢。。。)
使用session function是怎么个解决方案,不理解。

> @RelicOfTesla You can also use `builder` and `session function` to resovle your problems. 这是一个通用函数。。不是特定表的函数。。如 chunk( tableUser.join().where(...), func(bean){...} ) chunk( tableAdmin.join().where(...), func(bean){...} ) chunk( tableLevel.where(...), func(bean){...} ) chunk( tableHelp.select(...).where(...), func(bean){...} ) chunk( tableList.table(...), func(bean){...} ) // not where 使用builder我理解,就是直接抛弃xorm,直接用builder生成语句。(结构体查询语句法不能用,field、table name、中间件、类型转换等特性都不能用,难受。。**还得自己写builder.Clone的深层拷贝函数(selects/joins/...是数组变量)**。何苦呢。。。) 使用session function是怎么个解决方案,不理解。

好的,谢谢

resetStatement 机制是看错了,v0.8-v1一直都有,只会原来是叫 Init(),没细看 reset时也调用了。有这机制好像问题也不是太大。。。

前面说的按老方式Clone 导致原session丢失信息 是因为 session.statement v1 改成指针引用了,
我这暂时加了个 session statement 的浅拷贝扩展来兼容,

func (session *Session) CopyStatementFrom(src *Session) *Session {

	if session.statement == nil {
		return session
	}

	var statementNew = *src.statement
	if src.statement.LimitN != nil {
		var LimitNNew = *src.statement.LimitN
		statementNew.LimitN = &LimitNNew
	}
	session.statement = &statementNew
	return session
}

要维护完整的 session clone 或 engine clone 确实比较麻烦且容易出问题,但是 statement 变化应该不会太大,能否考虑直接提供一个类似的 statement 拷贝实现,还是有一定需求的。

你好,我们也准备升级遇到这个问题,请问最后怎么处理的?

> 好的,谢谢 > > resetStatement 机制是看错了,v0.8-v1一直都有,只会原来是叫 Init(),没细看 reset时也调用了。有这机制好像问题也不是太大。。。 > > 前面说的按老方式Clone 导致原session丢失信息 是因为 session.statement v1 改成指针引用了, > 我这暂时加了个 session statement 的浅拷贝扩展来兼容, > ``` > func (session *Session) CopyStatementFrom(src *Session) *Session { > > if session.statement == nil { > return session > } > > var statementNew = *src.statement > if src.statement.LimitN != nil { > var LimitNNew = *src.statement.LimitN > statementNew.LimitN = &LimitNNew > } > session.statement = &statementNew > return session > } > ``` > > 要维护完整的 session clone 或 engine clone 确实比较麻烦且容易出问题,但是 statement 变化应该不会太大,能否考虑直接提供一个类似的 statement 拷贝实现,还是有一定需求的。 > > 你好,我们也准备升级遇到这个问题,请问最后怎么处理的?
Owner

Make a function to generate session,

func MyFirstSession(opts MyOptions) *xorm.Session {
     return engine.Where(...).And(...)
}
Make a function to generate session, ``` func MyFirstSession(opts MyOptions) *xorm.Session { return engine.Where(...).And(...) } ```
lunny added
wontfix
and removed
kind
question
labels 2021-08-04 09:07:32 +00:00
lunny closed this issue 2021-08-04 09:07:34 +00:00
Sign in to join this conversation.
No Milestone
No Assignees
4 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: xorm/xorm#1798
No description provided.