Home > front end >  How to specify constraint for exception
How to specify constraint for exception

Time:01-19

I have a spring application and I added to my user entity unique constraints for username and email. So users can't create many accounts with the same username or email. When someone creates a new user with a username that already exists my app throws me ConstraintViolationException

My exception details:

org.postgresql.util.PSQLException: ERROR: Duplicate key values violate unique constraints "ukdfui7gxngrgwn9ewee3ogtgym" Detail: The key (username) = (dev123) already exists.

My user entity:

@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;

}

My user table:

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;

i tried to handle that exception in @RestControllerAdvice class:

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

But this catches all the problems with the constraints(not null violation, primary key violation, and so on) and I just want to catch specific constraint violations for my username and email

CodePudding user response:

You can not do custom exceptions if you depend on the Hibernate Validator.

To explain why, look at the BeanValidationEventHandler which listens to all Create/Update/Delete events, it uses the Validator validate method and deals with only 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
                );
            }
        }
    }

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


The Validator interface deals with ConstraintViolation also:

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);

CodePudding user response:

Before saving the User you can check whether the given username already exists or not. If it already exists then don't save the user and ask to enter another username else save the user.

  •  Tags:  
  • Related