這邊來認識一些我們可能會用到的一些資料庫查詢操作

根據前面的 products 結構來插入一些資料熟悉一些操作的用法吧。

1
2
3
4
5
6
7
8
9
10
11
12
INSERT INTO products (name, price, description) VALUES
('薩爾達傳說:曠野之息', 59.99, '設定在幻想世界中的開放世界動作冒險遊戲。'),
('超級瑪利歐:奧德賽', 49.99, '以瑪利歐為主角的平臺遊戲,探索各種世界。'),
('集合啦!動物森友會', 54.99, '在無人島上進行發展的生活模擬遊戲。'),
('最終幻想 VII 重製版', 69.99, '經典 RPG 的重製版,具有更新的圖像和遊戲玩法。'),
('巫師3:狂獵', 39.99, '開放世界 RPG,玩家扮演狩魔獵人傑洛特。'),
('電馭叛客2077', 59.99, '設定在反烏托邦未來世界的開放世界 RPG。'),
('對馬戰鬼', 59.99, '設定在日本封建時代的動作冒險遊戲。'),
('碧血狂殺2', 49.99, '以西部為主題的開放世界動作冒險遊戲。'),
('黑帝斯', 24.99, '在地下城中進行逃脫的類 Roguelike 遊戲。'),
('我們之中', 4.99, '在太空船上進行的多人派對遊戲,涉及團隊合作與背叛。');

操作查詢方法

(一) 根據欄位值的相等條件

前面一篇有提到可以直接透過欄位名稱來查找,JPA 會自動幫我們對應,最常用到的就是 findById,如果想要查找其他欄位就可以直接改成像是 findByName, findByPrice 等等都可以讓 JPA 幫我們匹配。

這邊根據 findByName 我們可以透過名稱找到對應商品

controller

1
2
3
4
5
@GetMapping("/q")
public Product getProductsByName(@RequestParam String name) {
return productService.getProductByName(name);
}

service

1
2
3
4
5
6
7
8
9
10
11

@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;

public Product getProductByName(String name) {
return productRepository.findByName(name);
}
}

repository

1
2
3
public interface ProductRepository extends JpaRepository<Product, Integer> {
Product findByName(String name);
}

https://ithelp.ithome.com.tw/upload/images/20240902/20150977BLTg7Zm4IW.png

(二) 範圍條件

可以根據數值的範圍來進行查詢,像是這邊可以用價錢來進行示範

他的格式會像 findBy{判斷欄位}GreaterThanEqual()

  • 大於 findByPriceGreaterThan()
  • 大於等於 findByPriceGreaterThanEqual()
  • 小於 findByPriceLessThan()
  • 小於等於 findByPriceLessThanEqual()

這邊用大於等於

controller

1
2
3
4
@GetMapping("/priceGreaterThanEqual")
public List<Product> getProductsByPriceGreaterThanEqual(@RequestParam int range) {
return productService.getProductGreaterThanEqual(range);
}

service

1
2
3
4
5
6
7
8
9
10
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;

public List<Product> getProductGreaterThanEqual(int range) {
return productRepository.findByPriceGreaterThanEqual(range);
}

}

repository

1
2
3
4
5
@Repository
public interface ProductRepository extends JpaRepository<Product, Integer> {
Product findByName(String name);
List<Product> findByPriceGreaterThanEqual(int range);
}

查詢大於 60 元剛好只有 69.99 元的最終幻想符合
https://ithelp.ithome.com.tw/upload/images/20240902/20150977HBj3KmiXkx.png

多個條件

可透過 And 或 Or 組合

controller

1
2
3
4
5

@GetMapping("/nameOrPrice")
public List<Product> getProductsByNameOrPrice(@RequestParam String name, @RequestParam double price) {
return productService.getProductByNameOrPrice(name, price);
}

service

1
2
3
public List<Product> getProductByNameOrPrice(String name, double price) {
return productRepository.findByNameOrPrice(name, price);
}

repository

1
2
3
4
5
@Repository
public interface ProductRepository extends JpaRepository<Product, Integer> {
List<Product> findByNameOrPrice(String name, double price);
}

https://ithelp.ithome.com.tw/upload/images/20240902/20150977UIMYJWNpeV.png
or 的話只要其中一項條件符合就會執行,像是下面有搜出 name = 黑帝斯 也有搜出 price = 49.99

