【引言】 最近在公司一项目开发中一个excel导入数据库程序时,碰到一个“灵异”现象。使用PreparedStatement构造查询语句,传入参数得到查询结果。很平常也很普通的一个用法,但是无论传入的参数是什么都返回同样的结果。但是把真正的查询sql语句放在sql语句执行环境中,实际上是没有结果的。这样就很困惑了,到底是什么原因呢?
先不分析原因了,先把我的代码贴出来,有代码有真相。
环境: 人力资源用的数据库是DB2 AS400
SimpleBaseDao.java 简化的BaseDao
//------------------------------------------------------------
package org.study.db.dao.base;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* Dao的基类,负责创建和回收数据库连接。
*
* @author Walker Jong
* @version 1.0, 2011-11-22
*/
public class SimpleBaseDao {
// 数据库连接相关参数
private static String dbDriver = "com.ibm.as400.access.AS400JDBCDriver";
private static String dbUrl = "jdbc:as400://localhost/JYK;package ccsid=1208;date format=iso";
private static String dbUserName = "***";
private static String dbPassword = "***";
static {
loadDriver();
}
private static void loadDriver() {
// 加载数据库驱动器
try {
Class.forName(dbDriver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获得一个数据库连接。
*
* @return
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(dbUrl, dbUserName, dbPassword);
}
/**
* 关闭数据库连接。
*
* @param conn
*/
public static void closeConnection(Connection conn) {
closeConnection(conn, null, null);
}
/**
* 关闭数据库连接创建的statement和connection。
*
* @param conn
* @param stat
*/
public static void closeConnection(Connection conn, Statement stat) {
closeConnection(conn, stat, null);
}
/**
* 关闭数据库连接创建的ResultSet、Statement、Connection等资源。
*
* @param conn
* @param stat
* @param result
*/
public static void closeConnection(Connection conn, Statement stat,
ResultSet result) {
if (result != null) {
try {
result.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stat != null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
实际访问数据库的Dao代码:
SourceItemDao.java
//------------------------------------------------------
package org.study.db.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.study.db.SourceItem;
import org.study.db.dao.base.SimpleBaseDao;
/**
* 查询sm_source_item表中相关信息。
* 该表主要表述数据库中创建的各表的字段信息。
*
* @author Walker Jong
* @version 1.0.0
*
*/
public class SourceItemDao extends SimpleBaseDao{
public List<SourceItem> getFieldList(String tableName) throws SQLException{
List<SourceItem> list = new ArrayList<SourceItem>();
String sql = " SELECT field_name, description, field_type, field_len, code_id FROM sm_source_item WHERE set_id = ? ";
Connection conn = null;
PreparedStatement pstat = null;
ResultSet result = null;
try{
conn = getConnection();
pstat = conn.prepareStatement(sql);
pstat.setString(1, tableName);
result = pstat.executeQuery();
while(result.next()){
String name = result.getString(1);
String description = result.getString(2);
String type = result.getString(3);
int length = result.getInt(4);
String codeId = result.getString(5);
list.add(new SourceItem(name, description, type, length, codeId));
}
}finally{
closeConnection(conn, pstat, result);
}
return list;
}
public static void main(String[] args){
SourceItemDao stDao = new SourceItemDao();
try {
// List<SourceItem> list = stDao.getFieldList("A001");
List<SourceItem> list = stDao.getFieldList("A001_bak");
System.out.println("size="+list.size());
for(SourceItem st: list){
System.out.println(st);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
现在根据代码阐述具体的问题:
在SourceItemDao类中,测试写的getFieldList()方法时,无论传入参数“A001”,还是“A001_bak",或在"A001****”都返回相同的结果,都是“A001”数据表的结果。
正确结果应该是:只有“A001”返回数据,其他的查询没有数据。
分析问题:
1、开始怀疑是AS400的问题,因为以前在SQLServer中从来没有碰到过这样的问题。
方案: 然后自己在AS400和SQLServer中建立了同样的表,通用的使用PreparedStatement的查询语句,但是结果一样,都是查询得到的正确结果。没有出现上述问题。
结论: 排除数据库AS400的问题。
2、那是什么问题呢?想了很久找不到原因?
于是找了几个DB2的高手帮忙看看,还是没有结果。
今天早上一个偶然机会,还是看看数据库中表的结构吧,不看不知道,一看吓一跳。原来如此。
在sm_source_item表的结构中,发现要查询的字段set_id的类型是char(4),原来如此,它设定的为4个字符长度,当用PreparedStatement的setString()方法时,驱动程序根据数据库的要求自动截断(汗~~,截断了一点信息提示都不给),然后就出现了上面的情况。
网上很多人都推荐PreparedStatement来代替Statement的使用,以前也觉得PreparedStatement比Statement多了很多好处(好处就不一一列举了,看网上高手介绍),现在发现原来都有使用范围。
本文仅阐述了自己开发过程中碰到的陷阱一个,供遇到相同困扰百思不得其解的人参考,也供路过的人消遣.....。