Spring事務 REQUIRES_NEW、NESTED與NOT_SUPPORTED

手動創建一個異常,用來探究Spring事務處理機制。
transactionManager爲DataSourceTransactionManager。
本次暫不討論JTA <tx:jta-transaction-manager/> ,由於公司項目歷史原因,不想在項目中使用Jta。

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

分別使用Propagation.NESTED與 Propagation.REQUIRES_NEW測試發現,在一系列的操作中,兩者的表現形式相同,都是Rollback了所有新增&更新操作。這是什麼原因?查看spring doc發現,REQUIRES_NEW與NOT_SUPPORTED都有下面一句話,需要使用JtaTransactionManager。
Note: Actual transaction suspension will not work on out-of-the-box on all transaction managers. This in particular applies to JtaTransactionManager, which requires the javax.transaction.TransactionManager to be made available it to it (which is server-specific in standard J2EE).
但實際情況並非如此,在DataSourceTransactionManager下使用,REQUIRES_NEW沒起任何作用,NOT_SUPPORTED則運行良好,正如doc所寫:Execute non-transactionally, suspend the current transaction if one exists.
至於NESTED,應該是因爲transaction整個都被rollback了,所以也沒有commit更新。在日誌中會出現如下片段:
DEBUG o.s.j.datasource.DataSourceTransactionManager - Creating nested transaction with name [com….OrderNo]
DEBUG o.s.j.datasource.DataSourceTransactionManager - Participating in existing transaction
……
DEBUG o.s.j.datasource.DataSourceTransactionManager - Releasing transaction savepoint

個人覺得在業務不是特別複雜的情況下,使用NOT_SUPPORTED更加簡單一些。
在Service其中一個方法中加註解:@Transactional(propagation = Propagation.NOT_SUPPORTED)

