如何指定异常约束

sta*_*tan 5 java postgresql spring hibernate exception

我有一个 Spring 应用程序,并向我的用户实体添加了用户名和电子邮件的唯一约束。因此,用户无法使用相同的用户名或电子邮件创建多个帐户。当有人使用已存在的用户名创建新用户时,我的应用程序会抛出 ConstraintViolationException

我的异常详细信息:

org.postgresql.util.PSQLException:错误:重复的键值违反唯一约束“ukdfui7gxngrgwn9ewee3ogtgym”详细信息:键(用户名)=(dev123)已存在。

我的用户实体:

@Table(name = "usr", uniqueConstraints={
@UniqueConstraint(columnNames ={"username"}),
@UniqueConstraint(columnNames={"email"})})
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="usr_seq")
    @SequenceGenerator(name = "usr_seq", sequenceName = "usr_seq", initialValue = 1, allocationSize=1)
    @Column(name="id")
    private Long id;

    @Column(name = "username")
    private String username;

    @Column(name = "password")
    private String password;

    @Column(name = "first_name")
    private String firstName;

    @Column(name = "last_name")
    private String lastName;

    @Column(name = "role")
    private Role role;

    @Column(name = "email")
    private String email;

    @Column(name = "status")
    private Status status;

//    @OneToMany(mappedBy = "author")
//    private List<Message> messages;

//    @OneToMany(mappedBy = "author")
//    private List<Comment> comments;

    @OneToOne
    @JoinColumn(name = "gender_id")
    private Gender gender;

    @OneToOne
    @JoinColumn(name = "address_id")
    private Address address;

    @ManyToMany
    @JoinTable(
            name = "user_subscriptions",
            joinColumns =  @JoinColumn(name = "channel_id") ,
            inverseJoinColumns =  @JoinColumn(name = "subscriber_id"))
    private Set<User> subscribers;

    @ManyToMany
    @JoinTable(
            name = "user_subscriptions",
            joinColumns =  @JoinColumn(name = "subscriber_id") ,
            inverseJoinColumns =  @JoinColumn(name = "channel_id"))
    private Set<User> subscriptions;

    @OneToOne
    @JoinColumn(name = "file_id")
    private FileEntity fileEntity;

}
Run Code Online (Sandbox Code Playgroud)

我的用户表:

create table usr
(
    id         bigint not null
        constraint usr_pkey
            primary key,
    email      varchar(255)
        constraint ukg9l96r670qkidthshajdtxrqf
            unique,
    first_name varchar(255),
    last_name  varchar(255),
    password   varchar(255),
    role       integer,
    status     integer,
    username   varchar(255)
        constraint ukdfui7gxngrgwn9ewee3ogtgym
            unique,
    address_id integer
        constraint fkilsqnqkb7dlk6s5gqedb6lk3r
            references address,
    file_id    bigint
        constraint fkabswqn807logqymtak5wfktfr
            references file,
    gender_id  integer
        constraint fkp89wdnbeu22hjl41g38rk8a1q
            references gender
);

alter table usr
    owner to postgres;
Run Code Online (Sandbox Code Playgroud)

我尝试在 @RestControllerAdvice 类中处理该异常:

@ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<Object> handleConstraintViolationException(){
        ExceptionDetails exceptionDetails = new ExceptionDetails(Errors.ERROR9.getMessage(),timestamp);
        return new ResponseEntity<>(exceptionDetails,HttpStatus.FORBIDDEN);
    }
Run Code Online (Sandbox Code Playgroud)

但这捕获了约束的所有问题(不是空违规、主键违规等),我只想捕获我的用户名和电子邮件的特定约束违规

Sus*_*afa 0

如果您依赖 Hibernate Validator,则无法执行自定义异常。

要解释原因,请查看 BeanValidationEventHandler,它侦听所有 Create/Update/Delete 事件,它使用该Validator validate方法并仅处理ConstraintViolation

private <T> void validate(T object, EntityMode mode, EntityPersister persister,
                              SessionFactoryImplementor sessionFactory, GroupsPerOperation.Operation operation) {
        if ( object == null || mode != EntityMode.POJO ) {
            return;
        }
        TraversableResolver tr = new HibernateTraversableResolver(
                persister, associationsPerEntityPersister, sessionFactory
        );
        Validator validator = factory.usingContext()
                .traversableResolver( tr )
                .getValidator();
        final Class<?>[] groups = groupsPerOperation.get( operation );
        if ( groups.length > 0 ) {
            final Set<ConstraintViolation<T>> constraintViolations = validator.validate( object, groups );
            if ( constraintViolations.size() > 0 ) {
                Set<ConstraintViolation<?>> propagatedViolations =
                        new HashSet<ConstraintViolation<?>>( constraintViolations.size() );
                Set<String> classNames = new HashSet<String>();
                for ( ConstraintViolation<?> violation : constraintViolations ) {
                    LOG.trace( violation );
                    propagatedViolations.add( violation );
                    classNames.add( violation.getLeafBean().getClass().getName() );
                }
                StringBuilder builder = new StringBuilder();
                builder.append( "Validation failed for classes " );
                builder.append( classNames );
                builder.append( " during " );
                builder.append( operation.getName() );
                builder.append( " time for groups " );
                builder.append( toString( groups ) );
                builder.append( "\nList of constraint violations:[\n" );
                for (ConstraintViolation<?> violation : constraintViolations) {
                    builder.append( "\t" ).append( violation.toString() ).append("\n");
                }
                builder.append( "]" );

                throw new ConstraintViolationException(
                        builder.toString(), propagatedViolations
                );
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

https://github.com/hibernate/hibernate-orm/blob/main/hibernate-core/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationEventListener.java


Validator接口还处理 ConstraintViolation:

public interface Validator {

    /**
     * Validates all constraints on {@code object}.
     *
     * @param object object to validate
     * @param groups the group or list of groups targeted for validation (defaults to
     *        {@link Default})
     * @return constraint violations or an empty set if none
     * @throws IllegalArgumentException if object is {@code null}
     *         or if {@code null} is passed to the varargs groups
     * @throws ValidationException if a non recoverable error happens
     *         during the validation process
     */
    <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups);
Run Code Online (Sandbox Code Playgroud)