1. 程式人生 > >SpringBoot高階篇JdbcTemplate之資料查詢上篇

SpringBoot高階篇JdbcTemplate之資料查詢上篇

前面一篇介紹如何使用JdbcTemplate實現插入資料,接下來進入實際業務中,最常見的查詢篇。由於查詢的姿勢實在太多,對內容進行了拆分,本篇主要介紹幾個基本的使用姿勢

  • queryForMap
  • queryForList
  • queryForObject

<!-- more -->

I. 環境準備

環境依然藉助前面一篇的配置,連結如: 190407-SpringBoot高階篇JdbcTemplate之資料插入使用姿勢詳解

或者直接檢視專案原始碼: https://github.com/liuyueyi/spring-boot-demo/blob/master/spring-boot/101-jdbctemplate

我們查詢所用資料,正是前面一篇插入的結果,如下圖

db mysql

II. 查詢使用說明

1. queryForMap

queryForMap,一般用於查詢單條資料,然後將db中查詢的欄位,填充到map中,key為列名,value為值

a. 基本使用姿勢

最基本的使用姿勢,就是直接寫完整的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

, 這樣輸出和我們預期的一致麼?

b. 佔位符替換

正是因為直接拼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);

c. 查不到的case

使用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();
}

查詢不到異常

2. queryForList

前面針對的主要是單個查詢,如果有多個查詢的場景,可能就需要用到queryForList了,它的使用姿勢和上面其實差別不大;

a. 基本使用姿勢

最基本的使用姿勢當然是直接寫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 拋異常是不一樣的

b. 佔位符替換

直接使用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);

3. queryForObject

如果是簡單查詢,直接用上面兩個也就夠了,但是對於使用過mybatis,Hibernate的同學來說,每次返回Map<String, Object>,就真的有點蛋疼了, 對於mysql這種資料庫,表的結構基本不變,完全可以和POJO進行關聯,對於業務開發者而言,當然是操作具體的POJO比Map要簡單直觀多了

下面將介紹下,如何使用 queryForObject 來達到我們的目標

a. 原始使用姿勢

首先介紹下利用 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);

b. 高階使用

當sql返回的列名和POJO的屬性名可以完全匹配上的話,上面的這種寫法就顯得非常冗餘和麻煩了,我需要更優雅簡潔的使用姿勢,最好就是直接傳入POJO型別,自動實現轉換

如果希望得到這個效果,你需要的就是下面這個了: BeanPropertyRowMapper

// 更簡單的方式,直接通過BeanPropertyRowMapper來實現屬性的賦值,前提是sql返回的列名能正確匹配
moneyPO = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(MoneyPO.class));
System.out.println("queryForObject by BeanPropertyRowMapper: " + moneyPO);

c. 易錯使用姿勢

檢視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);

show

4. 測試

上面所有程式碼可以檢視: https://github.com/liuyueyi/spring-boot-demo/blob/master/spring-boot/101-jdbctemplate/src/main/java/com/git/hui/boot/jdbc/query/QueryService.java

簡單的繼承呼叫下上面的所有方法

@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);
    }
}

輸出結果如下

result

III. 小結

本篇博文主要介紹了JdbcTemplate查詢的簡單使用姿勢,主要是queryForMap, queryForList, queryForObject三種方法的呼叫

1. 根據返回結果數量

單條記錄查詢

  • queryForMap : 返回一條記錄,返回的結果塞入Map<String, Object>, key為固定的String對應查詢的列名;value為實際值
  • queryForObject :同樣返回一條資料,與上面的區別在於可以藉助RowMapper來實現返回結果轉換為對應的POJO

需要注意的是,上面的查詢,必須有一條記錄返回,如果查不到,則拋異常

批量查詢

  • queryForList :一次查詢>=0條資料,返回型別為 List<Map<String, Object>>

2. 根據sql型別

有兩種sql傳參方式

  • 一個是寫完整的sql語句,就和我們普通的sql查詢一樣;問題是存在注入的風險
  • 其次是使用佔位符(?), 實際的值通過引數方式傳入

IV. 其他

0. 專案

1. 宣告

盡信書則不如,以上內容,純屬一家之言,因個人能力有限,難免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激

相關推薦

SpringBoot高階JdbcTemplate資料查詢

前面一篇介紹如何使用JdbcTemplate實現插入資料,接下來進入實際業務中,最常見的查詢篇。由於查詢的姿勢實在太多,對內容進行