logback日誌:
10:21:38.107 [http-apr-8080-exec-6] DEBUG com.incito.logistics.common.SpecialCharFilter - process in /web/myorder/agreeRefund
10:21:38.109 [http-apr-8080-exec-6] DEBUG java.sql.Connection - {conn-100785} Connection
10:21:38.110 [http-apr-8080-exec-6] DEBUG java.sql.Connection - {conn-100785} Preparing Statement:      SELECT * FROM orders WHERE  id =?
10:21:38.110 [http-apr-8080-exec-6] DEBUG o.s.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
10:21:38.117 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100786} Executing Statement:      SELECT * FROM orders WHERE  id =?
10:21:38.117 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100786} Parameters: [930aebeae7c845c88bb15f985f3cde83]
10:21:38.117 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100786} Types: [java.lang.String]
10:21:38.138 [http-apr-8080-exec-6] DEBUG java.sql.ResultSet - {rset-100787} ResultSet
10:21:38.139 [http-apr-8080-exec-6] DEBUG java.sql.ResultSet - {rset-100787} Header: [id, completetime, generatetime, orderno, status, agentid, carid, goodsid, consigneeid, shipperid, photo, leavelocation, memo, signtime, type, refusal, ordercustom, priseid, weight, volume, goodscustom, refusalreason, refusalmemo, refusaltime, cancelreason, cancelmemo, arrstartcity]
10:21:38.139 [http-apr-8080-exec-6] DEBUG java.sql.ResultSet - {rset-100787} Result: [930aebeae7c845c88bb15f985f3cde83, null, 2015-06-08 10:17:24.0, 04150608000001, 12, 36ff7818c242400db397075c87721db7, fb4db237da604688aeb1f369b5bfd45e, ad532d4b21184280bd0faec9685d1129, null, null, null, 32.08133,112.138352, null, null, 0, null, 0, null, null, null, 0, null, null, null, null, null, 0]
10:21:38.139 [http-apr-8080-exec-6] DEBUG o.s.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
10:21:38.139 [http-apr-8080-exec-6] DEBUG java.sql.Connection - {conn-100788} Connection
10:21:38.139 [http-apr-8080-exec-6] DEBUG java.sql.Connection - {conn-100788} Preparing Statement:    SELECT * FROM refundorders WHERE orderid=?
10:21:38.139 [http-apr-8080-exec-6] DEBUG o.s.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
10:21:38.142 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100789} Executing Statement:    SELECT * FROM refundorders WHERE orderid=?
10:21:38.142 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100789} Parameters: [930aebeae7c845c88bb15f985f3cde83]
10:21:38.143 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100789} Types: [java.lang.String]
10:21:38.147 [http-apr-8080-exec-6] DEBUG java.sql.ResultSet - {rset-100790} ResultSet
10:21:38.148 [http-apr-8080-exec-6] DEBUG java.sql.ResultSet - {rset-100790} Header: [id, orderid, applymoney, applytime, applyreason, applymemo, replytime, replyreason, replymemo, cancelflag, canceltime, reqthirdtime, arbitratetime, arbitrateon, arbitratememo]
10:21:38.148 [http-apr-8080-exec-6] DEBUG java.sql.ResultSet - {rset-100790} Result: [614cee2768e24f7d82a5f90d6f7a58bd, 930aebeae7c845c88bb15f985f3cde83, null, 2015-06-08 10:17:54.0, 3, , null, null, null, null, null, null, null, null, null]
10:21:38.148 [http-apr-8080-exec-6] DEBUG o.s.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
10:21:38.148 [http-apr-8080-exec-6] DEBUG o.s.j.datasource.DataSourceTransactionManager - Creating new transaction with name [com.incito.logistics.service.OrderService.agreeRefund]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; '',-java.lang.Exception
10:21:38.152 [http-apr-8080-exec-6] DEBUG o.s.j.datasource.DataSourceTransactionManager - Acquired Connection [jdbc:mysql://192.168.11.140:3306/smartdbdev?autoReconnect=true&useUnicode=true&characterEncoding=utf8, [email protected], MySQL Connector Java] for JDBC transaction
10:21:38.155 [http-apr-8080-exec-6] DEBUG o.s.j.datasource.DataSourceTransactionManager - Switching JDBC Connection [jdbc:mysql://192.168.11.140:3306/smartdbdev?autoReconnect=true&useUnicode=true&characterEncoding=utf8, [email protected], MySQL Connector Java] to manual commit
10:21:38.157 [http-apr-8080-exec-6] DEBUG java.sql.Connection - {conn-100791} Connection
10:21:38.158 [http-apr-8080-exec-6] DEBUG java.sql.Connection - {conn-100791} Preparing Statement:      UPDATE     goods set completedate = null                               where id = ?
10:21:38.158 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100792} Executing Statement:      UPDATE     goods set completedate = null                               where id = ?
10:21:38.158 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100792} Parameters: [ad532d4b21184280bd0faec9685d1129]
10:21:38.158 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100792} Types: [java.lang.String]
10:21:38.162 [http-apr-8080-exec-6] DEBUG java.sql.Connection - {conn-100793} Connection
10:21:38.163 [http-apr-8080-exec-6] DEBUG java.sql.Connection - {conn-100793} Preparing Statement:      UPDATE     orders     SET        status = ?                                                        , completetime = now()                                                                                                                           WHERE     id = ?
10:21:38.163 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100794} Executing Statement:      UPDATE     orders     SET        status = ?                                                        , completetime = now()                                                                                                                           WHERE     id = ?
10:21:38.163 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100794} Parameters: [16, 930aebeae7c845c88bb15f985f3cde83]
10:21:38.163 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100794} Types: [java.lang.String, java.lang.String]
10:21:38.167 [http-apr-8080-exec-6] DEBUG java.sql.Connection - {conn-100795} Connection
10:21:38.168 [http-apr-8080-exec-6] DEBUG java.sql.Connection - {conn-100795} Preparing Statement:    UPDATE refundorders   SET     replytime = now()                                               WHERE id = ?
10:21:38.168 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100796} Executing Statement:    UPDATE refundorders   SET     replytime = now()                                               WHERE id = ?
10:21:38.168 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100796} Parameters: [614cee2768e24f7d82a5f90d6f7a58bd]
10:21:38.168 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100796} Types: [java.lang.String]
10:21:38.180 [http-apr-8080-exec-6] DEBUG java.sql.Connection - {conn-100797} Connection
10:21:38.180 [http-apr-8080-exec-6] DEBUG java.sql.Connection - {conn-100797} Preparing Statement:       SELECT count(1) FROM orders where carid = ? AND status not in (2,7,8,9,10,11,14)
10:21:38.180 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100798} Executing Statement:       SELECT count(1) FROM orders where carid = ? AND status not in (2,7,8,9,10,11,14)
10:21:38.180 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100798} Parameters: [fb4db237da604688aeb1f369b5bfd45e]
10:21:38.180 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100798} Types: [java.lang.String]
10:21:38.185 [http-apr-8080-exec-6] DEBUG java.sql.ResultSet - {rset-100799} ResultSet
10:21:38.185 [http-apr-8080-exec-6] DEBUG java.sql.ResultSet - {rset-100799} Header: [count(1)]
10:21:38.185 [http-apr-8080-exec-6] DEBUG java.sql.ResultSet - {rset-100799} Result: [53]
10:21:38.186 [http-apr-8080-exec-6] DEBUG java.sql.Connection - {conn-100800} Connection
10:21:38.186 [http-apr-8080-exec-6] DEBUG java.sql.Connection - {conn-100800} Preparing Statement:      update car     SET                                                         carstatus = ?                                                                                                     where id = ?
10:21:38.187 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100801} Executing Statement:      update car     SET                                                         carstatus = ?                                                                                                     where id = ?
10:21:38.187 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100801} Parameters: [1, fb4db237da604688aeb1f369b5bfd45e]
10:21:38.187 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100801} Types: [java.lang.Integer, java.lang.String]
10:21:38.191 [http-apr-8080-exec-6] INFO  com.incito.logistics.service.OrderService - 調用銀聯接口退款 orderId: 930aebeae7c845c88bb15f985f3cde83
10:21:38.191 [http-apr-8080-exec-6] DEBUG o.s.j.datasource.DataSourceTransactionManager - Participating in existing transaction
10:21:38.192 [http-apr-8080-exec-6] DEBUG java.sql.Connection - {conn-100802} Connection
10:21:38.192 [http-apr-8080-exec-6] DEBUG java.sql.Connection - {conn-100802} Preparing Statement:      SELECT * FROM ums_paymentorder WHERE BizId=?
10:21:38.192 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100803} Executing Statement:      SELECT * FROM ums_paymentorder WHERE BizId=?
10:21:38.192 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100803} Parameters: [930aebeae7c845c88bb15f985f3cde83]
10:21:38.192 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100803} Types: [java.lang.String]
10:21:38.197 [http-apr-8080-exec-6] DEBUG java.sql.ResultSet - {rset-100804} ResultSet
10:21:38.199 [http-apr-8080-exec-6] DEBUG java.sql.ResultSet - {rset-100804} Header: [OrderNoMer, SrcReqId, SrcReqDate, TxnType, OrderNo, PyrUserIdMer, PyrUserType, PyeUserIdMer, PyeUserType, PyrUserId, PyeUserId, OrderAmt, OrderStatus, STATUS, StatusDesc, BizId, BizType, BizDesc, PaymentChannel, Priority, HashCode, GenerateTime, UpdateTime, OrgBizId, DrawStatus, DrawTime]
10:21:38.199 [http-apr-8080-exec-6] DEBUG java.sql.ResultSet - {rset-100804} Result: [DE15060800000000000001, null, 2015-06-08 10:17:48.0, 01, 201506081017323258568, fb4db237da604688aeb1f369b5bfd45e, 02, 36ff7818c242400db397075c87721db7, 01, null, null, 23500, 03, 00, Success!, 930aebeae7c845c88bb15f985f3cde83, 02, 付信息費-礦產, 02, 5, null, 2015-06-08 10:17:28.0, 2015-06-08 10:17:48.0, null, 0, null]
10:21:38.199 [http-apr-8080-exec-6] DEBUG java.sql.Connection - {conn-100805} Connection
10:21:38.199 [http-apr-8080-exec-6] DEBUG java.sql.Connection - {conn-100805} Preparing Statement:      SELECT * FROM ums_paymentorder WHERE BizId=?
10:21:38.199 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100806} Executing Statement:      SELECT * FROM ums_paymentorder WHERE BizId=?
10:21:38.199 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100806} Parameters: [DE15060800000000000001]
10:21:38.199 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100806} Types: [java.lang.String]
10:21:38.208 [http-apr-8080-exec-6] DEBUG java.sql.ResultSet - {rset-100807} ResultSet
10:21:38.209 [http-apr-8080-exec-6] DEBUG o.s.j.datasource.DataSourceTransactionManager - Suspending current transaction
10:21:38.209 [http-apr-8080-exec-6] DEBUG o.s.j.datasource.DataSourceTransactionManager - Creating new transaction with name [com.incito.logistics.service.SeqService.getNo]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
10:21:38.214 [http-apr-8080-exec-6] DEBUG o.s.j.datasource.DataSourceTransactionManager - Acquired Connection [jdbc:mysql://192.168.11.140:3306/smartdbdev?autoReconnect=true&useUnicode=true&characterEncoding=utf8, [email protected], MySQL Connector Java] for JDBC transaction
10:21:38.218 [http-apr-8080-exec-6] DEBUG o.s.j.datasource.DataSourceTransactionManager - Switching JDBC Connection [jdbc:mysql://192.168.11.140:3306/smartdbdev?autoReconnect=true&useUnicode=true&characterEncoding=utf8, [email protected], MySQL Connector Java] to manual commit
10:21:38.220 [http-apr-8080-exec-6] DEBUG java.sql.Connection - {conn-100808} Connection
10:21:38.220 [http-apr-8080-exec-6] DEBUG java.sql.Connection - {conn-100808} Preparing Statement:       SELECT CONCAT(?,DATE_FORMAT(NOW(), ?),LPAD(nextvalue(?),?,0)) AS ordernum;
10:21:38.220 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100809} Executing Statement:       SELECT CONCAT(?,DATE_FORMAT(NOW(), ?),LPAD(nextvalue(?),?,0)) AS ordernum;
10:21:38.220 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100809} Parameters: [CP, %y%m%d, chinapayorder, 14]
10:21:38.221 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100809} Types: [java.lang.String, java.lang.String, java.lang.String, java.lang.Integer]
10:21:38.228 [http-apr-8080-exec-6] DEBUG java.sql.ResultSet - {rset-100810} ResultSet
10:21:38.229 [http-apr-8080-exec-6] DEBUG java.sql.ResultSet - {rset-100810} Header: [ordernum]
10:21:38.229 [http-apr-8080-exec-6] DEBUG java.sql.ResultSet - {rset-100810} Result: [CP15060800000000000003]
10:21:38.229 [http-apr-8080-exec-6] DEBUG o.s.j.datasource.DataSourceTransactionManager - Initiating transaction commit
10:21:38.231 [http-apr-8080-exec-6] DEBUG o.s.j.datasource.DataSourceTransactionManager - Committing JDBC transaction on Connection [jdbc:mysql://192.168.11.140:3306/smartdbdev?autoReconnect=true&useUnicode=true&characterEncoding=utf8, UserName=root@172.16.0.3, MySQL Connector Java]
10:21:38.238 [http-apr-8080-exec-6] DEBUG o.s.j.datasource.DataSourceTransactionManager - Releasing JDBC Connection [jdbc:mysql://192.168.11.140:3306/smartdbdev?autoReconnect=true&useUnicode=true&characterEncoding=utf8, UserName=root@172.16.0.3, MySQL Connector Java] after transaction
10:21:38.238 [http-apr-8080-exec-6] DEBUG o.s.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
10:21:38.238 [http-apr-8080-exec-6] DEBUG o.s.j.datasource.DataSourceTransactionManager - Resuming suspended transaction after completion of inner transaction
10:21:38.238 [http-apr-8080-exec-6] DEBUG o.s.j.datasource.DataSourceTransactionManager - Resuming suspended transaction after completion of inner transaction
10:21:38.239 [http-apr-8080-exec-6] DEBUG java.sql.Connection - {conn-100811} Connection
10:21:38.239 [http-apr-8080-exec-6] DEBUG java.sql.Connection - {conn-100811} Preparing Statement:      insert  into ums_paymentorder (       SrcReqId,       OrderNoMer,       OrderNo,       SrcReqDate,       TxnType,       PyrUserIdMer,       PyeUserIdMer,       PyrUserType,       PyeUserType,       PyrUserId,       PyeUserId,       OrderAmt,       STATUS,       StatusDesc,       BizId,       BizType,       BizDesc,       OrgBizId,       PaymentChannel,       OrderStatus,       Priority,       HashCode,       GenerateTime,       UpdateTime     )     value (       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       now(),now()     )
10:21:38.240 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100812} Executing Statement:      insert  into ums_paymentorder (       SrcReqId,       OrderNoMer,       OrderNo,       SrcReqDate,       TxnType,       PyrUserIdMer,       PyeUserIdMer,       PyrUserType,       PyeUserType,       PyrUserId,       PyeUserId,       OrderAmt,       STATUS,       StatusDesc,       BizId,       BizType,       BizDesc,       OrgBizId,       PaymentChannel,       OrderStatus,       Priority,       HashCode,       GenerateTime,       UpdateTime     )     value (       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       ?,       now(),now()     )
10:21:38.240 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100812} Parameters: [null, CP15060800000000000003, null, 2015-06-08 10:21:38.238, 04, 36ff7818c242400db397075c87721db7, fb4db237da604688aeb1f369b5bfd45e, 01, 02, null, null, 23500, null, 已發送, DE15060800000000000001, 04, 退信息費DE15060800000000000001, 930aebeae7c845c88bb15f985f3cde83, 02, 01, 5, null]
10:21:38.240 [http-apr-8080-exec-6] DEBUG java.sql.PreparedStatement - {pstm-100812} Types: [null, java.lang.String, null, java.sql.Timestamp, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, null, null, java.lang.Long, null, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Integer, null]
10:21:38.245 [http-apr-8080-exec-6] INFO  c.i.logistics.chinapay.ChinaPayOrderService - 創建退貨訂單 success, refundOrder: PaymentOrder{OrderNoMer='CP15060800000000000003', SrcReqId='null', SrcReqDate=Mon Jun 08 10:21:38 CST 2015, OrderNo='null', TxnType='04', OrderAmt=23500, OrderStatus='01', PyrUserIdMer='36ff7818c242400db397075c87721db7', PyeUserIdMer='fb4db237da604688aeb1f369b5bfd45e', PyrUserType='01', PyeUserType='02', PyrUserId='null', PyeUserId='null', Status='null', StatusDesc='已發送', BizId='DE15060800000000000001', BizType='04', BizDesc='退信息費DE15060800000000000001', Priority=5, HashCode='null', GenerateTime=null, UpdateTime=null, PaymentChannel='02'}
10:21:38.245 [http-apr-8080-exec-6] DEBUG c.i.logistics.chinapay.utils.ChinaPayUtil - txnType-->04
10:21:38.245 [http-apr-8080-exec-6] DEBUG c.i.logistics.chinapay.utils.ChinaPayUtil - frontUrl-->http://192.168.81.24:8080/web/chinapay/callback
10:21:38.245 [http-apr-8080-exec-6] DEBUG c.i.logistics.chinapay.utils.ChinaPayUtil - channelType-->08
10:21:38.245 [http-apr-8080-exec-6] DEBUG c.i.logistics.chinapay.utils.ChinaPayUtil - currencyCode-->156
10:21:38.245 [http-apr-8080-exec-6] DEBUG c.i.logistics.chinapay.utils.ChinaPayUtil - merId-->777290058111285
10:21:38.245 [http-apr-8080-exec-6] DEBUG c.i.logistics.chinapay.utils.ChinaPayUtil - txnSubType-->00
10:21:38.245 [http-apr-8080-exec-6] DEBUG c.i.logistics.chinapay.utils.ChinaPayUtil - txnAmt-->23500
10:21:38.245 [http-apr-8080-exec-6] DEBUG c.i.logistics.chinapay.utils.ChinaPayUtil - version-->5.0.0
10:21:38.246 [http-apr-8080-exec-6] DEBUG c.i.logistics.chinapay.utils.ChinaPayUtil - signMethod-->01
10:21:38.246 [http-apr-8080-exec-6] DEBUG c.i.logistics.chinapay.utils.ChinaPayUtil - backUrl-->http://58.51.36.100:60001/chinapay/callback
10:21:38.246 [http-apr-8080-exec-6] DEBUG c.i.logistics.chinapay.utils.ChinaPayUtil - origQryId-->201506081017323258568
10:21:38.246 [http-apr-8080-exec-6] DEBUG c.i.logistics.chinapay.utils.ChinaPayUtil - encoding-->UTF-8
10:21:38.246 [http-apr-8080-exec-6] DEBUG c.i.logistics.chinapay.utils.ChinaPayUtil - bizType-->000201
10:21:38.246 [http-apr-8080-exec-6] DEBUG c.i.logistics.chinapay.utils.ChinaPayUtil - orderId-->CP15060800000000000003
10:21:38.246 [http-apr-8080-exec-6] DEBUG c.i.logistics.chinapay.utils.ChinaPayUtil - txnTime-->20150608102138
10:21:38.246 [http-apr-8080-exec-6] DEBUG c.i.logistics.chinapay.utils.ChinaPayUtil - accessType-->0
......
    at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:1852) [tomcat-coyote.jar:7.0.42]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [na:1.7.0_40]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [na:1.7.0_40]
    at java.lang.Thread.run(Thread.java:724) [na:1.7.0_40]
