我有一个独特的现场诽谤问题。这可以应用于任何对象上的任何唯一字段,但在我的情况下,我有一个用户,我希望用户名在数据库中是唯一的。
我的用户的片段:
@Entity
public class User {
@NotNull
@Column(unique = true)
@UniqueUsername
private String username;
}
UniqueUsername是一个自定义约束注释,如下所示:
@Constraint(validatedBy = {UniqueUsernameValidator.class})
public @interface UniqueUsername {
String message() default "User already exists with this username";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
然后,UniqueUsernameValidator是一个自定义约束验证器,它只是检查数据库中是否有给定用户名的现有用户:
@ApplicationScoped
public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, String> {
@Inject
private UserRepository userRepo;
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
boolean valid = false;
try {
userRepo.findByUsername(value);
} catch (NoResultException e) {
// No result means username not already used
valid = false
}
return valid;
}
}
现在,对于新用户,验证效果很好。我遇到的问题是当我编辑已经存在的用户时。更新用户后,验证会找到我当前正在编辑的用户,返回false并拒绝编辑。
理想情况下,我的验证器可能会访问/知道正在验证的用户对象(而不仅仅是字段值),这样我就可以检查我在数据库中找到的重复对象是否与正在编辑的对象相同。但我在验证器中找不到这样做的方法。
其他人遇到这个问题并提出可接受的解决方案吗?
答案 0 :(得分:0)
如何使用用户名和生成的主键(ID)的组合。使用id作为引用将更快,如果您正在更新或创建新实体,您将获得可以解决的其他信息。
您的验证需要移至实体级别,然后您可以检查实体是否具有空id(创建新)或具有一些ID(更新现有)。
创建新时,检查是否存在其他同名实体。 更新时执行相同的检查,但如果您发现具有相同名称的实体,请确保它不是同一个实体 - 使用id。
正如你所写,这种情况不只是关于用户,而是关于一般的实体,这种方法也可以解决具有多个唯一字段的情况。
答案 1 :(得分:-1)
我面临同样的问题,我以这种方式跳过了更新操作的验证器:
@Override
public boolean isValid(String email, ConstraintValidatorContext constraintValidatorContext) {
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
for (int i = 0; i < elements.length; i++) {
StackTraceElement element = elements[i];
if(element.getMethodName().equals("preUpdate")){
return true;
}
}