Django 資料庫 事務 回滾
如果你花費了很多的時間去進行Django資料庫事務處理的話,你將會了解到這是讓人暈頭轉向的。
在過去,只是提供了簡單的基礎文件,要想清楚知道它是怎麼使用的,還必須要通過建立和執行Django的事務處理。
這裡有眾多的Django事務處理的名詞,例如:commit_on_success , commit_manually , commit_unless_maneged,rollback_unless_managed,enter_transaction_management,leace_transaction_management,這些只是其中的一部分而已。
最讓人感到幸運的是,Django 1.6版本釋出後。現在你可以緊緊使用幾個函式就可以實現事務處理了,在我們進入學習這些函式的幾秒之前。首先,我們要明確下面這些問題:
- 什麼是事務處理呢?
- Django 1.6中的出現了那些新的事物處理優先權?
在進入“要怎麼才是Django 1.6版本中正確的事務處理”之前,請先了解一下下面的詳細案例:
帶狀案例
事務
- 推薦的方式
- 使用分隔符
- 每一個HTTP請求事務
儲存點
巢狀的事務
事務是什麼?
根據SQL-92所說,"一個SQL事務(有時簡稱為事務)是一個可以原子性恢復的SQL語句執行序列"。換句話說,所有的SQL語句一起執行和提交。同樣的,當回滾時,所有語句也一併回滾。
例如:
# START note = Note(title="my first note", text="Yay!") note = Note(title="my second note", text="Whee!") address1.save() address2.save() # COMMIT
所以一個事務是 資料庫中單個的工作單元。它是由一個start transaction開始和一個commit或者顯式的rollback結束。
Django 1.6之前的事務管理有什麼問題?
為了完整地回答這個問題,我們必須闡述一下事務在資料庫、客戶端以及Django中是如何處理的。
資料庫
資料庫中的每一條語句都執行在一個事務中,這個事務甚至可以只包含一條語句。
幾乎所有的資料庫都有一個AUTOCOMMIT設定,通常它被預設設定為True。AUTOCOMMIT將所有語句包裝到一個事務裡,只要語句成功執行,這個事務就立即被提交。當然你也可以手動呼叫START_TRANSACTION,它會暫時將AUTOCOMMIT掛起,直到你呼叫COMMIT_TRANSACTION或者ROLLBACK。
然後,這種方式將會使AUTOCOMMIT設定的作用於每條語句後的隱式提交失效。
然而,有諸如像sqlite3和mysqldb的python客戶端庫,它允許python程式與資料庫本身相連線。這些庫遵循一套如何訪問與查詢資料庫的標準。該DB API 2.0標準,被描述在PEP 249之中。雖然它可能讓人閱讀稍幹一些。一個重要帶走的是,在PEP 249狀態之中,預設資料庫應該關閉自動提交功能。
這明顯與資料庫內發生了什麼矛盾衝突:
- 資料庫語句總是要在一個事務中執行。此資料庫一般會為你開啟自動提交功能。
- 不過,根據PEP 249,這不應該發生。
- 客戶端庫必須反映在資料庫之中發生了什麼?但由於他們不允許預設開啟自動提交功能。他們只是簡單地在一個事務中包裹sql語句。就像資料庫。
好啦,在我身邊呆久一些吧。
Django
進入Django,Django也有關於事務處理的話要說。在Django 1.5和更早的版本。當你寫資料到資料庫時,Django基本上是執行一個開放的事務和自動提交該事務功能。所以每次你所稱謂的像諸如model.save() 或者model.update()的東西,Django生成相應的sql語句,並提交該事務。
也有在Django 1.5和更早的版本,它是建議你使用TransactionMiddleware繫結http請求事務。每個請求提供了一個事務。如果返回的響應沒有異常,Django會提交此事務。但如果你的檢視功能丟擲一個錯誤,回滾將被呼叫。這實際上說明,它關閉了自動提交功能。如果你想要標準化,資料庫級別自動提交風格式的事務管理,你必須管理你自己的交易-通常是通過使用事務裝飾你的檢視功能,例如@transaction.commit_manually,或者@transaction.commit_on_success.
吸一口氣,或者兩口。
這意味著什麼?
是啊,在那兒有許多事情要做,而事實證明,大多數開發者正需要這個標準資料庫級的自動提交功能-有意義的事務往往是留在幕後處理的。做你自己的事,直到你需要手動調整他們。
在Django 1.6版本之中,什麼是正確關於事務管理呢?
現在,歡迎來到Django 1.6.盡力忘掉一切吧,我們只是談論而已,只是記得在Django 1.6中,你可以使用資料庫,需要時可以手動自動提交和管理事務。從本質上來說,我們有一個更簡單的模型,基本上是要把設計什麼樣的資料庫擺在首位。
好啦!大功告成,讓我們寫程式碼吧?
Stripe案例
下面,我們使用處理一個使用者註冊的例子,呼叫了Stripe來處理信用卡程序。
def register(request):
user = None
if request.method == 'POST':
form = UserForm(request.POST)
if form.is_valid():
customer = Customer.create("subscription",
email = form.cleaned_data['email'],
description = form.cleaned_data['name'],
card = form.cleaned_data['stripe_token'],
plan="gold",
)
cd = form.cleaned_data
try:
user = User.create(cd['name'], cd['email'], cd['password'],
cd['last_4_digits'])
if customer:
user.stripe_id = customer.id
user.save()
else:
UnpaidUsers(email=cd['email']).save()
except IntegrityError:
form.addError(cd['email'] + ' is already a member')
else:
request.session['user'] = user.pk
return HttpResponseRedirect('/')
else:
form = UserForm()
return render_to_response(
'register.html',
{
'form': form,
'months': range(1, 12),
'publishable': settings.STRIPE_PUBLISHABLE,
'soon': soon(),
'user': user,
'years': range(2011, 2036),
},
context_instance=RequestContext(request)
)
例子首先呼叫了Customer.create,實際上就是呼叫Stripe來處理信用卡程序,然後我們建立一個新使用者。如果我們得到來自Stripe的響應,我們就用stripe_id更新新建立的使用者。如果我們沒有得到響應(Stripe已關閉),我們將用新建立使用者的email向UnpaidUsers表增加一個新條目,這樣我們可以讓他們稍後重試他們信用卡資訊。
思路是這樣的:如果Stripe沒有響應,使用者依然可以註冊,然後開始使用我們的網站。我們將在稍後的時候讓使用者提供信用卡的資訊。
“我明白這是一個特殊的例子,並且這也不是我想完成的功能的方式,但是它的目的是展示交易”
考慮交易,牢記住在Django1.6中提供了對於資料庫的“AUTOCOMMIT”功能。接下來看一下資料庫相關的程式碼:
cd = form.cleaned_data
try:
user = User.create(cd['name'], cd['email'], cd['password'], cd['last_4_digits'])
if customer:
user.stripe_id = customer.id
user.save()
else:
UnpaidUsers(email=cd['email']).save()
except IntegrityError:
你能發現問題了嗎?如果“UnpaidUsers(email=cd['email']).save()” 執行失敗,會發生什麼?
有一個使用者,註冊了系統;然後系統認為已經核對過信用卡了。但是事實上,系統並沒有核對過。
我們僅僅想得到其中一種結果:
1.在資料庫中建立了使用者,並有了stripe_id
2.在資料庫中建立了使用者,但是沒有stripe_id。同時在相關的“UnpaidUsers”行,存有相同的郵件地址
這就意味著,我們想要的是分開的資料庫語句頭完成任務或者回滾。這個例子很好的說明了這個交易。
首先,我們寫一些測試用例來驗證事情是否按照我們想象的方式執行:
@mock.patch('payments.models.UnpaidUsers.save', side_effect = IntegrityError)
def test_registering_user_when_strip_is_down_all_or_nothing(self, save_mock):
#create the request used to test the view
self.request.session = {}
self.request.method='POST'
self.request.POST = {'email' : '[email protected]',
'name' : 'pyRock',
'stripe_token' : '...',
'last_4_digits' : '4242',
'password' : 'bad_password',
'ver_password' : 'bad_password',
}
#mock out stripe and ask it to throw a connection error
with mock.patch('stripe.Customer.create', side_effect =
socket.error("can't connect to stripe")) as stripe_mock:
#run the test
resp = register(self.request)
#assert there is no record in the database without stripe id.
users = User.objects.filter(email="[email protected]")
self.assertEquals(len(users), 0)
#check the associated table also didn't get updated
unpaid = UnpaidUsers.objects.filter(email="[email protected]")
self.assertEquals(len(unpaid), 0)
當我們嘗試去儲存“UnpaidUsers”,測試上方的直譯器就會跑出異常'IntegrityError' 。
接下來是解釋這個問題的答案,“當“UnpaidUsers(email=cd['email']).save()”執行的時候到底發生了什麼?” 下面一段程式碼建立了一段對話,我們需要在註冊函式中給出一些合適的資訊。然後“with mock.patch” 會強制系統去認為Stripe沒響應,最終就跳到我們的測試用例中。
resp = register(self.request)
上面這段話僅僅是呼叫我們的註冊檢視去傳遞請求。然後我們僅僅需要去核對錶是否有更新:
#assert there is no record in the database without stripe_id.
users = User.objects.filter(email="[email protected]")
self.assertEquals(len(users), 0)
#check the associated table also didn't get updated
unpaid = UnpaidUsers.objects.filter(email="[email protected]")
self.assertEquals(len(unpaid), 0)
所以如果我們運行了測試用例,那麼它就該執行失敗:
======================================================================
FAIL: test_registering_user_when_strip_is_down_all_or_nothing (tests.payments.testViews.RegisterPageTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/j1z0/.virtualenvs/django_1.6/lib/python2.7/site-packages/mock.py", line 1201, in patched
return func(*args, **keywargs)
File "/Users/j1z0/Code/RealPython/mvp_for_Adv_Python_Web_Book/tests/payments/testViews.py", line 266, in test_registering_user_when_strip_is_down_all_or_nothing
self.assertEquals(len(users), 0)
AssertionError: 1 != 0
----------------------------------------------------------------------
贊。這就是我們最終想要的結果。
記住:我們這裡已經練習了“測試驅動開發”的能力。錯誤資訊提示我們:使用者資訊已經被儲存到資料庫中,但是這個並不是我們想要的,因為我們並沒有付費!
事務交易用於挽救這樣問題 ...
事務
對於Django1.6,有很多種方式來建立事務。
這裡簡單介紹幾種。
推薦的方法
依據Django1.6的文件,“Django提供了一種簡單的API去控制資料庫的事務交易...原子操作用來定義資料庫事務的屬性。原子操作允許我們在資料庫保證的前提下,建立一堆程式碼。如果這些程式碼被成功的執行,所對應的改變也會提交到資料庫中。如果有異常發生,那麼操作就會回滾。”
原子操作可以被用於解釋操作或者是內容管理。所以如果我們用作為內容管理的時候,我們的註冊函式的程式碼就會如下:
from django.db import transaction
try:
with transaction.atomic():
user = User.create(cd['name'], cd['email'], cd['password'], cd['last_4_digits'])
if customer:
user.stripe_id = customer.id
user.save()
else:
UnpaidUsers(email=cd['email']).save()
except IntegrityError:
form.addError(cd['email'] + ' is already a member')
注意在“with transaction.atomic()”這一行。這塊程式碼將會在事務內部執行。所以如果我們重新運行了我們的測試,他們都將會通過。
記住:事務是一個工作單元,所以當“UnpaidUsers”呼叫失敗的時候,內容管理的所有操作都會被一起回滾。
使用裝飾器
除了上面的做法,我們能使用Python的裝飾器特性來使用事務。
@transaction.atomic():
def register(request):
...snip....
try:
user = User.create(cd['name'], cd['email'], cd['password'], cd['last_4_digits'])
if customer:
user.stripe_id = customer.id
user.save()
else:
UnpaidUsers(email=cd['email']).save()
except IntegrityError:
form.addError(cd['email'] + ' is already a member')
如果我們重新跑一次測試,那還是會一樣失敗。
為啥呢?為啥事務沒有正確回滾呢?原因在與transaction.atomic會嘗試捕獲某種異常,而我們程式碼里人肉捕抓了(例如 try-except 程式碼塊裡的IntegrityError 異常),所以 transaction.atomic 永遠看不到這個異常,所以標準的AUTOCOMMIT 流程就此無效掉。
但是,刪掉try-catch語句會導致異常沒有捕獲,然後程式碼流程十有八九會就此亂掉。所以啊,也就是說不能去掉try-catch。
所以,技巧是將原子上下文管理器放入我們在第一個解決方案中的 try-catch 程式碼段裡。
再看一下正確的程式碼:
from django.db import transaction
try:
with transaction.atomic():
user = User.create(cd['name'], cd['email'], cd['password'], cd['last_4_digits'])
if customer:
user.stripe_id = customer.id
user.save()
else:
UnpaidUsers(email=cd['email']).save()
except IntegrityError:
form.addError(cd['email'] + ' is already a member')
當 UnpaidUsers 觸發 IntegrityError 時,上下文管理器 transaction.atomic() 會捕獲到它,並執行回滾操作。此時我們的程式碼在異常處理中執行(即行 theform.addErrorline),將會完成回滾操作,並且,如果必要的話,也可以安全的進行資料庫呼叫。也要注意:任何在上下文管理器 thetransaction.atomic() 前後的資料庫呼叫都不會受到它的執行結果的影響。
針對每次HTTP請求的事務交易
Django1.5和1.6版本都允許使用者操作請求事務模式。在這種模式下,Django會自動在事務中,處理你的檢視函式。如果檢視函式丟擲異常,Django會自動回滾事務;否則Django會提交事務。
為了實現這個功能,你需要在你想要有此功能的資料庫的配置中,設定“ATOMIC_REQUEST”為真。所以在我們的“settings.py”需要有如下設定:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(SITE_ROOT, 'test.db'),
'ATOMIC_REQUEST': True,
}
}
如果我們把直譯器放到檢視函式中,以上設定就會生效。所以這並沒有符合我們的想法。
但是,這裡依然值得注意的是直譯器:“ATOMIC_REQUESTS”和“@transaction.atomic”仍然會有可能在有異常丟擲的時候,處理這些錯誤。為了去捕捉這些錯誤,你需要去完成一些常規的中介軟體,或者需要去覆蓋“urls.hadler500”,或者是建立新的“500.html”模板。
儲存點
儘管事務是有原子性的,但還是能夠打散為多個“儲存點”——你可看作是“部分事務”。
例如,你有個事務包含了4條SQL語句,你可以在第二個SQL之後建立一個儲存點。一旦儲存點建立成功,就算第三條或第四條SQL執行失敗,你仍舊能夠做一個部分回滾,忽視後面兩條SQL,僅保留前面兩條。
基本上,這就像是提供了一個切割的能力:一個普通的事務能夠被切分為多個更“輕量”的事務,然後能進行部分回滾或部分提交。
但一定要注意,小心整個事務被無端回滾掉(例如由於丟擲了IntegrityError異常但卻沒有捕抓,那所有的儲存點都會因此被回滾掉的)
來看個示例程式碼,瞭解怎麼玩轉儲存點。
@transaction.atomic()
def save_points(self,save=True):
user = User.create('jj','inception','jj','1234')
sp1 = transaction.savepoint()
user.name = 'zheli hui guadiao, T.T'
user.stripe_id = 4
user.save()
if save:
transaction.savepoint_commit(sp1)
else:
transaction.savepoint_rollback(sp1)
示例中,整個函式都是屬於一個事務的。新User物件建立後,我們建立並得到了一個儲存點。然後後續3行語句——user.name = 'zheli hui guadiao, T.T'
user.stripe_id = 4
user.save()
——不屬於剛才的儲存點,因此他們有可能是屬於下面的savepoint_rollback或savepoint_commit的一部分。假設是savepoint_rollback, 那程式碼行user = User.create('jj','inception','jj','1234')仍舊會成功提交到資料庫中 ,而下面三行則不會成功。採用另外一種方法,下面的兩種測試用例描述了儲存點是如何工作的:
def test_savepoint_rollbacks(self):
self.save_points(False)
#verify that everything was stored
users = User.objects.filter(email="inception")
self.assertEquals(len(users), 1)
#note the values here are from the original create call
self.assertEquals(users[0].stripe_id, '')
self.assertEquals(users[0].name, 'jj')
def test_savepoint_commit(self):
self.save_points(True)
#verify that everything was stored
users = User.objects.filter(email="inception")
self.assertEquals(len(users), 1)
#note the values here are from the update calls
self.assertEquals(users[0].stripe_id, '4')
self.assertEquals(users[0].name, 'starting down the rabbit hole')
同樣,在我們提交或者回滾儲存點之後,我們仍然可以繼續在同一個事務中工作。同時,這個執行結果不受之前儲存點輸出結果的影響。
例如,如果我們按照如下例子更新“save_points”函式,
@transaction.atomic()
def save_points(self,save=True):
user = User.create('jj','inception','jj','1234')
sp1 = transaction.savepoint()
user.name = 'starting down the rabbit hole'
user.save()
user.stripe_id = 4
user.save()
if save:
transaction.savepoint_commit(sp1)
else:
transaction.savepoint_rollback(sp1)
user.create('limbo','[email protected]','mind blown',
'1111')
即使無論是“savepoint_commit”或者“savepoint_rollback”被“limbo”這個使用者呼叫了,這個事務仍然會被成功建立。如果沒有建立成功,整個事務將會被回滾。
巢狀事務
採用“savepoint()”,“savepoint_commit”和“savepoint_rollback”去手動指定儲存點,將會自動一個巢狀事務,同時這個巢狀事務會自動為我們建立一個儲存點。並且,如果我們遇到錯誤,這個事務將會回滾。
下面用一個擴充套件的例子來說明:
@transaction.atomic()
def save_points(self,save=True):
user = User.create('jj','inception','jj','1234')
sp1 = transaction.savepoint()
user.name = 'starting down the rabbit hole'
user.save()
user.stripe_id = 4
user.save()
if save:
transaction.savepoint_commit(sp1)
else:
transaction.savepoint_rollback(sp1)
try:
with transaction.atomic():
user.create('limbo','[email protected]','mind blown',
'1111')
if not save: raise DatabaseError
except DatabaseError:
pass
這裡我們可以看到:在我們處理儲存點之後,我們採用“thetransaction.atomic”的上下文管理區擦出我們建立的"limbo"這個使用者。當上下文管理被呼叫的時候,它會建立一個儲存點(因為我們已經在事務裡面了),同時這個儲存點將會依據已經存在的上下文管理器去被執行或者回滾。
這樣下面兩個測試用例就描述了這個行文:
def test_savepoint_rollbacks(self):
self.save_points(False)
#verify that everything was stored
users = User.objects.filter(email="inception")
self.assertEquals(len(users), 1)
#savepoint was rolled back so we should have original values
self.assertEquals(users[0].stripe_id, '')
self.assertEquals(users[0].name, 'jj')
#this save point was rolled back because of DatabaseError
limbo = User.objects.filter(email="[email protected]")
self.assertEquals(len(limbo),0)
def test_savepoint_commit(self):
self.save_points(True)
#verify that everything was stored
users = User.objects.filter(email="inception")
self.assertEquals(len(users), 1)
#savepoint was committed
self.assertEquals(users[0].stripe_id, '4')
self.assertEquals(users[0].name, 'starting down the rabbit hole')
#save point was committed by exiting the context_manager without an exception
limbo = User.objects.filter(email="[email protected]")
self.assertEquals(len(limbo),1)
因此,在現實之中你可以使用原子或者在事務之中建立儲存點的儲存點。使用原子,你不必要很仔細地擔心提交和會滾,當這種情況發生時,你可以完全控制其中的儲存點。
結論
如果你有任何以往使用Django更早版本事務處理的經驗,你可以看到很多更簡單地事務處理模型。如下,在預設情況下,也有自動提交功能,它是一個很好的例子,即Django與python兩者都引以為豪所提供的“理智的”預設值。對於如此多的系統,你將不需要直接地來處理事務。只是讓“自動提交功能”來完成其工作,但如果你這樣做,我將希望這個帖子能提供你所需要像專家一樣在Django之中管理的事務處理。
相關推薦
Django 資料庫 事務 回滾
如果你花費了很多的時間去進行Django資料庫事務處理的話,你將會了解到這是讓人暈頭轉向的。在過去,只是提供了簡單的基礎文件,要想清楚知道它是怎麼使用的,還必須要通過建立和執行Django的事務處理。這裡有眾多的Django事務處理的名詞,例如:commit_on_succe
Django資料庫--事務及事務回滾
資料庫的讀寫操作中,事務在保證資料的安全性和一致性方面起著關鍵的作用,而回滾正是這裡面的核心操作。Django的ORM在事務方面也提供了不少的API。有事務出錯的整體回滾操作,也有基於儲存點的部分回滾。本文將討論Django中的這兩種機制的執行原理。 Django利用django.db.tr
spring事務管理,基於xml配置完成事務回滾;spring中資料庫表中欄位名和pojo中屬性名不一致時候,實現RowMapper介面手動封裝
宣告使用JDK8,spring5.0.7, 測試說明: service 層 宣告介面進行轉賬,從A轉賬B ,然後對AB 進行更新操作,在事務中對find方法開啟 只讀許可權,無法進行更新操作,造成事務回滾進行測試事務; 主要測試方法:* void tra
Transactional 事務回滾 分析
transactionalSpring的AOP事務管理默認是針對unchecked exception回滾(運行期異常,Runtime Exception)。 unchecked ,就是不用手工寫try catch的exception Exception作為基類,下面還分checked exception
Mysql存儲過程中的事務回滾
異常 procedure mysq span val com cnblogs 事務 erro create procedure test(in a int) BEGIN DECLARE t_error INTEGER DEFAULT 0;
Spring 實現部分事務回滾
light back true prop 回滾 sage .class lba aaa 例如有業務需求,在catch異常後,catch塊內把異常的信息存入到數據庫,而catch外的數據全部回滾 try { ....... aaaService.save();
回滾的意義---JDBC事務回滾探究
final cti span net etc rom tle img round JDBC手動事務提交回滾的常見寫法一直是rollback寫在commit的catch之後: try{ conn.setAutoCommit(false); ps.execu
laravel數據庫事務回滾
laravel 事務回滾 異常 背景:近期使用Laravel-5.4進行項目開發時涉及到關聯模型的數據操作,想到用事務回滾。中間涉及到數據引用及異常的捕獲。 數據庫事務回滾有兩種方式:自動回滾、手動回滾。舉例如下: 自動回滾 function(Request $r
事務 回滾
事務 不執行 color 才會 同時 bsp 一個 div sco 1 using (TransactionScope ts = new TransactionScope()) 2 { 3
淺談Spring中的事務回滾
spec style try 常見 產生原因 turn prop ret run 使用Spring管理事務過程中,碰到過一些坑,因此也稍微總結一下,方便後續查閱。1.代碼中事務控制的3種方式編程式事務:就是直接在代碼裏手動開啟事務,手動提交,手動回滾。優點就是可以靈
哪些異常是RuntimeException?Sql異常屬於RuntimeException嗎?Spring下SQL異常事務回滾
tail 好的 duplicate 代碼 blog 後拋 ase owa 接口文檔 一,為什麽框架中根本沒有對Exception的一般子類進行回滾配置,異常發生時,事務都進行了回滾 ,說好的只會對RuntimeException(Unchecked 非受檢異常)回滾呢?
ThinkPHP 實現數據庫事務回滾示例代碼
數據庫事務 lse 業務 ttr ans ron 示例 示例代碼 sta ThinkPHP提供了數據庫的事務支持,如果要在應用邏輯中使用事務,可以參考下面的方法: 啟動事務: $User->startTrans(); 提交事務: $User->c
mysql事務回滾
技術分享 default null 刪除 inf nod .com efault 人員 mysql使用事務前需確定存儲引擎為innodb,譬如刪除一個人員數據,會刪除和這個人有關的一些數據刪除,這個時候就會用到事務,出現一個刪除錯誤整個刪除事務就會取消。 事務回滾步驟: /
事務和事務回滾
mysq back 事務 sql 結束 cti 數據庫 ons transacti 1、定義:一件事從開始發生到結束的整個過程 2、作用:確保數據的一致性 3、事務和事務回滾的應用 1、SQL命令會 autocommit 到數據庫執行 2、事務操作
關於JaveEE中三大框架事務回滾異常捕獲的問題
我搭建了一個SSH框架,然後在MySQL資料庫中設定某表的一個欄位唯一值約束,後來我插入一條記錄,該記錄與資料庫中的記錄存在唯一值衝突,這樣hibernate就會丟擲DataAccessException異常,該異常是一個執行時異常,不需要人為處理。我在Dao層進行異常捕獲,程式碼如下 經
thinkphp對180萬資料批量更新支援事務回滾
目前測試180萬多的資料,大概是正常的,不知道再多了會怎麼樣了我們要給表改成 innodb型別 乾貨來了: set_time_limit(0);
thinkphp對180萬數據批量更新支持事務回滾
每次 UNC memory .... document number field start sele 目前測試180萬多的數據,大概是正常的,不知道再多了會怎麽樣了我們要給表改成 innodb類型 幹貨來了: set_time_limit(0);
jfinal中事務回滾
1. 事務回滾 ①@Before(Tx.class)寫在controller中會回滾,而寫在service中不回滾,詳見商城excel匯入分類 ②atom return false 或者有異常丟擲都會回滾事務,return true 才會提交事務,Db.tx 方法是有返回值true/fa
JAVA設定手動提交事務,回滾事務,提交事務
/** * 設定資料庫是否自動提交事務 * @param flag * @throws SQLException */ public void setAutoCommit(boolean flag) throws SQLException {
java不是分散式的兩個服務之間呼叫,如何實現事務回滾?
之前開發springCloud微服務,遇到微服務之間呼叫事務回滾問題,網上瀏覽存在多種解決方案。但是最後發現,實現服務回滾的多個服務必須要在同一個註冊中心下,也就是說,必須要有一個主服務管理者所有的分散式服務。 &