申論(爆發資料閱讀

圖形 img 轉化 ima 進口 但是 info 餅狀圖 idt 一、常見的材料類型   1、文字類      2、表格類      3、圖形類 條形圖 折線圖&餅狀圖 三角模型

從誌願軍“斷刀”再論敏捷道(

慢慢 失敗 多個 之一 朝鮮 無法 一次 mark 學習 從誌願軍“斷刀”再論敏捷之道(上篇) 作者:歐德張(原創) ??在現在的IT項目中,以往常用的是瀑布模型套路,這些年敏捷模式大受歡迎,關於敏捷,現在諸人開口PMI-ACP,閉口則SCRUM,又有諸多實踐、案例遵行其

從零開始學習比特幣--P2P 網路的建立訊息處理

現在終於,來到了我們非常非常關心比特幣訊息處理,通過比特幣訊息處理,我們會理解比特幣的協義,理解比特幣是如何同步區塊,如何傳送交易,從而建立起理解比特幣的至關重要一步。 本部分內容是如此的重要,也是相當的長,所以我們分上下兩部分來介紹具體的訊息處理。 上篇主要

產品經理必備技能資料分析入門

一.資料分析門檻在那裡? 1.掌握利用資料評估產品改版(或新功能)效果的方法。2.掌握藉助資料發現產品改進關鍵點的方法。3.學會在資料的配合下快速完成使用者畫像的方法。4.知道如何定義資料埋點以及分析需求,並推動研發團隊實施,或者掌握一種資料分析工具(諸葛io、百度統計)

福布斯系列資料分析思路 | Python資料分析專案實戰

福布斯每年都會發布福布斯全球上市企業2000強排行榜(Forbes Global 2000),這個排行榜每年釋出的時候,國內外總有新聞會熱鬧的討論一番,但很少見到比較全面的分析。 因此才有了這樣一個想法,蒐集近些年每年釋出的排行榜,做一個進一步的分析。 在準

Hive資料查詢

一,排序和聚合 對於排序有兩種方式,一種是order by 一種是sort by order by 會對所有的資料進行排序,所以最後會只有一個reducer來處理,如果資料量非常大,效率會非常差勁 sort by是部分排序,只是對一個reducer的資料進行排序 FROM records2 SELECT

資料庫系統概論學習筆記(四):SQL的簡單應用資料查詢

嗯……最實用的部分,其他都可以不會,唯獨這個必須熟練吧 —— 資料查詢 這篇筆記主要是例子,使用的例子是《筆記(三)》中定義的學生選課關係。使用的SQL語句可能與教材上有些出入,因為我使用的是MySQL 5.7,對SQL語言支援可能會有些許差異。

前端外掛Datatables使用--

工欲善其事,必先利其器 本系列文章介紹我在運維繫統開發過程中用到的那些順手的前端外掛,前邊兩篇分別介紹了Duallistbox外掛和Select2外掛的使用,這一篇開始Databases的征服之旅 Databases是一款基於JQuery的表格外掛,主要用來優化table,支援表格分頁、搜尋、排序、顯示條

JDK原始碼那些事兒淺析Thread

JAVA中多執行緒的操作對於初學者而言是比較難理解的,其實聯想到底層作業系統時我們可能會稍微明白些,對於程式而言最終都是硬體上執行二進位制指令,然而,這些又太過底層,今天來看一下JAVA中的執行緒,淺析JDK原始碼中的Thread類,之後能幫助我們更好的處理執行緒問題 前言 JDK版

認證授權方案授權揭祕 ()

# 一、前言 回顧:[認證授權方案之授權初識](https://www.cnblogs.com/i3yuan/p/13198355.html) 從上一節中,我們在對授權系統已經有了初步的認識和使用,可以發現,asp.net core為我們提供的授權策略是一個非常強大豐富且靈活的認證授權方案,能夠滿足大部分

SpringBoot從入門到放棄》第(七)——JdbcTemplate訪問資料庫,postman 的下載、使用

為了測試方便,需要重新建立一個測試專案,以免受到之前測試專案的干擾,同時慢慢深入業務程式碼分離、為分散式開發做準備。 配置資料來源 ,在 pom.xml 新增依賴包(pom.xml裡面的內容可以複製之前的): <dependency> <gro

資料開發Hadoop----提交作業到yarn的流程

當一個mapreduce作業被提交到yarn上面的時候,他的流程是這樣的: 1,當client想yarn提交了作業後,就意味著想ResourceManager申請一個ApplicationMaster。這個時候RM(這裡我們將ResourceManager簡稱為RM,同理NodeManager為

資料結構排序——插入排序 //無序查詢有序位置,邊比較邊移動,有哨兵

      typedef int DataType; void insert(DataType *a, int n) {     for (int i = 1; i < n; i++) {   

資料結構排序——插入排序 //無序查詢有序位置,邊比較邊移動,有哨兵

      typedef int DataType; void insert(DataType *a, int n) {     for (int i = 1; i < n; i++) {         //把選擇的元素放在臨時變數中         Da

資料結構基本查詢與樹表查詢

只要你開啟電腦,就會涉及到查詢技術。如炒股軟體中查股票資訊、硬碟檔案中找照片、在光碟中搜DVD,甚至玩遊戲時在記憶體中查詢攻擊力、魅力值等資料修改用來作弊等,都要涉及到查詢。當然,在網際網路上查詢資訊就更加是家常便飯。查詢是計算機應用中最常用的操作之一,也是許多程

最簡單MySQL教程詳解(基礎多表聯合查詢

常用術語 內連線 外連線 左外連線 右外連線 注意事項: 自連線 子查詢 在上篇文章史上最簡單MySQL教程詳解(基礎篇)之資料庫設計正規化及應用舉例我們介紹過,在關係型資料庫中,我們通常為了減少資料的冗餘量將對資料表進行規範,將

SpringBoot基礎AOP高階使用技能

開發十年,就只剩下這套架構體系了! >>>   

SpringBoot高階搜尋Solr 文件新增與修改使用姿勢

大多涉及到資料的處理,無非CURD四種操作,對於搜尋SOLR而言,基本操作也可以說就這麼幾種,在實際應用中,搜尋條件的多樣性才是重

iOS多線程開發離不開的GCD(

sop 先進先出 調度 事件 實現 說明 優先級 子線程 函數 一、GCD基本概念 GCD 全稱Grand Central Dispatch(大中樞隊列調度),是一套低層API,提供了?種新的方法來進?並發程序編寫。從基本功能上講,GCD有點像NSOperatio