首页 > 解决方案 > 如果我的验证器需要在验证之前调用数据库,我应该让数据库处理这个验证吗?

问题描述

给定以下人为的和琐碎的数据库模式:

Owner
|id|name|age|

Car
|id|owner_id|model|color|

owner_id references Owner(id)

在我的 SpringBoot 应用程序中,如果我想插入一条新Car记录,有两种方法可以防止传递一个不存在的owner_id.

  1. 将插入语句包装在 try/catch 块中以捕获 SpringsDataIntegrityViolationException
try {
  jooq.insertInto(CAR)
  ....
} catch (DataIntegrityViolationException ex) {
  ....
}
  1. 在服务/控制器层使用验证器。这个验证器无论如何都需要查询数据库
class CarValidator {
  OwnerRepository ownerRepository;
  
  public boolean ownerIdExists(UUID ownerId) {
    Set<UUID> ownerIds = ownerRepository.fetchAllOwnerIds();
    return ownerIds.contains(ownerId);
  }
} 

我更喜欢后者(2),但有时我觉得它是多余的,因为无论如何您都需要访问数据库。前者感觉就像你最终用 try/catch 块阻塞了几乎所有的插入语句。

鉴于这两个例子哪个更好,为什么?如果您认为有更好的方法来处理此问题,请将其添加到您的答案中。如果这已在某处得到回答,请指出我。

标签: javadatabasespring-bootvalidationdatabase-design

解决方案


我会采用方法 1。它将为我节省额外的应用程序服务器到 db 往返的时间。

我假设您正在使用 JPA/Hibernate 和 JDBC 连接池。在方法二中,您将拥有一个连接,因为两个数据库查询都将属于同一会话。但是在同一个连接上,您将进行两次往返,一次用于选择,一次用于插入。而且我从上述场景中假设,用户不存在的机会是一种失败场景,这种情况会发生很少。因此,基本上,在所有情况下,您都在节省时间。

关于所有插入的 try/catch,我认为您有一个或最多两个(理想情况下只有一个,因为我看不出为什么需要更多)插入语句到 car & 可以封装在一个方法中。您可以在这个单一的 DAO 方法中处理所有数据库约束错误,并从那里抛出 javax.validation.ValidationException 异常。您的服务类方法可以处理 javax.validation.ValidationException 异常,并且您不需要处理服务层中的任何 JDBC/数据库特定错误。


推荐阅读