spring-jdbc直接獲得POJO對象

標準spring-jdbc JdbcTemplate提供了 queryForObject(sql, requiredType)方法,
也許你會認爲,他會把 select name,id from usr 轉換成包含name和id的 Usr 類,

但是隻要你看一下他的源代碼,就知道這隻能是一種幻想
@Override
public <T> T queryForObject(String sql, Class<T> requiredType) throws DataAccessException {
return queryForObject(sql, getSingleColumnRowMapper(requiredType));
}

spring只能處理返回一個字段的SQL,那麼只能針對String這類簡單對象。

這個方法我個人覺得,很欠妥,對比一下.net中針對返回一條記錄一個字段的情形
Object SqlCommand.ExecuteScalar()

這個方法多麼清晰明確,返回的是一個標量,不是集合更不是列表,這種地方真心沒必要用泛型。

而對於返回一個個性對象,mybatis中可以很簡單實現,其實我們大可自己實現一個,沒什麼難的。

對於類
class Usr{
void setName(String s){}
void setId(int id){}
}

就可以簡單使用下面方式
Usr u=myJdbcTemplate.getPOJO("select id,name from usr where id=?",Usr.class,1);
if(u==null){
//用戶沒找到
}

spring-jdbc還有一個問題,就是返回單記錄的查詢,如果不存在,會直接拋出異常,但是實際使用中,這種情況一般都是正常情況,
程序本來就要處理,這樣就逼迫程序必須捕獲異常,變得很難看。更糟糕的是,其內部實際都是先生成一個List,
然後檢查是否只有一條記錄,0或者>1全部要拋出異常。白白多生成一個List對象。這點對比.net的設計就知道差距了。


因此額外增加了一個getSingleMap方法
其他代碼基本都是自解釋的,無非根據jdbc返回記錄的字段名,去對應class利用反射查詢相應方法
可能需要改進的就是一些特殊的SQL字段類型需要單獨處理一下。目前,字符串,整數,日期類型都沒問題

public class myJdbcTemplate extends JdbcTemplate {
	
	/**獲得單一map對象,不存在時返回null*/
	public Map<String, Object> getSingleMap(String sql, Object... args) throws DataAccessException{
		
        return this.query(sql, new ResultSetExtractor<Map<String ,Object>>(){

			public Map<String ,Object> extractData(ResultSet rs) throws SQLException,
					DataAccessException {
				if(!rs.next()) return null;
				
				return new ColumnMapRowMapper().mapRow(rs, 1);
			}
        }, args);
	}
	
	/**獲得單一POJO對象,對應字段名存在1個參數的setXXX方法且字段值不爲null時調用,不存在時返回null*/
	public <T> T getPOJO(String sql,final Class<T> t,Object... args){
        return this.query(sql, new ResultSetExtractor<T>(){

			public T extractData(ResultSet rs) throws SQLException,
					DataAccessException {
				if(!rs.next()) return null;
				
				ResultSetMetaData dd= rs.getMetaData();
				int count=dd.getColumnCount();
		
				return toPOJO(t,rs,dd,count);
				
			}
        }, args);
		
	}

	/**獲得POJO對象List,對應字段名存在1個參數的setXXX方法且字段值不爲null時調用*/
	public <T> List<T> getPOJOList(String sql,final Class<T> t,Object... args){
        return this.query(sql, new ResultSetExtractor<List<T>>(){

			public List<T> extractData(ResultSet rs) throws SQLException,
					DataAccessException {
				List<T> l=new ArrayList<T>();
				
				ResultSetMetaData dd= rs.getMetaData();
				int count=dd.getColumnCount();
				
				while(rs.next()) {
					T o=toPOJO(t,rs,dd,count);
					if(o==null) return null;
					l.add(o);
				}
				
				return l;
			}
        }, args);
		
	}
	
	//轉換成 pojo
	private <T> T toPOJO(Class<T>t,ResultSet rs,ResultSetMetaData dd,int count) throws SQLException{
		T o;
		try {
			o =t.newInstance();
		} catch (InstantiationException e) {
			logger.error("getPOJO InstantiationException when new "+t.getName());
			return null;
		} catch (IllegalAccessException e) {
			logger.error("getPOJO IllegalAccessException when new "+t.getName());
			return null;
		}
		
		for (Method m : t.getMethods()){
			String ms=m.getName();
			if (!ms.startsWith("set") || m.getParameterTypes().length!=1 ) continue;
		
			ms=ms.substring(3).toLowerCase();
			for(int i=1;i<=count;i++){
				if (!dd.getColumnLabel(i).toLowerCase().equals(ms)) continue;
				
				Object a=rs.getObject(i);
				if (a==null) break;
				
				try {
					m.invoke(o, a);
				} catch (IllegalAccessException e) {
					logger.error("getPOJO IllegalAccessException when "+m.getName());
					return null;
				} catch (IllegalArgumentException e) {
					logger.error("getPOJO IllegalArgumentException when "+m.getName());
					return null;
				} catch (InvocationTargetException e) {
					logger.error("getPOJO InvocationTargetException when "+m.getName());
					return null;
				}
				break;
			}
		}
		return o;
		
	}
}


有了這個方法,使用mybatis的理由就又少了一個。

版權聲明:本文爲博主原創文章,未經博主允許不得轉載。

發佈了31 篇原創文章 · 獲贊 12 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章