前面一篇介紹如何使用JdbcTemplate實現插入數據,接下來進入實際業務中,最多見的查詢篇。因爲查詢的姿式實在太多,對內容進行了拆分,本篇主要介紹幾個基本的使用姿式java
環境依然藉助前面一篇的配置,連接如: 190407-SpringBoot高級篇JdbcTemplate之數據插入使用姿式詳解mysql
或者直接查看項目源碼: github.com/liuyueyi/sp…git
咱們查詢所用數據,正是前面一篇插入的結果,以下圖github
queryForMap,通常用於查詢單條數據,而後將db中查詢的字段,填充到map中,key爲列名,value爲值spring
最基本的使用姿式,就是直接寫完整的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);
}
複製代碼
直接看上面代碼,會發現問題麼???數組
有經驗的小夥伴,可能一會兒就發現了sql注入的問題,若是傳入的參數是 '一灰灰blog' or 1=1 order by id desc limit 1
, 這樣輸出和咱們預期的一致麼?bash
正是由於直接拼sql,可能到只sql注入的問題,因此更推薦的寫法是經過佔位符 + 傳參的方式mybatis
// 使用佔位符替換方式查詢
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);
複製代碼
上面全部代碼能夠查看: github.com/liuyueyi/sp…
簡單的繼承調用下上面的全部方法
@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或者有更好的建議,歡迎批評指正,不吝感激