session_tx_plus.go 10 KB

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