session_tx_plus.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. package xorm
  2. import (
  3. "sync"
  4. "github.com/xormplus/core"
  5. )
  6. const (
  7. PROPAGATION_REQUIRED = 0 //Support a current transaction; create a new one if none exists.
  8. PROPAGATION_SUPPORTS = 1 //Support a current transaction; execute non-transactionally if none exists.
  9. PROPAGATION_MANDATORY = 2 //Support a current transaction; return an error if no current transaction exists.
  10. PROPAGATION_REQUIRES_NEW = 3 //Create a new transaction, suspending the current transaction if one exists.
  11. PROPAGATION_NOT_SUPPORTED = 4 //Do not support a current transaction; rather always execute non-transactionally.
  12. PROPAGATION_NEVER = 5 //Do not support a current transaction; return an error if a current transaction exists.
  13. PROPAGATION_NESTED = 6 //Execute within a nested transaction if a current transaction exists, behave like PROPAGATION_REQUIRED else.
  14. )
  15. type Transaction struct {
  16. txSession *Session
  17. transactionDefinition int
  18. isNested bool
  19. savePointID string
  20. }
  21. func (transaction *Transaction) TransactionDefinition() int {
  22. return transaction.transactionDefinition
  23. }
  24. func (transaction *Transaction) IsExistingTransaction() bool {
  25. if transaction.txSession.Tx == nil {
  26. return false
  27. } else {
  28. return true
  29. }
  30. }
  31. func (transaction *Transaction) GetSavePointID() string {
  32. return transaction.savePointID
  33. }
  34. func (transaction *Transaction) Session() *Session {
  35. return transaction.txSession
  36. }
  37. func (transaction *Transaction) Do(doFunc func(params ...interface{}), params ...interface{}) {
  38. if transaction.isNested {
  39. go doFunc(params...)
  40. } else {
  41. doFunc(params...)
  42. }
  43. }
  44. func (transaction *Transaction) WaitForDo(doFunc func(params ...interface{}), params ...interface{}) {
  45. if transaction.isNested {
  46. var w sync.WaitGroup
  47. w.Add(1)
  48. go func() {
  49. doFunc(params...)
  50. w.Done()
  51. }()
  52. w.Wait()
  53. } else {
  54. doFunc(params...)
  55. }
  56. }
  57. func (session *Session) Begin(transactionDefinition ...int) (*Transaction, error) {
  58. var tx *Transaction
  59. if len(transactionDefinition) == 0 {
  60. tx = session.transaction(PROPAGATION_REQUIRED)
  61. } else {
  62. tx = session.transaction(transactionDefinition[0])
  63. }
  64. err := tx.Begin()
  65. if err != nil {
  66. return nil, err
  67. }
  68. return tx, nil
  69. }
  70. func (session *Session) transaction(transactionDefinition int) *Transaction {
  71. if transactionDefinition > 6 || transactionDefinition < 0 {
  72. return &Transaction{txSession: session, transactionDefinition: PROPAGATION_REQUIRED}
  73. }
  74. return &Transaction{txSession: session, transactionDefinition: transactionDefinition}
  75. }
  76. // Begin a transaction
  77. func (transaction *Transaction) Begin() error {
  78. switch transaction.transactionDefinition {
  79. case PROPAGATION_REQUIRED:
  80. if !transaction.IsExistingTransaction() {
  81. if err := transaction.txSession.begin(); err != nil {
  82. return err
  83. }
  84. } else {
  85. if transaction.txSession.currentTransaction != nil {
  86. transaction.savePointID = transaction.txSession.currentTransaction.savePointID
  87. }
  88. transaction.isNested = true
  89. }
  90. transaction.txSession.currentTransaction = transaction
  91. return nil
  92. case PROPAGATION_SUPPORTS:
  93. if transaction.IsExistingTransaction() {
  94. transaction.isNested = true
  95. if transaction.txSession.currentTransaction != nil {
  96. transaction.savePointID = transaction.txSession.currentTransaction.savePointID
  97. }
  98. transaction.txSession.currentTransaction = transaction
  99. }
  100. return nil
  101. case PROPAGATION_MANDATORY:
  102. if !transaction.IsExistingTransaction() {
  103. return ErrNestedTransaction
  104. } else {
  105. if transaction.txSession.currentTransaction != nil {
  106. transaction.savePointID = transaction.txSession.currentTransaction.savePointID
  107. }
  108. transaction.isNested = true
  109. transaction.txSession.currentTransaction = transaction
  110. }
  111. return nil
  112. case PROPAGATION_REQUIRES_NEW:
  113. transaction.txSession = transaction.txSession.Engine.NewSession()
  114. if err := transaction.txSession.begin(); err != nil {
  115. return err
  116. }
  117. transaction.isNested = false
  118. transaction.txSession.currentTransaction = transaction
  119. return nil
  120. case PROPAGATION_NOT_SUPPORTED:
  121. if transaction.IsExistingTransaction() {
  122. transaction.isNested = true
  123. transaction.txSession = transaction.txSession.Engine.NewSession()
  124. }
  125. return nil
  126. case PROPAGATION_NEVER:
  127. if transaction.IsExistingTransaction() {
  128. return ErrNestedTransaction
  129. }
  130. return nil
  131. case PROPAGATION_NESTED:
  132. if !transaction.IsExistingTransaction() {
  133. if err := transaction.txSession.begin(); err != nil {
  134. return err
  135. }
  136. } else {
  137. transaction.isNested = true
  138. dbtype := transaction.txSession.Engine.Dialect().DBType()
  139. if dbtype == core.MSSQL {
  140. transaction.savePointID = "xorm" + NewShortUUID().String()
  141. } else {
  142. transaction.savePointID = "xorm" + NewV1().WithoutDashString()
  143. }
  144. if err := transaction.SavePoint(transaction.savePointID); err != nil {
  145. return err
  146. }
  147. transaction.txSession.IsAutoCommit = false
  148. transaction.txSession.IsCommitedOrRollbacked = false
  149. transaction.txSession.currentTransaction = transaction
  150. }
  151. return nil
  152. default:
  153. return ErrTransactionDefinition
  154. }
  155. }
  156. // Commit When using transaction, Commit will commit all operations.
  157. func (transaction *Transaction) Commit() error {
  158. switch transaction.transactionDefinition {
  159. case PROPAGATION_REQUIRED:
  160. if !transaction.IsExistingTransaction() {
  161. return ErrNotInTransaction
  162. }
  163. if !transaction.isNested {
  164. err := transaction.txSession.commit()
  165. if err != nil {
  166. return err
  167. }
  168. }
  169. return nil
  170. case PROPAGATION_SUPPORTS:
  171. if transaction.IsExistingTransaction() {
  172. if !transaction.isNested {
  173. err := transaction.txSession.commit()
  174. if err != nil {
  175. return err
  176. }
  177. }
  178. }
  179. return nil
  180. case PROPAGATION_MANDATORY:
  181. if !transaction.IsExistingTransaction() {
  182. return ErrNotInTransaction
  183. }
  184. if !transaction.isNested {
  185. err := transaction.txSession.commit()
  186. if err != nil {
  187. return err
  188. }
  189. }
  190. return nil
  191. case PROPAGATION_REQUIRES_NEW:
  192. if !transaction.IsExistingTransaction() {
  193. return ErrNotInTransaction
  194. }
  195. if !transaction.isNested {
  196. err := transaction.txSession.commit()
  197. if err != nil {
  198. return err
  199. }
  200. }
  201. return nil
  202. case PROPAGATION_NOT_SUPPORTED:
  203. if transaction.IsExistingTransaction() {
  204. return ErrNestedTransaction
  205. }
  206. return nil
  207. case PROPAGATION_NEVER:
  208. if transaction.IsExistingTransaction() {
  209. return ErrNestedTransaction
  210. }
  211. return nil
  212. case PROPAGATION_NESTED:
  213. if !transaction.IsExistingTransaction() {
  214. return ErrNotInTransaction
  215. }
  216. if !transaction.isNested {
  217. err := transaction.txSession.commit()
  218. if err != nil {
  219. return err
  220. }
  221. } else if transaction.txSession.rollbackSavePointID == transaction.savePointID {
  222. if err := transaction.RollbackToSavePoint(transaction.savePointID); err != nil {
  223. transaction.txSession.rollbackSavePointID = ""
  224. return err
  225. }
  226. }
  227. return nil
  228. default:
  229. return ErrTransactionDefinition
  230. }
  231. }
  232. // Rollback When using transaction, you can rollback if any error
  233. func (transaction *Transaction) Rollback() error {
  234. switch transaction.transactionDefinition {
  235. case PROPAGATION_REQUIRED:
  236. if !transaction.IsExistingTransaction() {
  237. return ErrNotInTransaction
  238. }
  239. if transaction.savePointID == "" {
  240. err := transaction.txSession.rollback()
  241. if err != nil {
  242. return err
  243. }
  244. } else {
  245. transaction.txSession.rollbackSavePointID = transaction.savePointID
  246. }
  247. return nil
  248. case PROPAGATION_SUPPORTS:
  249. if transaction.IsExistingTransaction() {
  250. if transaction.savePointID == "" {
  251. err := transaction.txSession.rollback()
  252. if err != nil {
  253. return err
  254. }
  255. } else {
  256. transaction.txSession.rollbackSavePointID = transaction.savePointID
  257. }
  258. return nil
  259. }
  260. return nil
  261. case PROPAGATION_MANDATORY:
  262. if !transaction.IsExistingTransaction() {
  263. return ErrNotInTransaction
  264. }
  265. if transaction.savePointID == "" {
  266. err := transaction.txSession.rollback()
  267. if err != nil {
  268. return err
  269. }
  270. } else {
  271. transaction.txSession.rollbackSavePointID = transaction.savePointID
  272. }
  273. return nil
  274. case PROPAGATION_REQUIRES_NEW:
  275. if !transaction.IsExistingTransaction() {
  276. return ErrNotInTransaction
  277. }
  278. err := transaction.txSession.rollback()
  279. if err != nil {
  280. return err
  281. }
  282. return nil
  283. case PROPAGATION_NOT_SUPPORTED:
  284. if transaction.IsExistingTransaction() {
  285. return ErrNestedTransaction
  286. }
  287. return nil
  288. case PROPAGATION_NEVER:
  289. if transaction.IsExistingTransaction() {
  290. return ErrNestedTransaction
  291. }
  292. return nil
  293. case PROPAGATION_NESTED:
  294. if !transaction.IsExistingTransaction() {
  295. return ErrNotInTransaction
  296. }
  297. if transaction.txSession.rollbackSavePointID == transaction.savePointID {
  298. return nil
  299. }
  300. if transaction.isNested {
  301. if err := transaction.RollbackToSavePoint(transaction.savePointID); err != nil {
  302. return err
  303. }
  304. return nil
  305. } else {
  306. err := transaction.txSession.rollback()
  307. if err != nil {
  308. return err
  309. }
  310. return nil
  311. }
  312. default:
  313. return ErrTransactionDefinition
  314. }
  315. }
  316. func (transaction *Transaction) SavePoint(savePointID string) error {
  317. if transaction.txSession.Tx == nil {
  318. return ErrNotInTransaction
  319. }
  320. var lastSQL string
  321. dbtype := transaction.txSession.Engine.Dialect().DBType()
  322. if dbtype == core.MSSQL {
  323. lastSQL = "save tran " + savePointID
  324. } else {
  325. lastSQL = "SAVEPOINT " + savePointID + ";"
  326. }
  327. transaction.txSession.saveLastSQL(lastSQL)
  328. if _, err := transaction.txSession.Tx.Exec(lastSQL); err != nil {
  329. return err
  330. }
  331. return nil
  332. }
  333. func (transaction *Transaction) RollbackToSavePoint(savePointID string) error {
  334. if transaction.txSession.Tx == nil {
  335. return ErrNotInTransaction
  336. }
  337. var lastSQL string
  338. dbtype := transaction.txSession.Engine.Dialect().DBType()
  339. if dbtype == core.MSSQL {
  340. lastSQL = "rollback tran " + savePointID
  341. } else {
  342. lastSQL = "ROLLBACK TO SAVEPOINT " + transaction.savePointID + ";"
  343. }
  344. transaction.txSession.saveLastSQL(lastSQL)
  345. if _, err := transaction.txSession.Tx.Exec(lastSQL); err != nil {
  346. return err
  347. }
  348. return nil
  349. }