transaction.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  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. transaction.txSession = transaction.txSession.Engine.NewSession()
  122. if transaction.IsExistingTransaction() {
  123. transaction.isNested = true
  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. }
  222. return nil
  223. default:
  224. return ErrTransactionDefinition
  225. }
  226. }
  227. // Rollback When using transaction, you can rollback if any error
  228. func (transaction *Transaction) Rollback() error {
  229. switch transaction.transactionDefinition {
  230. case PROPAGATION_REQUIRED:
  231. if !transaction.IsExistingTransaction() {
  232. return ErrNotInTransaction
  233. }
  234. err := transaction.txSession.rollback()
  235. if err != nil {
  236. return err
  237. }
  238. return nil
  239. case PROPAGATION_SUPPORTS:
  240. if transaction.IsExistingTransaction() {
  241. err := transaction.txSession.rollback()
  242. if err != nil {
  243. return err
  244. }
  245. return nil
  246. }
  247. return nil
  248. case PROPAGATION_MANDATORY:
  249. if !transaction.IsExistingTransaction() {
  250. return ErrNotInTransaction
  251. }
  252. if transaction.savePointID != "" {
  253. if err := transaction.RollbackToSavePoint(transaction.savePointID); err != nil {
  254. return err
  255. }
  256. return nil
  257. } else {
  258. err := transaction.txSession.rollback()
  259. if err != nil {
  260. return err
  261. }
  262. return nil
  263. }
  264. case PROPAGATION_REQUIRES_NEW:
  265. if !transaction.IsExistingTransaction() {
  266. return ErrNotInTransaction
  267. }
  268. err := transaction.txSession.rollback()
  269. if err != nil {
  270. return err
  271. }
  272. return nil
  273. case PROPAGATION_NOT_SUPPORTED:
  274. if transaction.IsExistingTransaction() {
  275. return ErrNestedTransaction
  276. }
  277. return nil
  278. case PROPAGATION_NEVER:
  279. if transaction.IsExistingTransaction() {
  280. return ErrNestedTransaction
  281. }
  282. return nil
  283. case PROPAGATION_NESTED:
  284. if !transaction.IsExistingTransaction() {
  285. return ErrNotInTransaction
  286. }
  287. if transaction.isNested {
  288. if err := transaction.RollbackToSavePoint(transaction.savePointID); err != nil {
  289. return err
  290. }
  291. return nil
  292. } else {
  293. err := transaction.txSession.rollback()
  294. if err != nil {
  295. return err
  296. }
  297. return nil
  298. }
  299. default:
  300. return ErrTransactionDefinition
  301. }
  302. }
  303. func (transaction *Transaction) SavePoint(savePointID string) error {
  304. if transaction.txSession.Tx == nil {
  305. return ErrNotInTransaction
  306. }
  307. var lastSQL string
  308. dbtype := transaction.txSession.Engine.Dialect().DBType()
  309. if dbtype == core.MSSQL {
  310. lastSQL = "save tran " + savePointID
  311. } else {
  312. lastSQL = "SAVEPOINT " + savePointID + ";"
  313. }
  314. transaction.txSession.saveLastSQL(lastSQL)
  315. if _, err := transaction.txSession.Tx.Exec(lastSQL); err != nil {
  316. return err
  317. }
  318. return nil
  319. }
  320. func (transaction *Transaction) RollbackToSavePoint(savePointID string) error {
  321. if transaction.txSession.Tx == nil {
  322. return ErrNotInTransaction
  323. }
  324. var lastSQL string
  325. dbtype := transaction.txSession.Engine.Dialect().DBType()
  326. if dbtype == core.MSSQL {
  327. lastSQL = "rollback tran " + savePointID
  328. } else {
  329. lastSQL = "ROLLBACK TO SAVEPOINT " + transaction.savePointID + ";"
  330. }
  331. transaction.txSession.saveLastSQL(lastSQL)
  332. if _, err := transaction.txSession.Tx.Exec(lastSQL); err != nil {
  333. return err
  334. }
  335. return nil
  336. }