Introduction:
Recently in the project to participate in a coupon collection activities, when multiple users to receive the same coupon, the use of database lock control concurrency, the initial assumption is: if more than one person to receive a coupon, the first person to arrive at the successful, others continue to find out whether there are remaining coupons, if any, continue to collect, Otherwise, the collection fails. In the implementation, I began to use a recursive way to find coupons, the actual test found infinite recursion, through the DEGUG and access to data only found that this is due to the MyBatis cache, the following will be the problem and share the discussion with you.
1. Knowledge Reserves
Brief introduction:
Mybatis
First level cache: Default on, sqlsession level cache, active in current session, Sqlsession commit (), close (), ClearCache () will clear cache after execution.
Second level cache: Requires manual open, global level caching, related to mapper namespace.
For details, see: http://www.mamicode.com/info-detail-890951.html
2. Code examples
Here is an auxiliary method for collecting coupons-randomly extracting a coupon code and invoking the public method of the helper method to open the transaction. During the actual testing process, it is found that infinite recursion occurs when there is only one coupon in the database and is collected by multiple users at the same time. The code is as follows:
1 /**2 * Randomly draw a coupon code3 * 4 * @paramCodeprefix5 * Coupon code prefix6 * @returnCoupon Code9 */Ten Privatestring Randextractoneticketcode (String mobile, String codeprefix) { OneList<string> notexchangecodelist =yzticketcodedaoext.getticketcodelist (Codeprefix, A mobileserviceconstants.ticket_code_status_not_exchange); -Logger.info ("Collect coupons >>> coupons available Quantity {}", Collectionutils.size (notexchangecodelist)); - if(Collectionutils.isempty (notexchangecodelist)) { theLogger.warn ("collect coupons >>> coupons {} has been completed", codeprefix); - Throw Newyzruntimeexception (mobileserviceconstants.ticket_not_remainder); - } - + intRandomindex = Random.nextint (Notexchangecodelist.size ());//Random Index -String Ticketcode = Notexchangecodelist.get (Randomindex);//a randomly selected coupon code +Yzticketcode ticketcodeobj =Yzticketcodedaoext.getbycode (ticketcode); A if(Ticketcodeobj = =NULL at|| Ticketcodeobj.getstatus ()! =Mobileserviceconstants.ticket_code_status_not_exchange) { - //if the coupon has been used -Logger.info ("Pick up coupon >>> coupon code {} does not exist or has been used", Ticketcode); - return Randextractoneticketcode (String mobile, string codeprefix); Recursive lookup - } - /* in * Update promo code status - */ to Ticketcodeobj.setexchangetime (Calendar.getinstance (). GetTime ()); + Ticketcodeobj.setstatus (mobileserviceconstants.ticket_code_status_had_exchanged); - ticketcodeobj.setmobile (mobile); the intupdatecnt =yzticketcodedaoext.update4receive (ticketcodeobj); * if(updatecnt <= 0){ $ //optimistic lock, does not affect the row, indicates that the update failed, may be that the coupon does not exist or has been usedPanax NotoginsengLogger.info ("Pick up coupon >>> coupon code {} does not exist or has been used", Ticketcode); - return Randextractoneticketcode (String mobile, string codeprefix); Recursive lookup the }; + returnTicketcode; A}
Debug found that the query results executed by line 11th are mybatis cached, so each time a coupon can be picked up, but in fact this coupon has been picked up by other users, resulting in infinite recursion.
3. Solution
1) Programmatic transactions, get sqlsession through TransactionManager, and then clear the first-level cache through the sqlsession ClearCache () method.
2) because the project uses spring-declared transactions, and the concurrency is not high, considering the reduction of complexity, the direct prompt user system is busy.
/*** Randomly draw a coupon code * *@paramcodeprefix * Promo code prefix *@returnCoupon Code *@throwsyzruntimeexception * If no coupon is available*/ Privatestring Randextractoneticketcode (String mobile, String codeprefix) {List<String> notexchangecodelist =yzticketcodedaoext.getticketcodelist (Codeprefix, Mobileserviceconstants.ticket_code_status_not_exchang E); Logger.info ("Pick up coupon >>> coupon available Quantity {}", Collectionutils.size (notexchangecodelist)); if(Collectionutils.isempty (notexchangecodelist)) {Logger.warn ("Collect coupons >>> coupons {} has been completed", Codeprefix); Throw Newyzruntimeexception (Mobileserviceconstants.ticket_not_remainder); } intRandomindex = Random.nextint (Notexchangecodelist.size ());//Random IndexString Ticketcode = Notexchangecodelist.get (Randomindex);//a randomly selected coupon codeYzticketcode ticketcodeobj =Yzticketcodedaoext.getbycode (Ticketcode); if(Ticketcodeobj = =NULL|| Ticketcodeobj.getstatus ()! =Mobileserviceconstants.ticket_code_status_not_exchange) { //if the coupon has been usedLogger.info ("Pick up coupon >>> coupon code {} does not exist or has been used", Ticketcode); Throw Newyzruntimeexception (mobileserviceconstants.ticket_system_busy); } /** Update promo code status*/Ticketcodeobj.setexchangetime (Calendar.getinstance (). GetTime ()); Ticketcodeobj.setstatus (mobileserviceconstants.ticket_code_status_had_exchanged); Ticketcodeobj.setmobile (mobile); intupdatecnt =yzticketcodedaoext.update4receive (ticketcodeobj); if(updatecnt <= 0){ //optimistic lock, does not affect the row, indicates that the update failed, may be that the coupon does not exist or has been usedLogger.info ("Pick up coupon >>> coupon code {} does not exist or has been used", Ticketcode); Throw Newyzruntimeexception (mobileserviceconstants.ticket_system_busy); }; returnTicketcode; }
Infinite recursion caused by mybatis level cache