我负责维护的一个JavaEE项目中有这样一个功能:可以用poi批量导入Excel表中的学号并在该系统中创建对应的用户。上周三中午,接到用户反馈,将Excel导入系统后用其中的用户名出现了无法登陆提示用户名或密码错误的提示。
第一次问题分析:
在测试环境中导入该Excel表格后,发现数据库用户表中确实出现了该用户名,但仔细观察,其前后都有空格。看来问题的故障定位在于从Excel表中按数值型读出数据失败抛出异常后,数据被当做String类型直接写入了数据库。这样,当用户上传的Excel表中用户名字段前后包含空格时,直接被写入了数据库中。而当用户通过页面输入用户名密码登录时,用户名中的空格却被忽略掉了,导致后台按前端传入的用户名查询时数据库返回无此记录从而登录失败。
第一次修复尝试:
既然直到了问题的症结,那修复起来也就清晰了。具体思路就是利用MySQL提供的一些函数完成表中指定字段的空格去除,搜索得知有trim和replace两种方法。首先在导入了存在问题Excel表的测试环境中:
备份数据库:由于要对数据库进行改动,所以在之前一定要进行备份。
mysqldump -u 用户名 -p 数据库名 > 路径/文件名.sql
尝试去除user表中Login字段的前后空格:
UPDATE user SET Login=trim(Login); UPDATE user SET Login=replace(Login, ' ', '');
以上两种尝试都以失败告终,最终结果显示:0 rows affected。分析多半出现在正常用户名前后的不是真正的空格,而是一些显示不出的ASCII码,为了方便探查,这里就用JDBC来查询吧。
第二次原因分析
在测试环境中,编写一段JDBC程序对SQL中某行有问题的数据读出,并逐字符转换为ASCII码对应的数字进行检查。在导入了MySQL的JDBC驱动jar包后,相关代码如下:
import java.sql.*;
public class CBugFind {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
String dbUrl = "jdbc:mysql://localhost:端口/数据库名?useSSL=true";
String dbUserName = "root";
String dbPassword = "这里是密码";
Connection con = DriverManager.getConnection(dbUrl, dbUserName, dbPassword);
Statement stat = con.createStatement();
String sql = "SELECT * FROM user WHERE Id=78"; //78号用户的用户名恰有异常
ResultSet rs = stat.executeQuery(sql);
String strLogin = null;
String strNumber = null;
while (rs.next()) {
strLogin = rs.getString("Login");
for (int i = 0; i < strLogin.length(); i++) {
System.out.println((int)strLogin.charAt(i));
}
}
rs.close();
stat.close();
con.close();
}
}
运行后,部分结果如下:
160
50
48
49
55
48
48
48
50
48
49
48
49
50
160
32
看到了么,就是这个十进制为160的数在捣鬼,它又是何方神圣呢?其实是故人,就是HTML里常用的空格而已。在明白这一点后,我们就可以尝试将其去掉了。然而很不幸,经过多次尝试,也无法使用trim或replace成功将这个160号空格干掉。索性那就还是用Java吧:
public class CBugFixDemo1 {
public static void main(String[] args) {
String strUser = " 2017010801015 ";
strUser = " 2017000201012 ";
showAllChar(strUser);
System.out.println("-------------------------------");
// This is round fix1
strUser = strUser.replaceAll("\\u00A0","");
showAllC