在Spring Web專案中,通常在DAO或Repository層存取資料庫,而存取資料庫時拋出例外錯誤該在哪處理?
有些人偏好在DAO層以try-catch
捕捉所有異常,然後在catch
區塊紀錄log並拋出自訂例外錯誤。例如下面是存取資料表EMPLOYEE
的DemoDao
及Spring Data JPA的Repository DemoRepo
。
DemoDao
package com.abc.demo.dao;
import com.abc.demo.entity.Employee;
import com.abc.demo.exception.DemoException;
import com.abc.demo.repo.DemoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public class DemoDao {
@Autowired
private DemoRepository demoRepository;
public void add(Employee employee) {
try {
demoRepository.save(employee);
} catch (Exception e) {
String message = "access database error";
log.error(message, e);
throw new DemoException(message); // 拋出自訂例外錯誤
}
}
}
DemoRepository
package com.abc.demo.repo;
import com.abc.demo.entity.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface DemoRepository extends JpaRepository<Employee, Long> {}
自訂的例外錯誤類別。
DemoException
package com.abc.demo.exception;
public class DemoException extends RuntimeException {
private String message;
public DemoException(String message) {
this.message = message;
}
// getters and setters
}
但這樣的做法導致每個DAO都會有一堆類似的try-catch
,且只是要把原始錯誤訊息轉譯為自訂訊息而已。
更甚者還會把DAO拋出的自訂例外錯誤繼承Exception
而非RuntimeException
,也就代表著在Service層又要在處理一次自訂例外,然後才拋出另一個繼承RuntimeException
的自訂例外,最後在@ControllerAdvice
類處理錯誤及回應結果給外部。
然而Spring存取資料庫發生錯誤時會拋出以DataAccessException
為父類別所對應的子類,而DataAccessException
繼承RuntimeException
。這意味著Spring並不要求在執行資料庫存取的地方處理例外,而是讓錯誤一直往外拋直到可處理的地方,也就代表以上的做法不好。
若要把原始例外錯誤轉成自訂例外錯誤,可統一在@ControllerAdvice
類處理,也就是DAO應改成下面。
package com.abc.demo.dao;
import com.abc.demo.entity.Employee;
import com.abc.demo.exception.DemoException;
import com.abc.demo.repo.DemoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public class DemoDao {
@Autowired
private DemoRepository demoRepository;
public void add(Employee employee) {
demoRepository.save(employee); // 把try-catch拿掉,不在此處理例外錯誤
}
}
所有Spring Web資料庫存取拋出的錯誤統一由@ControllerAdvice
捕捉DataAccessException
來處理。
DemoExceptionHandler
package com.abc.demo.controller;
import lombok.extern.log4j.Log4j2;
import org.springframework.dao.DataAccessException;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@Log4j2
@ControllerAdvice
public class DemoExceptionHandler {
@ExceptionHandler({DataAccessException.class})
public final ResponseEntity<String> handleDataAccessException(DataAccessException e) {
String message = "access database error";
log.error(message, e);
return ResponseEntity.badRequest().body(message);
}
}
結論是若無特殊需求不應在Spring的Service,DAO或Repository層個別用try-catch
處理存取資料庫造成的例外,原因為:
- Spring存取資料庫發生的例外錯誤設計不要求立即處理。
try-catch
區塊讓程式碼看起來比較髒。- 團隊成員一多就會有人忘記加
try-catch
。 - 拋出的錯誤訊息格式不統一。
沒有留言:
張貼留言