数据库怪异事件探源之PreparedStatement

【引言】  最近在公司一项目开发中一个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多了很多好处(好处就不一一列举了,看网上高手介绍),现在发现原来都有使用范围。

本文仅阐述了自己开发过程中碰到的陷阱一个,供遇到相同困扰百思不得其解的人参考,也供路过的人消遣.....。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值