在使用Spring/Spring Boot+Spring Data JPA的開發過程中,判斷表中滿足條件的記錄是否存在是一個經常遇到的業務場景,比如檢查指定用戶名的用戶是否存在,檢查指定id的設備是否存在等等。在業務開發過程中,通常有兩種方法:
1)使用指定的條件查詢數據庫,然後在業務代碼判斷返回的結果是否爲null,如果爲null則滿足條件的記錄不存在,否則記錄存在。
2)使用JPA提供的封裝方法existsXXX,方法返回ture表示滿足條件的記錄存在,否則不存在。
那麼這兩種方法有何區別,在業務中應該如何選擇呢?下面我們從兩種方法使用的SQL語句,網絡交互以及適用的業務場景出發講解兩種方法。
1 實驗環境準備
1.1 版本信息
JPA 版本:2.3.4.RELEASE
MySQL版本:8.0
Wireshark:3.6.1
1.2 數據庫表
@Table(name = "t_device", indexes = @Index(name = "device_id", columnList = "device_id"))
public class Device {
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
private String id;
@Column(name = "device_id", unique = true, nullable = false)
private String deviceId;
@Column(name = "app_id", nullable = false)
private String appId;
}
1.3 方法定義
@Repository
public interface DeviceRepository extends JpaRepository<Device, String> {
Device findByDeviceId(String deviceId); // 方法1
boolean existsByDeviceId(String deviceId); // 方法2
}
2、SQL分析
有兩種方法可以獲取上述方法1和方法2執行的SQL語句,一種時在應用程序開啓SQL語句打印,一種是通過網絡抓包。由於網絡抓包相對不夠簡潔,因此在應用程序開啓SQL打印可以方便在日常工作中分析JPA執行的SQL。在application.ymal中添加下面的配置可以開啓打印SQL語句:
spring:
jpa:
show-sql: true #控制檯顯示SQL
方法Device findByDeviceId(String deviceId);
執行的SQL語句爲(語句經過了修改,JPA生成的語句比較不方便查看):
SELECT * FROM t_device WHERE device_id = 'your device id'
方法boolean existsByDeviceId(String deviceId);
執行的SQL語句爲(語句經過了修改,JPA生成的語句比較不方便查看):
SELECT id FROM t_device WHERE device_id = 'your device id' LIMIT 1
從SQL語句來看,兩個方法的主要區別爲:
1)方法1查詢記錄的所有字段
2)方法2只查詢記錄的主鍵並且只返回一條記錄(limit 1)
方法1返回的數據量要大於方法2,因此當表的字段很多時,這種數據量差異就更加明顯。下面我們通過網絡抓包來證實數據量的差異。
3 網絡分析
可以使用Wireshark軟件進行網絡抓包和分析,Wireshark支持分析MySQL協議。如果你不方便抓包也沒有關係,可以從本文對應的項目下載抓包文件並分析,下載地址:
3.1 方法1對應Server的響應如下:
3.2 方法2對應Server的響應如下:
從網絡抓包來看,方法1返回的數據量要大於方法2。
4 業務場景選擇
從SQL和網絡分析來看,兩種方法的原來是相同的,都是向Server發送一個查詢命令,然後根據查詢的結果進行判斷。不同的是兩種方法執行的SQL不同,Server返回的數據量不同,方法1的數據量以及對帶寬的佔用多與方法2,那麼在開發中如何根據不同的場景進行選擇呢?
-
場景1:只需要判斷記錄是否存在
這種場景選擇方法2的成本最低執行效率最高,因爲只是判斷記錄是否存在,不需要查詢記錄所有的字段。
-
場景2:判斷記錄是否存在,並且在記錄存在時對記錄執行其他操作
這種場景選擇方法1,因爲在判斷記錄是否存在後還需要進行其他的操作,如果使用方法2還需要再次使用方法1獲取記錄進行操作。