問題1.Column 'username' in field list is ambiguous
### The error may involve com.fx.oa.module.per.leave.api.shared.domain.PositiveEntity.queryListForPage-Inline
### The error occurred while setting parameters
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'username' in field list is ambiguous
; SQL []; Column 'username' in field list is ambiguous; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolat
今天正式系統更新後,打開建立工作,頁面報出500錯誤。打開日志有如上錯誤。負責處理問題的工程師跟我說,可能是有同僚在測試系統資料和代碼裡都增加了一個名為userName的字段,但沒有在正式系統資料庫更新表結構導緻的。我拿到日志資訊,看到是ambiguous,這個單詞的意思是username這個字段是不明确、有歧義的。這種錯誤往往是多個表之間關聯查詢,而表中有相同字段名稱,未為該字段指定所屬表引起的。在建立工作頁面的底端,有查詢辦理曆史的表格。查詢曆史時會先将所有任務拿出,由于任務的審批人存儲的是使用者名,需要進一步關聯到使用者表取出使用者姓名,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<code>SELECT</code>
<code> </code><code>task.processExecutionId,</code>
<code> </code><code>process.</code><code>name</code> <code>as</code> <code>processDefineName,</code>
<code> </code><code>task.activityName,</code>
<code> </code><code>user</code><code>.userName </code><code>as</code> <code>createUserCode,</code>
<code> </code><code>task.status,</code>
<code> </code><code>task.description,</code>
<code> </code><code>task.finishTime,</code>
<code> </code><code>task.createTime</code>
<code> </code><code>FROM</code> <code>T_BPM_PROCESS_TASK task</code>
<code> </code><code>LEFT</code> <code>JOIN</code> <code>T_BPM_PROCESS_EXECUTION execution </code><code>ON</code> <code>task.processExecutionId = execution.id</code>
<code> </code><code>LEFT</code> <code>JOIN</code> <code>t_bpm_process_define process </code><code>ON</code> <code>execution.processDefineId = process.id</code>
<code> </code><code>LEFT</code> <code>JOIN</code> <code>T_SYS_USER </code><code>user</code> <code>ON</code> <code>task.createUserCode = </code><code>user</code><code>.userCode</code>
<code> </code><code>WHERE</code> <code>task.processExecutionId = #{id}</code>
<code> </code><code>ORDER</code> <code>BY</code> <code>task.createTime </code><code>DESC</code><code>, task.finishTime </code><code>DESC</code>
在這個版本上線裡,我們新上線了進階查詢的基于使用者的管理權限控制,負責的杜工在t_bpm_process_define中新增加了兩個字段,他仿照已經存在的roleCode、roleName,增加了userCode、userName兩個字段,而這兩個字段至少在檔案和使用者相關的表廣泛被使用。其中這兩個字段在我們的系統裡就可以被看做是保留字,其他表盡量不要使用。我建議将這兩個專用于進階查詢的字段加上字首,而不必修改sql。
表結構修改後該問題消失。
問題2.sql:截取中出現的問題
本期OA在正式系統上線後,與moss系統并行了一段時間。在這段時間裡oa的所有流程及表單均為測試,正式單據在moss裡填寫。是以我們在流程的顯示名稱在表單頁面均添加了“(測試)”字樣,而且頁面中凡包含“(測試)”字樣的表單,在填寫和審批時都會提示“該表單是測試表單,請到moss裡填寫”。3月12号是OA表單正式啟用,需要将所有的測試字樣從系統裡抹去。在測試字樣添加的時候,由各流程負責同僚在系統前端手動添加和儲存。如果再在系統頁面每張表單打開删除會非常麻煩。經過觀察,我發現所有流程的顯示名都包含(測試單據)或(測試字樣),且幾十個流程均未有在名稱中包含以上字樣。而在測試單據的html和template中,均包含<span style="color:#ff0000;">(測試表單)</span>,是以我寫了以下sql:
<code>update</code> <code>t_bpm_process_define </code><code>set</code> <code>name</code><code>=SUBSTR(</code><code>NAME</code> <code>FROM</code> <code>1 </code><code>FOR</code> <code>INSTR(</code><code>name</code><code>,</code><code>"(測試單據)"</code><code>)-1) </code><code>where</code> <code>INSTR(</code><code>name</code><code>,</code><code>"(測試單據)"</code><code>)!=0;</code>
<code>update</code> <code>t_bpm_form_define </code><code>set</code> <code>template=</code><code>REPLACE</code><code>(template, </code><code>'(測試單據)'</code><code>,</code><code>''</code><code>)</code><code>where</code> <code>INSTR(template,</code><code>'(測試單據)'</code><code>)!=0;</code>
<code>update</code> <code>t_bpm_form_define </code><code>set</code> <code>html=</code><code>REPLACE</code><code>(html, </code><code>'<span style="color:#ff0000;">(測試表單)</span>'</code><code>,</code><code>''</code><code>)</code><code>where</code> <code>INSTR(html,</code><code>'<span style="color:#ff0000;">(測試表單)</span>'</code><code>)!=0;</code>
執行後,表單倒是沒有問題,但第一句的執行出現了可怕的現象,很多流程名稱是空的。
第一句中我使用的是字元串的截取,第二、第三句都是替換部分字元串。我最初想可能跟這個有關,實際也确實有關,如果我也用replace函數是沒有問題的,原因很簡單,如果文本中不包含目标字元串肯定是不會替換的。但用字元串截取從邏輯上也講得通,為什麼會出現這麼可怕的結果呢?
原因就在于我們update沒有使用表内聯,sql在執行的時候根本沒有什麼資料去關聯它到底該修改哪條資料。
如果我們将第一條資料修改成這樣,就沒有問題了:
<code>update</code> <code>t_bpm_process_define a,(</code><code>select</code> <code>id,</code><code>name</code> <code>from</code> <code>t_bpm_proces_define </code><code>where</code> <code>INSTR(</code><code>name</code><code>,</code><code>"(測試單據)"</code><code>)!=0) )b </code><code>set</code> <code>a.</code><code>name</code><code>=SUBSTR(</code><code>NAME</code> <code>FROM</code> <code>1 </code><code>FOR</code> <code>INSTR(</code><code>name</code><code>,</code><code>"(測試單據)"</code><code>)-1) </code><code>where</code> <code>a.id = b.id;</code>
問題3.由于我們公司使用的郵件伺服器是Zimbra伺服器,由雲計算部門購買和維護。OA中的提醒郵件也是通過Zimbra往外發送。對于一些某時段的大批量郵件,比如逾期工作和考勤異常都是在某個時間點上由quartz觸發,同時會發出數百封以至上千封郵件。可能對成熟的商用郵件伺服器這點壓力算不上什麼,但對于Zimbra可能已經吃不消了。在雲計算和IT的同僚協助下,由我對郵件伺服器進行了測試,測試用例如下:
測試用例1:100封,總用時約:16min;實收97封,失敗3次,3次錯誤資訊均為:javax.mail.MessagingException: Could not connect to SMTP host
測試用例2:100封,總用時約:16min;實收100封,失敗2次,錯誤同上。加失敗重發機制,失敗後等待10s重發,最多重發3次;
測試用例3:每發一封,停留10s,總用時32min;實收100封,失敗1次,錯誤同上;重發機制同用例2.
我看了一下幾種解釋:1.網絡;2.防火牆;3.伺服器的自我保護,比如防止大批量發送時挂掉或者垃圾郵件,我覺得第三種解釋靠譜一些。
後來又做了一些其他壓力強度的測試,而且針對MessagingException的錯誤,一步步跟蹤代碼,也進行了相關分析
具體錯誤資訊如下:
javax.mail.MessagingException: Could not connect to SMTP host: mail.cn.phicomm.com, port: 25;
nested exception is:
java.net.SocketException: Software caused connection abort: connect
at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:1282)
at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:37
0)
at javax.mail.Service.connect(Service.java:297)
at javax.mail.Service.connect(Service.java:156)
at javax.mail.Service.connect(Service.java:105)
我通讀了一下代碼,發現是這樣的,首先會連接配接生成serverSocket:
<code>if</code> <code>(serverSocket != </code><code>null</code><code>)</code>
<code> </code><code>openServer(); </code><code>// only happens from connect(socket) else</code>
<code> </code><code>// openServer(host, port);</code>
<code> </code><code>else</code>
<code> </code><code>openServer(host, port);</code>
以下是openServer中的代碼:
<code>if</code> <code>(debug) </code>
<code> </code><code>out.println(</code><code>"DEBUG SMTP: got bad greeting from host \""</code> <code>+ </code>
<code> </code><code>server + </code><code>"\", port: "</code> <code>+ </code>
<code> </code><code>port + </code><code>", response: "</code> <code>+ </code>
<code> </code><code>r + </code><code>"\n"</code><code>); </code>
<code> </code><code>throw</code> <code>new</code> <code>MessagingException( </code><code>"Got bad greeting from SMTP host: "</code> <code>+ server + </code>
<code> </code><code>", port: "</code> <code>+ </code>
<code> </code><code>r); </code>
<code> </code><code>}</code>
而debug是一個全局變量,是session的一個屬性,其值
Since the debug setting can be turned on only after the Session has been created
也就是說session未建立就會報MessagingException這個錯誤。是以我進一步懷疑,我們的發送郵件代碼每次都會去重連認證,這個也是代碼的問題之一,是以我加了session失效才重連。
接下來又進行了一次測試,用例如下:
用例1.代碼修改如下
<code>if</code> <code>(session == </code><code>null</code> <code>|| session.getDebug()) {</code>
<code> </code><code>System.out.println(</code><code>"第"</code> <code>+ j + </code><code>"次重連"</code><code>);</code>
<code> </code><code>session = Session.getDefaultInstance(prop, auth);</code>
<code> </code><code>}</code>
結果:沒有失敗
用例2.仍用1的代碼,
結果:但失敗1次,失敗後未重新獲得session,可見失敗後session仍有效;
用例3.代碼修改如下:
<code> </code><code>// 如果需要身份驗證,建立一個賬号密碼驗證器</code>
<code> </code><code>if</code> <code>(mail.isValidate()) {</code>
<code> </code><code>auth = </code><code>new</code> <code>MailAuthenticator(mail.getUserName(),</code>
<code> </code><code>mail.getPassword());</code>
<code> </code><code>}</code>
<code> </code><code>System.out.println(</code><code>"第"</code> <code>+ j + </code><code>"次重連"</code><code>);</code>
<code> </code><code>session = Session.getDefaultInstance(prop, auth);</code>
結果:有1次失敗,失敗後未重連。
通過以上測試,是否每次重新獲得session對效率 沒有影響,對異常的發生也沒有影響。異常報出後,session仍有效,并且可以重發。
經過三輪測試,其實我們可以發現重發機制已經可以保證郵件的完全送達。
重發的代碼如下:
16
17
18
19
20
21
22
23
24
<code>/**</code>
<code> </code><code>* Temporary mechanism: automatic resend mail</code>
<code> </code><code>* @author chao.gao</code>
<code> </code><code>* @date 2015-2-4 上午9:27:59</code>
<code> </code><code>* @param mail</code>
<code> </code><code>* @return</code>
<code> </code><code>*/</code>
<code> </code><code>public</code> <code>static</code> <code>boolean</code> <code>sendHtmlMail(BasicMail mail){</code>
<code> </code>
<code> </code><code>if</code><code>(sendHtmlMail_(mail)){</code>
<code> </code><code>return</code> <code>true</code><code>;</code>
<code> </code><code>} </code><code>else</code><code>{</code>
<code> </code><code>int</code> <code>i = </code><code>0</code><code>;</code>
<code> </code><code>while</code><code>(!sendHtmlMail_(mail) && i < </code><code>3</code><code>){ </code>
<code> </code><code>try</code> <code>{ </code>
<code> </code><code>i++;</code>
<code> </code><code>Thread.sleep(</code><code>1000</code><code>*</code><code>60</code><code>);</code>
<code> </code><code>} </code><code>catch</code> <code>(InterruptedException e) {</code>
<code> </code><code>LOGGER.error(</code><code>"resend mail error"</code><code>, e); </code>
<code> </code><code>}</code>
<code> </code><code>}</code>
加入重發機制運作了一段時間,未再發現類似的漏發現象。
本文轉自 gaochaojs 51CTO部落格,原文連結:http://blog.51cto.com/jncumter/1620095,如需轉載請自行聯系原作者