gpt4 book ai didi

java - 标识触发 DataIntegrityViolationException 的约束名称

转载 作者:行者123 更新时间:2023-11-30 06:49:39 26 4
gpt4 key购买 nike

我遇到了识别哪个约束触发 DataIntegrityViolationException 的问题。我有两个独特的限制条件:用户名和电子邮件,但我没有找到答案。

我试图找出根本原因异常,但我收到了这条消息

唯一索引或主键违规:“UK_6DOTKOTT2KJSP8VW4D0M25FB7_INDEX_4 ON PUBLIC.USERS(EMAIL) VALUES ('copeland@yahoo.com', 21)”;语句:
插入用户 (id, created_at, updated_at, country, email, last_name, name, password, phone, sex, username) values (null, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [23505-193]

阅读错误我知道电子邮件约束会触发验证,但我想返回给用户,例如:{type: ERROR, message: "The email already exist"}

我在其他帖子中读过,人们处理它是为了在异常中寻找约束名称(例如,users_unique_username_idx)并向用户显示正确的消息。但是我无法获得那种类型的约束名称

也许我缺少配置。我正在使用:

Spring Boot 1.5.1.RELEASE、JPA、Hibernate 和 H2

我的application.properties

spring.jpa.generate-ddl=true

用户类:

@Entity(name = "users")
public class User extends BaseEntity {
private static final Logger LOGGER = LoggerFactory.getLogger(User.class);

public enum Sex { MALE, FEMALE }

@Id
@GeneratedValue
private Long id;

@Column(name = "name", length = 100)
@NotNull(message = "error.name.notnull")
private String name;

@Column(name = "lastName", length = 100)
@NotNull(message = "error.lastName.notnull")
private String lastName;

@Column(name = "email", unique = true, length = 100)
@NotNull(message = "error.email.notnull")
private String email;

@Column(name = "username", unique = true, length = 100)
@NotNull(message = "error.username.notnull")
private String username;

@Column(name = "password", length = 100)
@NotNull(message = "error.password.notnull")
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String password;

@Enumerated(EnumType.STRING)
private Sex sex;

@Column(name = "phone", length = 50)
private String phone;

@Column(name = "country", length = 100)
@NotNull(message = "error.country.notnull")
private String country;

public User() {}

// Getters and setters

}

ControllerValidationHandler.class

@ControllerAdvice
public class ControllerValidationHandler {
private final Logger LOGGER = LoggerFactory.getLogger(ControllerValidationHandler.class);

@Autowired
private MessageSource msgSource;

private static Map<String, String> constraintCodeMap = new HashMap<String, String>() {
{
put("users_unique_username_idx", "exception.users.duplicate_username");
put("users_unique_email_idx", "exception.users.duplicate_email");
}
};

// This solution I see in another stackoverflow answer but not work
// for me. This is the closest solution to solve my problem that I found
@ResponseStatus(value = HttpStatus.CONFLICT) // 409
@ExceptionHandler(DataIntegrityViolationException.class)
@ResponseBody
public ErrorInfo conflict(HttpServletRequest req, DataIntegrityViolationException e) {
String rootMsg = ValidationUtil.getRootCause(e).getMessage();
LOGGER.info("rootMessage" + rootMsg);
if (rootMsg != null) {
Optional<Map.Entry<String, String>> entry = constraintCodeMap.entrySet().stream()
.filter((it) -> rootMsg.contains(it.getKey()))
.findAny();
LOGGER.info("Has entries: " + entry.isPresent()); // false
if (entry.isPresent()) {
LOGGER.info("Value: " + entry.get().getValue());
e=new DataIntegrityViolationException(
msgSource.getMessage(entry.get().getValue(), null, LocaleContextHolder.getLocale()));
}
}
return new ErrorInfo(req, e);
}

此时的响应是:

{"timestamp":1488063801557,"status":500,"error":"内部服务器错误","exception":"org.springframework.dao.DataIntegrityViolationException","message":"无法执行语句;SQL [n/a];约束 [\"UK_6DOTKOTT2KJSP8VW4D0M25FB7_INDEX_4 ON PUBLIC.USERS(EMAIL) VALUES ('copeland@yahoo.com', 21)\"; SQL 语句:\ninsert into users (id, created_at, updated_at , country, email, last_name, name, password, phone, sex, username) values (null, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [23505-193]]; 嵌套异常是 org.hibernate.exception.ConstraintViolationException: 无法执行语句","path":"/users"

更新

这是我处理持久化操作的服务层

MysqlService.class

@Service
@Qualifier("mysql")
class MysqlUserService implements UserService {
private UserRepository userRepository;

@Autowired
public MysqlUserService(UserRepository userRepository) {
this.userRepository = userRepository;
}

@Override
public List<User> findAll() {
return userRepository.findAll();
}

@Override
public Page<User> findAll(Pageable pageable) {
return userRepository.findAll(pageable);
}

@Override
public User findOne(Long id) {
return userRepository.findOne(id);
}

@Override
public User store(User user) {
return userRepository.save(user);
}

@Override
public User update(User usr) {
User user = this.validateUser(usr);

return userRepository.save(user);
}

@Override
public void destroy(Long id) {
this.validateUser(id);

userRepository.delete(id);
}

private User validateUser(User usr) {
return validateUser(usr.getId());
}

/**
* Validate that an user exists
*
* @param id of the user
* @return an existing User
*/
private User validateUser(Long id) {
User user = userRepository.findOne(id);
if (user == null) {
throw new UserNotFoundException();
}
return user;
}
}

更新#2

repo 重现问题 https://github.com/LTroya/boot-users .我在 ValidationExceptionHandler.class 上评论了我的处理程序,以便查看异常。

发送两次 json at Json to test on Readme.md to POST/users/

最佳答案

您要做的不是在 @Column 注释上指定唯一的列要求,您可以在 JPA 的 @Table 注释上实际定义那些具有名称的列提供对这些约束的进一步控制。

@Entity
@Table(uniqueConstraints = {
@UniqueConstraint(name = "UC_email", columnNames = { "email" } ),
@UniqueConstraint(name = "UC_username", columnNames = " { "userName" } )
})

现在有两种处理异常的方法:

在 Controller 中

您可以选择将解析逻辑放在您的 Controller 中,并简单地捕获 spring 抛出的 DataIntegrityException 并在那里解析它。类似于以下伪代码:

public ResponseBody myFancyControllerMethod(...) {
try {
final User user = userService.myFactoryServiceMethod(...);
}
catch ( DataIntegrityException e ) {
// handle exception parsing & setting the appropriate error here
}
}

对我来说,这种方法的最终症结在于,我们将处理持久性问题的代码向上移动了两层,而不是直接位于持久性层之上的层。这意味着如果我们有多个 Controller 需要处理这种情况,我们要么发现自己正在执行以下操作之一

  • 引入一些抽象的基础 Controller 来放置逻辑。
  • 引入一些带有我们调用的静态方法的辅助类以供重用。
  • 剪切-粘贴代码 - 是的,这种情况比我们想象的要多。

将代码放在表示层中还会引入一些问题,当您需要与可能实际上不会返回某种类型的 html View 的其他消费者类型共享该服务时。

这就是为什么我建议将逻辑再向下推一层。

在服务中

这是一种更简洁的方法,因为我们将约束处理的验证推送到持久层之上的层,这意味着我们最终将在其中处理持久性故障。不仅如此,我们的代码实际上记录了失败条件,我们可以根据上下文选择忽略或处理它们。

这里需要注意的是,我建议您创建从服务层代码中抛出的特定异常类,以便识别唯一约束失败并在解析来自 hibernate 。

在您的 web Controller 、rest Controller 或任何其他正在调用您的服务的消费者中,您只需要在必要时捕获适当的异常类并相应地进行分支。这是一些服务伪代码:

public User myFancyServiceMethod(...) {
try {
// do your stuff here
return userRepository.save( user );
}
catch( ConstraintViolationException e ) {
if ( isExceptionUniqueConstraintFor( "UC_email" ) ) {
throw new EmailAddressAlreadyExistsException();
}
else if ( isExceptionUniqueConstraintFor( "UC_username" ) ) {
throw new UserNameAlreadyExistsException();
}
}
}

关于java - 标识触发 DataIntegrityViolationException 的约束名称,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42462829/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com