mybatis使用char型別欄位查詢oracle資料庫時結果返回null
同事在學mybatis時,遇到了一個問題就是,使用char型別欄位作為查詢條件時一直都查不出資料,其他型別的則可以。
使用的資料庫是oracle,查詢條件欄位型別是char(50),java程式碼對應的是String型別。
後來經過排查,是由於在oracle中,char型別欄位,如果內容長度不夠,會自動以空格方式補足長度。如欄位 name char(5)
,若值為sgl
,那麼oracle會自動用空格補足長度,最終值為sgl
。
一、解決方法:
方法1:先用trim()函式把值去掉兩邊空格再作為條件查詢,如:
select * from data where data.name=#{name}
改為:
select * from data where trim(data.name)=#{name}
方法2:將欄位型別char()改為varchar2()型別。一般情況下,只有所有值長度都一樣時才用char()型別,比如性別欄位,用0表示男和1表示女時,就可以用char(1),如果值的長度不固定,有長有短,最好別用char()型別。
二、深入瞭解mybatis返回null
拋開mybatis框架,回到原始的jdbc查詢,當使用oracle的char型別作為條件查詢資料時,只有值完全一樣時才能查到資料。
如建立一個測試表:
create table t_user(
user_name char (5)
);
insert into t_user (user_name)values('sgl');
select '"'||user_name||'"' from t_user; -- 查詢結果為"sgl ",可以看出oracle自動補了兩個空格
通過jdbc的PreparedStatement方式查詢資料:
conn=getConnection();
ps=conn.prepareStatement("select * from t_user where user_name=?");
ps.setString(1,"sgl");
ResultSet rs = ps.executeQuery ();
通過上面方式是無法查到資料的,因為查詢條件值”sgl”和資料庫中值”sgl “是不相等的。
如果值用“sgl ”可以查到資料:
conn=getConnection();
ps=conn.prepareStatement("select * from t_user where user_name=?");
ps.setString(1,"sgl "); -- 增加兩個空格不足5位長度
ResultSet rs = ps.executeQuery();
如果使用trim()方式也可以查詢到資料,如:
conn=getConnection();
ps=conn.prepareStatement("select * from t_user where trim(user_name)=?"); -- 先對資料庫中user_name進行去空格,然後再比較
ps.setString(1,"sgl");
ResultSet rs = ps.executeQuery();
現在回到mybatis,同事的Mapper檔案裡查詢sql如下:
<select id="selectByName" resultType="com.entity.Data" parameterType="java.lang.String">
select * from data where data.name=#{name}
</select>
main方法內容為:
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
DataService d = (DataService) ctx.getBean("dataServiceImpl");
Data data = d.selectByName("sgl");
System.out.println(data);
}
其實,通過檢視原始碼或將日誌改為debug級別,可以看出在mybatis底層,會將查詢語句使用PreparedStatement預編譯,然後再將引數設定進去。如下面是mybatis打印出來的日誌:
==> Preparing: select * from data where data.name=?
==> Parameters: sgl(String)
根據前面的jdbc查詢,我們知道原因,所以很容易理解mybatis中的問題。
另外,mysql下面,當char型別欄位的值不足時,好像並不自動將值以空格補足,儘管如此,當值長度不固定時,也不推薦使用char型別。
jdbc查詢完整的程式碼如下:
jdbc工具類:
package com.songguoliang.url;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
/**
* 純jdbc連線資料類
* @author sgl
*
*/
public class PureJdbcDao {
private static ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
private static int reCount = 0;
/**
* 獲取連線
* @return
*/
private static Connection getConnection(){
Connection conn=null;
try {
Class.forName(bundle.getString("driverClassName"));
conn = DriverManager.getConnection(bundle.getString("url") ,
bundle.getString("username") , bundle.getString("password"));
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally{
if(null==conn&&reCount<5){
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
reCount++;
System.out.println("資料庫第"+reCount+"次重連");
conn = getConnection();
}
}
return conn;
}
/**
* 查詢資料
* @param sql
* @return
*/
public static List<String[]>query(String sql){
List<String[]>result=new ArrayList<String[]>();
Connection conn=null;
Statement stmt=null;
try {
//System.out.println("[PureJdbcDao]查詢語句:" + sql);
conn=getConnection();
stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
ResultSetMetaData rsMeta = rs.getMetaData();
while(rs.next()){
int columnNum=rsMeta.getColumnCount();
String []field=new String[columnNum];
String fieldValue=null;
for(int i=1;i<=columnNum;i++){
fieldValue=rs.getString(i);
if(fieldValue==null){
fieldValue="";
}
field[i-1]=fieldValue;
}
result.add(field);
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
try {
if(stmt!=null){
stmt.close();
}
if(conn!=null){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
return result;
}
public static List<String[]>query(String sql,List<String>params){
List<String[]>result=new ArrayList<String[]>();
Connection conn=null;
PreparedStatement ps=null;
try {
conn=getConnection();
ps=conn.prepareStatement(sql);
for(int i=0;i<params.size();i++){
ps.setString(i+1,params.get(i));
}
ResultSet rs = ps.executeQuery();
ResultSetMetaData rsMeta = rs.getMetaData();
while(rs.next()){
int columnNum=rsMeta.getColumnCount();
String []field=new String[columnNum];
String fieldValue=null;
for(int i=1;i<=columnNum;i++){
fieldValue=rs.getString(i);
if(fieldValue==null){
fieldValue="";
}
field[i-1]=fieldValue;
}
result.add(field);
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
try {
if(ps!=null){
ps.close();
}
if(conn!=null){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
return result;
}
/**
* 執行sql語句
* @param sql
*/
public static void execute(String sql){
Connection conn=null;
Statement stmt=null;
try {
//System.out.println("[PureJdbcDao]sql語句:" + sql);
conn = getConnection();
conn.setAutoCommit(false);
stmt = conn.createStatement();
stmt.execute(sql);
conn.commit();
} catch (SQLException e) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally{
try {
if(stmt!=null){
stmt.close();
}
if(conn!=null){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
測試類:
package com.songguoliang;
import java.util.Arrays;
import java.util.List;
import com.songguoliang.url.PureJdbcDao;
public class Test {
public static void main(String[] args) {
//List<String[]>list=PureJdbcDao.query("select * from t_user where user_name=?",Arrays.asList("sgl")); // 查詢到條數:0
//List<String[]>list=PureJdbcDao.query("select * from t_user where user_name=?",Arrays.asList("sgl ")); //查詢到條數:1
List<String[]>list=PureJdbcDao.query("select * from t_user where trim(user_name)=?",Arrays.asList("sgl")); //查詢到條數:1
System.out.println("查詢到條數:"+list.size());
}
}