As we all know, databases have transaction processing (Database Transaction), When no operations in a transaction can be performed, the previous operations will be rolled back.
If operations are performed on the same database, you can directly use database transactions for processing. But what if cross-database operations are performed? You can use JTA. Let's take a look at the explanation of JTA in Baidu Encyclopedia:"JTA (Java transaction API)TransactionsAPI. JTA allows applicationsProgramExecute Distributed Transaction Processing-access and update data on two or more network computer resources.". If you are interested, you can search for JTA usage.
Putting rollback on the business layer has advantages and disadvantages
The advantage is that you do not need to add the DaO layer.CodeThe Dao layer only plays the role of reading and writing data, and the operation granularity is very small. Fine Granularity means that the DaO layer interface can have better reusability. In addition, if JTA is not used, SQL-related statements are not incorporated into the business layer. All DB-related parts are encapsulated in the DaO layer and will not be leaked to the upper layer. When the database is changed, you only need to change the data interface code of the DaO layer, instead of the Business Layer Code.
The disadvantage is that performing code rollback at the business layer is a complicated task. You need to make a step-by-step judgment and the rollback commands are accumulated.
Why is the rollback command accumulated?
Assume that there are four operations (operation1 ~ 4). The operation is accepted only when the four operations are successful. Otherwise, the operation is performed before rollback.
The general logic is (pseudo code, assuming there are four replacement operations, each replacement is successful is correct, otherwise the previous operation will be rolled back ):
Public Boolean Rollbackexample (string oldid, object newone ){ Boolean Res = True ; Object oldone = GetObject (oldid); Res = Replace1 (oldid, newone ); If (RES ){ // Operation1 success // Now doing operation 2 Res = Replace2 (oldid, newone ); If (RES ){ // Operation2 success // Now doing operation 3 Res = Replace3 (oldid, newone ); If (RES ){ // Operation3 success // Now doing operation 4 Res = Replace4 (oldid, newone ); If (RES ){ Return True ;} Else { // Rollback replace3 \ replace2 \ replace1 Replace3 (newone. GETID (), oldone); replace2 (newone. GETID (), oldone); replace1 (newone. GETID (), oldone ); Return False ;}} Else { // Rollback replace2 \ replace1 Replace2 (newone. GETID (), oldone); replace1 (newone. GETID (), oldone ); Return False ;}} Else { // Rollback replace1 Replace1 (newone. GETID (), oldone ); Return False ;}} Else { Return False ;}}
We can see that the code is judged step by step, and the rollback list is gradually increased based on the depth of the operation. The rollback operation is proposed separately to make it more obvious:
When the second operation fails, you only need to roll back
//Rollback replace1Replace1 (newone. GETID (), oldone );
When the third operation fails, you need to roll back:
//Rollback replace2 \ replace1Replace2 (newone. GETID (), oldone); replace1 (newone. GETID (), oldone );
When the fourth operation fails, roll back:
//Rollback replace3 \ replace2 \ replace1Replace3 (newone. GETID (), oldone); replace2 (newone. GETID (), oldone); replace1 (newone. GETID (), oldone );
Assuming that the transaction consists of N operations, an error occurs when the nth operation is performed and a N-1 item rollback is required. The accumulated code is 1 + 2 + ...... + N-1 = N (N-1)/2 lines of code, intuitive point of view is if there are 10 operations, then theoretically there are 9 possible rollback operations, in addition, 45 lines of code for rollback will appear in the function. The average number of repeated occurrences of the code used to describe rollback reaches 5. Very slow.
How can we solve the problem of the Code being not elegant?
First, the judgment condition is indispensable, that is, the IF-else statement cannot be omitted. Because operationj may be running on the basis of operationi (J later then I), it is necessary to judge step by step to avoid errors.
Second, no matter which step is wrong, the rollback operation is directly related to the execution depth. When the K step goes wrong, the K-1 and the previous steps need to be rolled back, as is true for every operation. This property can be found in the switch statement without a break. After case1 is executed, case2 will be executed ...... And so on.
Therefore, we can design the rollback operation to a switch-case statement. The pseudocode is as follows:
Public Boolean Rollbackexample2 (string oldid, object newone ){ Boolean Res = True ; Object oldone = GetObject (oldid ); Int phase = 0 ;Res = Replace1 (oldid, newone ); If (RES) {res = Replace2 (oldid, newone ); If (RES) {res = Replace3 (oldid, newone ); If (RES) {res = Replace4 (oldid, newone ); If (RES ){Phase = 4 ;}} Else {Phase = 3 ;}} Else {Phase = 2 ;}} Else {Phase = 1 ;} Switch (Phase){ Case 4 : Return True ; Case 3: Replace3 (newone. GETID (), oldone ); Case 2 : Replace2 (newone. GETID (), oldone ); Case 1 : Replace1 (newone. GETID (), oldone ); Default : Return False ;}}
As you can see, when you use the switch-case structure + Phase Stage judgment, there will be no code redundancy for the rollback command.