前面一篇介紹如何使用JdbcTemplate實現插入數據,接下來進入實際業務中,最多見的查詢篇。因爲查詢的姿式實在太多,對內容進行了拆分,本篇主要介紹幾個基本的使用姿式java
<!-- more -->mysql
環境依然藉助前面一篇的配置,連接如: 190407-SpringBoot高級篇JdbcTemplate之數據插入使用姿式詳解git
或者直接查看項目源碼: https://github.com/liuyueyi/spring-boot-demo/blob/master/spring-boot/101-jdbctemplategithub
咱們查詢所用數據,正是前面一篇插入的結果,以下圖spring
queryForMap,通常用於查詢單條數據,而後將db中查詢的字段,填充到map中,key爲列名,value爲值sql
最基本的使用姿式,就是直接寫完整的sql,執行數據庫
String sql = "select * from money where id=1"; Map<String, Object> map = jdbcTemplate.queryForMap(sql); System.out.println("QueryForMap by direct sql ans: " + map);
這種用法的好處是簡單,直觀;可是有個很是致命的缺點,若是你提供了一個接口爲數組
public Map<String, Object> query(String condition) { String sql = "select * from money where name=" + condition; return jdbcTemplate.queryForMap(sql); }
直接看上面代碼,會發現問題麼???mybatis
有經驗的小夥伴,可能一會兒就發現了sql注入的問題,若是傳入的參數是 '一灰灰blog' or 1=1 order by id desc limit 1
, 這樣輸出和咱們預期的一致麼?app
正是由於直接拼sql,可能到只sql注入的問題,因此更推薦的寫法是經過佔位符 + 傳參的方式
// 使用佔位符替換方式查詢 sql = "select * from money where id=?"; map = jdbcTemplate.queryForMap(sql, new Object[]{1}); System.out.println("QueryForMap by ? ans: " + map); // 指定傳參類型, 經過傳參來填充sql中的佔位 sql = "select * from money where id =?"; map = jdbcTemplate.queryForMap(sql, 1); System.out.println("QueryForMap by ? ans: " + map);
從上面的例子中也能夠看出,佔位符的使用很簡單,用問好(?
)來代替具體的取值,而後傳參
傳參有兩種姿式,一個是傳入Object[]
數組;另一個是藉助java的不定長參數方式進行傳參;兩個的佔位替換都是根據順序來的,也就是若是你有一個值想替換多個佔位符,那就得血屢次
如:
sql = "select * from money where (name=? and id=?) or (name=? and id=?)"; map = jdbcTemplate.queryForMap(sql, "一灰灰blog", 1, "一灰灰blog", 2);
使用queryForMap有個不得不注意的事項,就是若是查不到數據時,會拋一個異常出來,因此須要針對這種場景進行額外處理
// 查不到數據的狀況 try { sql = "select * from money where id =?"; map = jdbcTemplate.queryForMap(sql, 100); System.out.println("QueryForMap by ? ans: " + map); } catch (EmptyResultDataAccessException e) { e.printStackTrace(); }
前面針對的主要是單個查詢,若是有多個查詢的場景,可能就須要用到queryForList
了,它的使用姿式和上面其實差異不大;
最基本的使用姿式固然是直接寫sql執行了
System.out.println("============ query for List! =============="); String sql = "select id, `name`, money, is_deleted as isDeleted, unix_timestamp(create_at) as created, unix_timestamp(update_at) as updated from money limit 3;"; // 默認返回 List<Map<String, Object>> 類型數據,若是一條數據都沒有,則返回一個空的集合 List<Map<String, Object>> res = jdbcTemplate.queryForList(sql); System.out.println("basicQueryForList: " + res);
注意返回的結果是List<Map<String, Object>>
, 若是一條都沒有命中,會返回一個空集合, 和 QueryForMap
拋異常是不同的
直接使用sql的查詢方式,依然和前面同樣,可能有注入問題,固然優先推薦的使用經過佔位來傳參方式
String sql2 = "select id, `name`, money, is_deleted as isDeleted, unix_timestamp(create_at) as created, " + "unix_timestamp(update_at) as updated from money where id=? or name=?;"; res = jdbcTemplate.queryForList(sql2, 2, "一灰灰2"); System.out.println("queryForList by template: " + res);
若是是簡單查詢,直接用上面兩個也就夠了,可是對於使用過mybatis,Hibernate的同窗來講,每次返回Map<String, Object>
,就真的有點蛋疼了, 對於mysql這種數據庫,表的結構基本不變,徹底能夠和POJO進行關聯,對於業務開發者而言,固然是操做具體的POJO比Map要簡單直觀多了
下面將介紹下,如何使用 queryForObject
來達到咱們的目標
首先介紹下利用 RowMapper
來演示下,最原始的使用姿式
第一步是定義對應的POJO類
@Data public static class MoneyPO implements Serializable { private static final long serialVersionUID = -5423883314375017670L; private Integer id; private String name; private Integer money; private boolean isDeleted; private Long created; private Long updated; }
而後就是使用姿式
// sql + 指定返回類型方式訪問 // 使用這種sql的有點就是方便使用反射方式,實現PO的賦值 String sql = "select id, `name`, money, is_deleted as isDeleted, unix_timestamp(create_at) as created, unix_timestamp(update_at) as updated from money limit 1;"; // 須要注意,下標以1開始 MoneyPO moneyPO = jdbcTemplate.queryForObject(sql, new RowMapper<MoneyPO>() { @Override public MoneyPO mapRow(ResultSet rs, int rowNum) throws SQLException { MoneyPO po = new MoneyPO(); po.setId(rs.getInt(1)); po.setName(rs.getString(2)); po.setMoney(rs.getInt(3)); po.setDeleted(rs.getBoolean(4)); po.setCreated(rs.getLong(5)); po.setUpdated(rs.getLong(6)); return po; } }); System.out.println("queryFroObject by RowMapper: " + moneyPO);
從使用姿式上看,RowMapper
就是一個sql執行以後的回調,實現結果封裝,這裏須要注意的就是 ResultSet
封裝了完整的返回結果,能夠經過下標方式指定,下標是從1開始,而不是咱們常見的0,須要額外注意
這個下標從1開始,感受有點蛋疼,總容易記錯,因此更推薦的方法是直接經過列名獲取數據
// 直接使用columnName來獲取對應的值,這裏就能夠考慮使用反射方式來賦值,減小getter/setter moneyPO = jdbcTemplate.queryForObject(sql, new RowMapper<MoneyPO>() { @Override public MoneyPO mapRow(ResultSet rs, int rowNum) throws SQLException { MoneyPO po = new MoneyPO(); po.setId(rs.getInt("id")); po.setName(rs.getString("name")); po.setMoney(rs.getInt("money")); po.setDeleted(rs.getBoolean("isDeleted")); po.setCreated(rs.getLong("created")); po.setUpdated(rs.getLong("updated")); return po; } }); System.out.println("queryFroObject by RowMapper: " + moneyPO);
當sql返回的列名和POJO的屬性名能夠徹底匹配上的話,上面的這種寫法就顯得很是冗餘和麻煩了,我須要更優雅簡潔的使用姿式,最好就是直接傳入POJO
類型,自動實現轉換
若是但願獲得這個效果,你須要的就是下面這個了: BeanPropertyRowMapper
// 更簡單的方式,直接經過BeanPropertyRowMapper來實現屬性的賦值,前提是sql返回的列名能正確匹配 moneyPO = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(MoneyPO.class)); System.out.println("queryForObject by BeanPropertyRowMapper: " + moneyPO);
查看JdbcTemplate提供的接口時,能夠看到下面這個接口
@Override public <T> T queryForObject(String sql, Class<T> requiredType, @Nullable Object... args) throws DataAccessException { return queryForObject(sql, args, getSingleColumnRowMapper(requiredType)); }
天然而然的想到,直接傳入POJO的類型進去,是否是就能夠獲得咱們預期的結果了?
String sql = "select id, `name`, money, is_deleted as isDeleted, unix_timestamp(create_at) as created, unix_timestamp(update_at) as updated from money limit 1;"; try { MoneyPO po = jdbcTemplate.queryForObject(sql, MoneyPO.class); System.out.println("queryForObject by requireType return: " + po); } catch (Exception e) { e.printStackTrace(); }
執行上面的代碼,拋出異常
從上面的源碼也能夠看到,上面的使用姿式,適用於sql只返回一列數據的場景,即下面的case
// 下面開始測試下 org.springframework.jdbc.core.JdbcTemplate.queryForObject(java.lang.String, java.lang.Class<T>, java.lang.Object...) // 根據測試,這個類型,只能是基本類型 String sql2 = "select id from money where id=?"; Integer res = jdbcTemplate.queryForObject(sql2, Integer.class, 1); System.out.println("queryForObject by requireId return: " + res);
簡單的繼承調用下上面的全部方法
@SpringBootApplication public class Application { private QueryService queryService; public Application(QueryService queryService) { this.queryService = queryService; queryTest(); } public void queryTest() { queryService.queryForMap(); queryService.queryForObject(); queryService.queryForList(); } public static void main(String[] args) { SpringApplication.run(Application.class); } }
輸出結果以下
本篇博文主要介紹了JdbcTemplate查詢的簡單使用姿式,主要是queryForMap
, queryForList
, queryForObject
三種方法的調用
單條記錄查詢
queryForMap
: 返回一條記錄,返回的結果塞入Map<String, Object>
, key爲固定的String對應查詢的列名;value爲實際值queryForObject
:一樣返回一條數據,與上面的區別在於能夠藉助RowMapper
來實現返回結果轉換爲對應的POJO須要注意的是,上面的查詢,必須有一條記錄返回,若是查不到,則拋異常
批量查詢
queryForList
:一次查詢>=0條數據,返回類型爲 List<Map<String, Object>>
有兩種sql傳參方式
盡信書則不如,以上內容,純屬一家之言,因我的能力有限,不免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激