问题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
|
SELECT
task.processExecutionId,
process.
name
as
processDefineName,
task.activityName,
user
.userName
as
createUserCode,
task.status,
task.description,
task.finishTime,
task.createTime
FROM
T_BPM_PROCESS_TASK task
LEFT
JOIN
T_BPM_PROCESS_EXECUTION execution
ON
task.processExecutionId = execution.id
LEFT
JOIN
t_bpm_process_define process
ON
execution.processDefineId = process.id
LEFT
JOIN
T_SYS_USER
user
ON
task.createUserCode =
user
.userCode
WHERE
task.processExecutionId = #{id}
ORDER
BY
task.createTime
DESC
, task.finishTime
DESC
|
在这个版本上线里,我们新上线了高级查询的基于用户的管理权限控制,负责的杜工在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:
1
2
3
|
update
t_bpm_process_define
set
name
=SUBSTR(
NAME
FROM
1
FOR
INSTR(
name
,
"(测试单据)"
)-1)
where
INSTR(
name
,
"(测试单据)"
)!=0;
update
t_bpm_form_define
set
template=
REPLACE
(template,
'(测试单据)'
,
''
)
where
INSTR(template,
'(测试单据)'
)!=0;
update
t_bpm_form_define
set
html=
REPLACE
(html,
'<span style="color:#ff0000;">(测试表单)</span>'
,
''
)
where
INSTR(html,
'<span style="color:#ff0000;">(测试表单)</span>'
)!=0;
|
执行后,表单倒是没有问题,但第一句的执行出现了可怕的现象,很多流程名称是空的。
第一句中我使用的是字符串的截取,第二、第三句都是替换部分字符串。我最初想可能跟这个有关,实际也确实有关,如果我也用replace函数是没有问题的,原因很简单,如果文本中不包含目标字符串肯定是不会替换的。但用字符串截取从逻辑上也讲得通,为什么会出现这么可怕的结果呢?
原因就在于我们update没有使用表内联,sql在执行的时候根本没有什么数据去关联它到底该修改哪条数据。
如果我们将第一条数据修改成这样,就没有问题了:
1
|
update
t_bpm_process_define a,(
select
id,
name
from
t_bpm_proces_define
where
INSTR(
name
,
"(测试单据)"
)!=0) )b
set
a.
name
=SUBSTR(
NAME
FROM
1
FOR
INSTR(
name
,
"(测试单据)"
)-1)
where
a.id = b.id;
|
问题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:
1
2
3
4
5
|
if
(serverSocket !=
null
)
openServer();
// only happens from connect(socket) else
// openServer(host, port);
else
openServer(host, port);
|
以下是openServer中的代码:
1
2
3
4
5
6
7
8
9
10
|
if
(debug)
out.println(
"DEBUG SMTP: got bad greeting from host \""
+
server +
"\", port: "
+
port +
", response: "
+
r +
"\n"
);
throw
new
MessagingException(
"Got bad greeting from SMTP host: "
+ server +
", port: "
+
port +
", response: "
+
r);
}
|
而debug是一个全局变量,是session的一个属性,其值
Since the debug setting can be turned on only after the Session has been created
也就是说session未建立就会报MessagingException这个错误。所以我进一步怀疑,我们的发送邮件代码每次都会去重连认证,这个也是代码的问题之一,所以我加了session失效才重连。
接下来又进行了一次测试,用例如下:
用例1.代码修改如下
1
2
3
4
|
if
(session ==
null
|| session.getDebug()) {
System.out.println(
"第"
+ j +
"次重连"
);
session = Session.getDefaultInstance(prop, auth);
}
|
结果:没有失败
用例2.仍用1的代码,
结果:但失败1次,失败后未重新获得session,可见失败后session仍有效;
用例3.代码修改如下:
1
2
3
4
5
6
7
8
9
|
if
(session ==
null
|| session.getDebug()) {
// 如果需要身份验证,创建一个账号密码验证器
if
(mail.isValidate()) {
auth =
new
MailAuthenticator(mail.getUserName(),
mail.getPassword());
}
System.out.println(
"第"
+ j +
"次重连"
);
session = Session.getDefaultInstance(prop, auth);
}
|
结果:有1次失败,失败后未重连。
通过以上测试,是否每次重新获得session对效率 没有影响,对异常的发生也没有影响。异常报出后,session仍有效,并且可以重发。
经过三轮测试,其实我们可以发现重发机制已经可以保证邮件的完全送达。
重发的代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
/**
* Temporary mechanism: automatic resend mail
* @author chao.gao
* @date 2015-2-4 上午9:27:59
* @param mail
* @return
*/
public
static
boolean
sendHtmlMail(BasicMail mail){
if
(sendHtmlMail_(mail)){
return
true
;
}
else
{
int
i =
0
;
while
(!sendHtmlMail_(mail) && i <
3
){
try
{
i++;
Thread.sleep(
1000
*
60
);
}
catch
(InterruptedException e) {
LOGGER.error(
"resend mail error"
, e);
}
}
return
true
;
}
}
|
加入重发机制运行了一段时间,未再发现类似的漏发现象。
本文转自 gaochaojs 51CTO博客,原文链接:http://blog.51cto.com/jncumter/1620095,如需转载请自行联系原作者