session_get.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. // Copyright 2016 The Xorm Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package xorm
  5. import (
  6. "errors"
  7. "reflect"
  8. "strconv"
  9. "github.com/xormplus/core"
  10. )
  11. // Get retrieve one record from database, bean's non-empty fields
  12. // will be as conditions
  13. func (session *Session) Get(bean interface{}) (bool, error) {
  14. defer session.resetStatement()
  15. if session.isAutoClose {
  16. defer session.Close()
  17. }
  18. beanValue := reflect.ValueOf(bean)
  19. if beanValue.Kind() != reflect.Ptr {
  20. return false, errors.New("needs a pointer to a value")
  21. } else if beanValue.Elem().Kind() == reflect.Ptr {
  22. return false, errors.New("a pointer to a pointer is not allowed")
  23. }
  24. if beanValue.Elem().Kind() == reflect.Struct {
  25. if err := session.statement.setRefValue(beanValue.Elem()); err != nil {
  26. return false, err
  27. }
  28. }
  29. var sqlStr string
  30. var args []interface{}
  31. var err error
  32. if session.statement.RawSQL == "" {
  33. if len(session.statement.TableName()) <= 0 {
  34. return false, ErrTableNotFound
  35. }
  36. session.statement.Limit(1)
  37. sqlStr, args, err = session.statement.genGetSQL(bean)
  38. if err != nil {
  39. return false, err
  40. }
  41. } else {
  42. sqlStr = session.statement.RawSQL
  43. args = session.statement.RawParams
  44. }
  45. if session.canCache() && beanValue.Elem().Kind() == reflect.Struct {
  46. if cacher := session.engine.getCacher2(session.statement.RefTable); cacher != nil &&
  47. !session.statement.unscoped {
  48. has, err := session.cacheGet(bean, sqlStr, args...)
  49. if err != ErrCacheFailed {
  50. return has, err
  51. }
  52. }
  53. }
  54. return session.nocacheGet(beanValue.Elem().Kind(), bean, sqlStr, args...)
  55. }
  56. func (session *Session) nocacheGet(beanKind reflect.Kind, bean interface{}, sqlStr string, args ...interface{}) (bool, error) {
  57. session.queryPreprocess(&sqlStr, args...)
  58. var rawRows *core.Rows
  59. var err error
  60. if session.isAutoCommit {
  61. _, rawRows, err = session.innerQuery(sqlStr, args...)
  62. } else {
  63. rawRows, err = session.tx.Query(sqlStr, args...)
  64. }
  65. if err != nil {
  66. return false, err
  67. }
  68. defer rawRows.Close()
  69. if !rawRows.Next() {
  70. return false, nil
  71. }
  72. switch beanKind {
  73. case reflect.Struct:
  74. fields, err := rawRows.Columns()
  75. if err != nil {
  76. // WARN: Alougth rawRows return true, but get fields failed
  77. return true, err
  78. }
  79. dataStruct := rValue(bean)
  80. if err := session.statement.setRefValue(dataStruct); err != nil {
  81. return false, err
  82. }
  83. scanResults, err := session.row2Slice(rawRows, fields, len(fields), bean)
  84. if err != nil {
  85. return false, err
  86. }
  87. rawRows.Close()
  88. _, err = session.slice2Bean(scanResults, fields, len(fields), bean, &dataStruct, session.statement.RefTable)
  89. case reflect.Slice:
  90. err = rawRows.ScanSlice(bean)
  91. case reflect.Map:
  92. err = rawRows.ScanMap(bean)
  93. default:
  94. err = rawRows.Scan(bean)
  95. }
  96. return true, err
  97. }
  98. func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interface{}) (has bool, err error) {
  99. // if has no reftable, then don't use cache currently
  100. if !session.canCache() {
  101. return false, ErrCacheFailed
  102. }
  103. for _, filter := range session.engine.dialect.Filters() {
  104. sqlStr = filter.Do(sqlStr, session.engine.dialect, session.statement.RefTable)
  105. }
  106. newsql := session.statement.convertIDSQL(sqlStr)
  107. if newsql == "" {
  108. return false, ErrCacheFailed
  109. }
  110. cacher := session.engine.getCacher2(session.statement.RefTable)
  111. tableName := session.statement.TableName()
  112. session.engine.logger.Debug("[cacheGet] find sql:", newsql, args)
  113. ids, err := core.GetCacheSql(cacher, tableName, newsql, args)
  114. table := session.statement.RefTable
  115. if err != nil {
  116. var res = make([]string, len(table.PrimaryKeys))
  117. rows, err := session.DB().Query(newsql, args...)
  118. if err != nil {
  119. return false, err
  120. }
  121. defer rows.Close()
  122. if rows.Next() {
  123. err = rows.ScanSlice(&res)
  124. if err != nil {
  125. return false, err
  126. }
  127. } else {
  128. return false, ErrCacheFailed
  129. }
  130. var pk core.PK = make([]interface{}, len(table.PrimaryKeys))
  131. for i, col := range table.PKColumns() {
  132. if col.SQLType.IsText() {
  133. pk[i] = res[i]
  134. } else if col.SQLType.IsNumeric() {
  135. n, err := strconv.ParseInt(res[i], 10, 64)
  136. if err != nil {
  137. return false, err
  138. }
  139. pk[i] = n
  140. } else {
  141. return false, errors.New("unsupported")
  142. }
  143. }
  144. ids = []core.PK{pk}
  145. session.engine.logger.Debug("[cacheGet] cache ids:", newsql, ids)
  146. err = core.PutCacheSql(cacher, ids, tableName, newsql, args)
  147. if err != nil {
  148. return false, err
  149. }
  150. } else {
  151. session.engine.logger.Debug("[cacheGet] cache hit sql:", newsql)
  152. }
  153. if len(ids) > 0 {
  154. structValue := reflect.Indirect(reflect.ValueOf(bean))
  155. id := ids[0]
  156. session.engine.logger.Debug("[cacheGet] get bean:", tableName, id)
  157. sid, err := id.ToString()
  158. if err != nil {
  159. return false, err
  160. }
  161. cacheBean := cacher.GetBean(tableName, sid)
  162. if cacheBean == nil {
  163. cacheBean = bean
  164. has, err = session.nocacheGet(reflect.Struct, cacheBean, sqlStr, args...)
  165. if err != nil || !has {
  166. return has, err
  167. }
  168. session.engine.logger.Debug("[cacheGet] cache bean:", tableName, id, cacheBean)
  169. cacher.PutBean(tableName, sid, cacheBean)
  170. } else {
  171. session.engine.logger.Debug("[cacheGet] cache hit bean:", tableName, id, cacheBean)
  172. has = true
  173. }
  174. structValue.Set(reflect.Indirect(reflect.ValueOf(cacheBean)))
  175. return has, nil
  176. }
  177. return false, nil
  178. }