原生 SQL

使用 JPA 如果不夠彈性你也可使用原生的語法 @Query

@Query 裡面要第一個參數註明是 nativeQuery = true , 第二個參數是 vlaue = “{sql 語法}” ,也可以把 value 省略,直接用 SQL 字串放第二個參數,注意這邊 SQL 語法結尾請不要加分號 “;”

參數的帶入也可以用 :param 的方式,這邊其實寫法有很多種,大家就找到自己喜歡或是團隊易懂的方式來撰寫。

*如果 nativeQuery 沒有開啟就是使用 JPQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//    用 """ """ 可以不用顧慮換行問題
// @Query(
// nativeQuery = true,
// value = """
// SELECT *
// FROM `products`
// WHERE `name` = ?1 OR `price` = ?2
// """
// )
// JPQL 須注意如果語法長中間可能換行及空格導致語法錯誤
@Query("SELECT p FROM Product p WHERE p.name = :name OR p.price = :price")

// Native SQL 須注意如果語法長中間可能換行及空格導致語法錯誤
@Query(
nativeQuery = true,
value = "SELECT * FROM products WHERE name = ?1 OR price = ?2"
)
List<Product> findByNameOrPriceNative(String name, double price);

排序(Sorting)與分頁(Pagination)

Sort 物件

利用 Sort 物件可以做到排序,看是要升序(ASC)或是降序(DESC),這個物件是可以直接帶入 repository 的方法中告知 JPA 要進行排序

1
2
3
4
// asc
Sort sort = Sort.by({欄位}).ascending()
// desc
Sort sort = Sort.by({欄位}).descending();

以撈出全部資料 Service 方法為例,根據價錢進行升序

1
2
3
4
public List<Product> getAllProducts() {
Sort sort = Sort.by("price").ascending();
return productRepository.findAll(sort);
}

可以看到顯示資料就照價錢由小到大排了

https://ithelp.ithome.com.tw/upload/images/20240903/20150977FBBLSm2UcJ.png
Sort 也可以整合下面分頁物件一起做到排序及分頁

Pageable 物件

Pageabe 是一個介面,通常會放入三個參數來透過 PageRequest 方法實作

  • 第一個參數 page 表示第幾頁(需要特別注意第一頁是 0)
  • 第二個參數 limit 表示回傳資料筆數上限
  • 第三個參數 sort 表示排序物件
1
Pageable pageable = PageRequest.of(page, limit, sort);

把上面的 Sort 物件一起帶進去,並且告知我要取得第一頁的 3 筆資料

1
2
3
4
5
6
7
8
9
public Page<Product> getAllProducts() {
Sort sort = Sort.by("price").ascending();

Pageable pageable = PageRequest.of(0,3, sort);

Page<Product> productPage = productRepository.findAll(pageable);

return productPage;
}

回傳的資料可以看到確實有排序,還有提供分頁的相關資訊,也只顯示出第一頁的 3 筆資料

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
{
"content": [
{
"id": 4,
"name": "最終幻想 VII 重製版",
"price": 69.99,
"description": "經典 RPG 的重製版,具有更新的圖像和遊戲玩法。"
},
{
"id": 1,
"name": "薩爾達傳說:曠野之息",
"price": 59.99,
"description": "設定在幻想世界中的開放世界動作冒險遊戲。"
},
{
"id": 6,
"name": "電馭叛客2077",
"price": 59.99,
"description": "設定在反烏托邦未來世界的開放世界 RPG。"
}
],
"pageable": {
"pageNumber": 0,
"pageSize": 3,
"sort": {
"empty": false,
"sorted": true,
"unsorted": false
},
"offset": 0,
"paged": true,
"unpaged": false
},
"totalPages": 4,
"totalElements": 10,
"last": false,
"size": 3,
"number": 0,
"sort": {
"empty": false,
"sorted": true,
"unsorted": false
},
"numberOfElements": 3,
"first": true,
"empty": false
}

以上就是基本的一些查詢跟應用,對於許多網站的搜尋及資料的查找都是相當基本的用法,Spring Data Jpa 也提供給我們許多方便的用法,還有一定的彈性可以自行撰寫原生 SQL。那下篇來講一些關於資料庫關聯的一些操作介紹吧。


參考資料: