晚上飯才剛煮好就被通知上線的程式有錯,趕緊遠端回去查log找問題。
最近在做遷移舊專案邏輯到新專案的工作,舊程式在查資料時是用JDBC PreparedStatement
及ResultSet
取得查詢結果,新專案要改為Spring Data JPA query method寫法。
舊專案的查詢結果可能會有多筆,但下面用if(rs.next())
將游標(cursor)移到第一筆資料並生成物件的寫法讓我誤以為只會有一筆。
...
String sql = "select * from product"
+ " where product_id = ? "
+ " and approved = 1 "
+ " order by create_date desc";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setInt(1, productId);
ResultSet rs = ps.executeQuery();
if (rs.next()) { // 這裡用if讓我誤以為結果只會有一筆
return new Product(rs);
}
...
上面這個很適合拿來做面試題,「請問以上查詢結果的ResultSet有幾筆資料?」,答案是有多筆資料。
在新專案以Spring Data JPQ query method改寫只回傳單一物件如下,在查詢結果為多筆時導致NonUniqueResultException
錯誤。
public interface ProductRepository extends JpaRepository<Product, Long> {
...
Optional<Product> findByProductIdAndApproved(long productId, int approved);
...
}
修正為只取第一筆(top)資料如下解決。
public interface ProductRepository extends JpaRepository<Product, Long> {
...
Optional<Product> findTopByProductIdAndApprovedOrderByCreateDateDesc(long productId, int approved);
...
}
其實這不是一個坑,而是我對理解程式的邏輯有誤造成此問題(寫程式五年多了還是會犯這種錯),因為從ResultSet
取得資料時通常是用while(rs.next())
。此外是上線後才發現錯誤,之前在UAT測試都未發現問題,因此要避免類似的問題的作法只有:
- 加強對程式的理解。
- 完整的測試案例。
沒有留言:
張貼留言