一、前言
今天在做一些高並發的簡單測試時(主要測試悲觀鎖、樂觀鎖、重入機制等等的效率,加深對鎖的理解),報了一個莫名其妙的空指針錯誤:
錯誤原因指向一個業務實現類:
觀察到,第62行只是一個簡單的判斷語句:
if(redPacket.getStock() > 0)
該判斷句所引對象來自61行代碼:
RedPacket redPacket = redPacketDAO.getRedPacket(redPacketId);
我發現,如果是空指針錯誤的話,那應該是對象redPacket為空,也就是說61行代碼執行失敗了。問題來了:既然61行執行失敗,干嘛要報62行的錯?果然,在我注釋掉62行及以后的代碼,print打印redPacket對象時,發現該對象本身及其所有屬性均能正常打印(自動該類調用自定義的toString()方法),並沒有報錯。
哈,到這里真的有點摸不着頭腦了,對象和他的屬性均正常,set.get方法也無誤,一個線程它正兒八經,正正常常的一行一行執行着,前一句不空,后一句突然變空,這…………
困難是有的,但還是可以解決的!抱着試一試的心態,先把62行外層if語句里所有內容注釋掉,嘗試性又執行一遍,額,空指針沒了。
以上就是發現這個奇葩空指針源頭的過程,再之后就簡單了,一句句排查,鎖定了錯誤的源頭在Mybatis的這個方法中:
我注意到,sql語句中,參數有兩個:id和version,我卻還當作一個參數來處理:沒有定義parameterType。粗心的毛病啊,,,,當時這段是直接復制悲觀鎖的代碼,根本沒注意到多加了一個參數。
二、Mybatis如何傳遞多個參數
四種方法噢,咱由淺入深,先從最不常用的開始,哈哈。
(1)采用Map傳多參數
Dao層:
public int selectUser(Map paramMap);
Mapper.xml:
<update id="selectUser"> update table_name set xxx where id = #{id} and version = #{version} </update>
Service層:
Private User x(){ Map paramMap=new hashMap(); paramMap.put(“id”,”2”); paramMap.put(“version”,”1”); User user=xxx. selectUser(paramMap); }
(2)使用#{arg0},#{arg1}...或#{param1},#{param2}
細心的小伙伴遇到類似錯誤時可能會發現錯誤日志里有類似的提醒:
Parameter ‘id’ not found. Available parameters are [arg1, arg0, param1, param2]
沒錯,就是把sql代碼中#{id}和#{version}改為#{arg0}或者#{param1}等等:(注意:arg下標從0開始,param下標從1開始)
Dao層:
public int selectUser(Long id, int version);
Mapper.xml:
<update id="selectUser"> update table_name set xxx where id = #{arg0} and version = #{arg1} </update>
(3)定義一個實體類來承載多個參數
pojo(實體類):
public class Param(){
private Long id; private int version; -------getter and setter------- }
DAO層:
public int selectUser(Param p);
Mapper.xml:同(1)
(4)使用@Param注解
DAO層:
public int selectUser(@Param("id") Long id, @Param("version") int version);
Mapper.xml同(1)
這四種方法中,個人認為第四種方法最好,使用@Param注解能讓開發者看到dao層方法就知道該傳什么樣的參數,比較直觀。
選擇上述四種方法任意一種,問題解決。
三、結語
與其說問題已經解決,不如說只是代碼不報錯了而已。留給我的問題是:為嘛參數傳遞格式有問題,會報空指針錯誤?為嘛對象正常,只有調用屬性的getter方法才會報錯?仍在思考,希望路過的大神指導一二。