10:21:38.502 [http-apr-8080-exec-6] DEBUG o.s.j.datasource.DataSourceTransactionManager - Participating transaction failed - marking existing transaction as rollback-only
10:21:38.504 [http-apr-8080-exec-6] DEBUG o.s.j.datasource.DataSourceTransactionManager - Setting JDBC transaction [jdbc:mysql://192.168.11.140:3306/smartdbdev?autoReconnect=true&useUnicode=true&characterEncoding=utf8, UserName=root@172.16.0.3, MySQL Connector Java] rollback-only
10:21:38.505 [http-apr-8080-exec-6] DEBUG o.s.j.datasource.DataSourceTransactionManager - Initiating transaction rollback
10:21:38.510 [http-apr-8080-exec-6] DEBUG o.s.j.datasource.DataSourceTransactionManager - Rolling back JDBC transaction on Connection [jdbc:mysql://192.168.11.140:3306/smartdbdev?autoReconnect=true&useUnicode=true&characterEncoding=utf8, UserName=root@172.16.0.3, MySQL Connector Java]
10:21:38.523 [http-apr-8080-exec-6] DEBUG o.s.j.datasource.DataSourceTransactionManager - Releasing JDBC Connection [jdbc:mysql://192.168.11.140:3306/smartdbdev?autoReconnect=true&useUnicode=true&characterEncoding=utf8, UserName=root@172.16.0.3, MySQL Connector Java] after transaction
10:21:38.523 [http-apr-8080-exec-6] DEBUG o.s.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
Caused by: java.lang.RuntimeException: test db exception ...
    at com.incito.logistics.service.ChinaPayOrderService.refund(ChinaPayOrderService.java:700) ~[logistic-server-0.0.1-SNAPSHOT.jar:na]
    ... 69 common frames omitted
10:21:38.530 [http-apr-8080-exec-6] DEBUG com.incito.logistics.common.SpecialCharFilter - process out /web/myorder/agreeRefund

可以看到,不管方法的註解是什麼,select語句並不會打開事務。只有insert\update出現時,才Creating new transaction。之後調用其它service方法時都是參與到當前的事務中Participating in existing transaction。最後commit或rollback。
(NOT_SUPPORTED與REQUIRES_NEW的日誌被提取了出去,後面會看到,而他們之外的事務處理流程都一樣)
DEBUG o.s.j.datasource.DataSourceTransactionManager - Creating new transaction with name
DEBUG o.s.j.datasource.DataSourceTransactionManager - Acquired Connection [jdbc:mysql://。。。
DEBUG o.s.j.datasource.DataSourceTransactionManager - Switching JDBC Connection [jdbc:mysql://。。。]to manual commit
。。。
DEBUG o.s.j.datasource.DataSourceTransactionManager - Participating in existing transaction
。。。
DEBUG o.s.j.datasource.DataSourceTransactionManager - Participating transaction failed - marking existing transaction as rollback-only
DEBUG o.s.j.datasource.DataSourceTransactionManager - Setting JDBC transaction [jdbc:mysql://192.168。。。
DEBUG o.s.j.datasource.DataSourceTransactionManager - Initiating transaction rollback
DEBUG o.s.j.datasource.DataSourceTransactionManager - Rolling back JDBC transaction on Connection [jdbc:mysql://19。。。
DEBUG o.s.j.datasource.DataSourceTransactionManager - Releasing JDBC Connection [jdbc:mysql://192.1。。。
DEBUG o.s.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource

如果其中的一個方法中使用了PROPAGATION_NOT_SUPPORTED,那麼上面的流程中會出現下面日誌,進入方法前先掛起當前事務且創建新事務,在NOT_SUPPORTED方法範圍內,每執行一次就要commit一次,退出方法時提交新事務且恢復被掛起的事務。從日誌中可看出NOT_SUPPORTED的行爲。如下:執行了兩次查詢SELECT orderno,每次執行完後都commit了。
DEBUG o.s.j.datasource.DataSourceTransactionManager - Suspending current transaction
DEBUG o.s.j.datasource.DataSourceTransactionManager - Creating new transaction with name [com.incito.logistics.service.SeqSer
DEBUG o.s.j.datasource.DataSourceTransactionManager - Acquired Connection [jdbc:mysql://192.168.11.140:3306/smartdbdev?autoRe
DEBUG o.s.j.datasource.DataSourceTransactionManager - Switching JDBC Connection [jdbc:mysql://192.168.11.140:3306/smartdbdev?
DEBUG java.sql.Connection - {conn-100158} Connection
DEBUG java.sql.Connection - {conn-100158} Preparing Statement: SELECT CONCAT(?,DATE_FORMAT(NOW(), ?),LPAD(nextvalue(?),
DEBUG java.sql.PreparedStatement - {pstm-100159} Executing Statement: SELECT CONCAT(?,DATE_FORMAT(NOW(), ?),LPAD(nextva
DEBUG java.sql.PreparedStatement - {pstm-100159} Parameters: [CP, %y%m%d, chinapayorder, 14]
DEBUG java.sql.PreparedStatement - {pstm-100159} Types: [java.lang.String, java.lang.String, java.lang.String, java.lang.Inte
DEBUG java.sql.ResultSet - {rset-100160} ResultSet
DEBUG java.sql.ResultSet - {rset-100160} Header: [ordernum]
DEBUG java.sql.ResultSet - {rset-100160} Result: [CP15060800000000000006]
DEBUG o.s.j.datasource.DataSourceTransactionManager - Initiating transaction commit
DEBUG o.s.j.datasource.DataSourceTransactionManager - Committing JDBC transaction on Connection [jdbc:mysql://192.168.11.140:
DEBUG o.s.j.datasource.DataSourceTransactionManager - Releasing JDBC Connection [jdbc:mysql://192.168.11.140:3306/smartdbdev?
DEBUG o.s.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
DEBUG o.s.j.datasource.DataSourceTransactionManager - Resuming suspended transaction after completion of inner transaction
DEBUG o.s.j.datasource.DataSourceTransactionManager - Creating new transaction with name [com.incito.logistics.service.SeqSer
DEBUG o.s.j.datasource.DataSourceTransactionManager - Acquired Connection [jdbc:mysql://192.168.11.140:3306/smartdbdev?autoRe
DEBUG o.s.j.datasource.DataSourceTransactionManager - Switching JDBC Connection [jdbc:mysql://192.168.11.140:3306/smartdbdev?
DEBUG java.sql.Connection - {conn-100161} Connection
DEBUG java.sql.Connection - {conn-100161} Preparing Statement: SELECT CONCAT(?,DATE_FORMAT(NOW(), ?),LPAD(nextvalue(?),
DEBUG java.sql.PreparedStatement - {pstm-100162} Executing Statement: SELECT CONCAT(?,DATE_FORMAT(NOW(), ?),LPAD(nextva
DEBUG java.sql.PreparedStatement - {pstm-100162} Parameters: [CP, %y%m%d, chinapayorder, 14]
DEBUG java.sql.PreparedStatement - {pstm-100162} Types: [java.lang.String, java.lang.String, java.lang.String, java.lang.Inte
DEBUG java.sql.ResultSet - {rset-100163} ResultSet
DEBUG java.sql.ResultSet - {rset-100163} Header: [ordernum]
DEBUG java.sql.ResultSet - {rset-100163} Result: [CP15060800000000000007]
DEBUG o.s.j.datasource.DataSourceTransactionManager - Initiating transaction commit
DEBUG o.s.j.datasource.DataSourceTransactionManager - Committing JDBC transaction on Connection [jdbc:mysql://192.168.11.140:
DEBUG o.s.j.datasource.DataSourceTransactionManager - Releasing JDBC Connection [jdbc:mysql://192.168.11.140:3306/smartdbdev?
DEBUG o.s.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
DEBUG o.s.j.datasource.DataSourceTransactionManager - Resuming suspended transaction after completion of inner transaction
DEBUG o.s.j.datasource.DataSourceTransactionManager - Resuming suspended transaction after completion of inner transaction

如果其中的一個方法中使用了PROPAGATION_REQUIRES_NEW,與NOT_SUPPORTED相比只有一點區別,就是REQUIRES_NEW只commit了一次。之後處理步驟就相同了Resuming suspended transaction。。。

DEBUG o.s.j.datasource.DataSourceTransactionManager - Suspending current transaction, creating new transaction with name [co
DEBUG o.s.j.datasource.DataSourceTransactionManager - Acquired Connection [jdbc:mysql://192.168.11.140:3306/smartdbdev?autoR
DEBUG o.s.j.datasource.DataSourceTransactionManager - Switching JDBC Connection [jdbc:mysql://192.168.11.140:3306/smartdbdev
DEBUG o.s.j.datasource.DataSourceTransactionManager - Participating in existing transaction
DEBUG java.sql.Connection - {conn-100195} Connection
DEBUG java.sql.Connection - {conn-100195} Preparing Statement: SELECT CONCAT(?,DATE_FORMAT(NOW(), ?),LPAD(nextvalue(?)
DEBUG java.sql.PreparedStatement - {pstm-100196} Executing Statement: SELECT CONCAT(?,DATE_FORMAT(NOW(), ?),LPAD(nextv
DEBUG java.sql.PreparedStatement - {pstm-100196} Parameters: [CP, %y%m%d, chinapayorder, 14]
DEBUG java.sql.PreparedStatement - {pstm-100196} Types: [java.lang.String, java.lang.String, java.lang.String, java.lang.Int
DEBUG java.sql.ResultSet - {rset-100197} ResultSet
DEBUG java.sql.ResultSet - {rset-100197} Header: [ordernum]
DEBUG java.sql.ResultSet - {rset-100197} Result: [CP15060800000000000010]
DEBUG o.s.j.datasource.DataSourceTransactionManager - Participating in existing transaction
DEBUG java.sql.Connection - {conn-100198} Connection
DEBUG java.sql.Connection - {conn-100198} Preparing Statement: SELECT CONCAT(?,DATE_FORMAT(NOW(), ?),LPAD(nextvalue(?)
DEBUG java.sql.PreparedStatement - {pstm-100199} Executing Statement: SELECT CONCAT(?,DATE_FORMAT(NOW(), ?),LPAD(nextv
DEBUG java.sql.PreparedStatement - {pstm-100199} Parameters: [CP, %y%m%d, chinapayorder, 14]
DEBUG java.sql.PreparedStatement - {pstm-100199} Types: [java.lang.String, java.lang.String, java.lang.String, java.lang.Int
DEBUG java.sql.ResultSet - {rset-100200} ResultSet
DEBUG java.sql.ResultSet - {rset-100200} Header: [ordernum]
DEBUG java.sql.ResultSet - {rset-100200} Result: [CP15060800000000000011]
DEBUG o.s.j.datasource.DataSourceTransactionManager - Initiating transaction commit
DEBUG o.s.j.datasource.DataSourceTransactionManager - Committing JDBC transaction on Connection [jdbc:mysql://192.168.11.140
DEBUG o.s.j.datasource.DataSourceTransactionManager - Releasing JDBC Connection [jdbc:mysql://192.168.11.140:3306/smartdbdev
DEBUG o.s.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
DEBUG o.s.j.datasource.DataSourceTransactionManager - Resuming suspended transaction after completion of inner transaction

==============================================================================================
測試時,遇到了另外一種現象:
假設在同一個XXXService中,有下面兩個方法:
@Transactional
method_One(){…method_Two()…}

@Transactional(propagation = Propagation.REQUIRES_NEW)
method_Two(){…}

問method_Two()會不會創建一個新事務?
答:不會創建。仔細查看了日誌,沒有找到類似creating new transaction的輸出,應該是因爲在同一個Service類中,spring並不重新創建新事務,如果是兩不同的Service,就會創建新事務了。
那麼爲什麼spring只對跨Service的方法才生效?
Debug代碼發現跨Service調用方法時,都會經過org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor.intercept()方法,只有經過此處,才能對事務進行控制。
如果你要問此方法又是如何控制事務的?下次有機會再研究。。。嘿嘿

==============================================================================================

DEBUG o.s.j.datasource.DataSourceTransactionManager - Global transaction is marked as rollback-only but transactional code requested commit
DEBUG o.s.j.datasource.DataSourceTransactionManager - Initiating transaction rollback
DEBUG o.s.j.datasource.DataSourceTransactionManager - Rolling back JDBC transaction on Connection [jdbc:mysql://。。。, MySQL Connector Java]
DEBUG o.s.j.datasource.DataSourceTransactionManager - Releasing JDBC Connection [jdbc:mysql://。。。,  ] after transaction
DEBUG o.s.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

Propagation.REQUIRES_NEW 與 Propagation.REQUIRED 比較
method_XX(){
      //創建退貨訂單
      try {
        refundOrder.setOrderNoMer(seqService.nextOrderNo());//跨Service調用方法時
      } catch (Exception e) {//catch後不拋出
        refundOrder.setOrderNoMer("11111111111111112");//商戶端訂單號
        logger.info("catch後不拋出", e);
      }
}

跨Service調用方法時:
**如果被調用方法是Propagation.REQUIRES_NEW,被catch後不拋出,事務可以正常提交;
如果被調用方法是Propagation.REQUIRED,被catch後不拋出,後面的代碼雖然可以執行下去,但最終還是會分出rollback-only異常:**Global transaction is marked as rollback-only but transactional code requested commit
如果是在同一個Service中調用方法時:
不論註解是REQUIRES_NEW還是REQUIRED,其結果都是一樣的,就是都被忽略掉了,等於沒寫。當其拋出異常時,只需catch住不拋出,事務就可以正常提交。

==============================================================================================

發佈了36 篇原創文章 · 獲贊 12 · 訪問量 42萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章