1

I currently have a completely working Spring application, tested very thoroughly and has been successfully running in production for over a year now. Recently, I wanted to have hibernate email validation on the username field. The username field is used for logging in and is also the email of our users.

When I annotate the username field with @Email, the id (annotated with @Id and @Generated(strategy = GenerationType.AUTO)) is not generated anymore and keeps the value null. This causes the hashCode to fail on a NullPointerException (No problem with this though). So for some reason, the id is not generated anymore due to the addition of an @Email annotation to the User.java Entity.

@Entity
@Table(uniqueConstraints = @UniqueConstraint(name = "username",
columnNames = "username"))
public class User implements Serializable, UserDetails {

    @GeneratedValue(strategy = GenerationType.AUTO)
    @Id
    private Long id;

    @Column(nullable = false)
    @JsonView(View.NoProfile.class)
    @Email
    protected String username;

    @Column(nullable = false)
    private String passwordHash;

    @JsonIgnore
    @ElementCollection(targetClass = Role.class, fetch = FetchType.EAGER)
    @Enumerated(EnumType.STRING)
    @CollectionTable(name = "user_role")
    private Set<Role> roles;

    @OneToOne(targetEntity = Profile.class, cascade = CascadeType.ALL)
    @JsonView(View.Public.class)
    protected Profile profile;

    @JsonIgnore
    private boolean accountNonExpired = true;
    @JsonIgnore
    private boolean accountNonLocked = true;
    @JsonIgnore
    private boolean credentialsNonExpired = true;
    @JsonIgnore
    private boolean enabled = true;

    public User(String username, String passwordHash) {
        this.username = username;
        this.passwordHash = passwordHash;
        this.profile = new Profile();
        this.roles = new HashSet<>();
        roles.add(Role.ROLE_USER);
    }

    User() { // jpa only
    }

    public Profile getProfile() {
        return profile;
    }

    public Long getId() {
        return id;
    }

    public void setPasswordHash(String passwordHash) {
        this.passwordHash = passwordHash;
    }

    @Override
    public Set<? extends GrantedAuthority> getAuthorities() {
        return roles;
    }

    @Override
    @JsonIgnore
    public String getPassword() {
    return passwordHash;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return accountNonExpired;
    }

    @Override
    public boolean isAccountNonLocked() {
        return accountNonLocked;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return credentialsNonExpired;
    }

    @Override
    public boolean isEnabled() {
        return enabled;
    }

    public void resetProfile() {
        this.profile = new Profile();
    }

    public void addRole(Role role) {
        this.roles.add(role);
    }

    public void setAccountNonLocked(boolean accountNonLocked) {
        this.accountNonLocked = accountNonLocked;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    @JsonView(View.Public.class)
    public int getReference() {
        return username.hashCode();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        User user = (User) o;

        return username.equals(user.username) && id.equals(user.id);

    }

    @Override
    public int hashCode() {
        int result = username.hashCode();
        result = 31 * result + id.hashCode();
        return result;
    }
}
  • You haven't changed your database schema/entity recently have you? And then failed to update? – Derrops Oct 10 '16 at 02:13
  • Do you have ? update, or create is good too. – Derrops Oct 10 '16 at 02:14
  • This happens on either `create` and `create-drop`, so the database should be right. – martijn9612 Oct 10 '16 at 07:58
  • Why do you have an UniqueConstraint at the class level with only one field? Shouldn't you just put that on the email, otherwise it's confusing, see http://stackoverflow.com/questions/15372654/uniqueconstraint-and-columnunique-true-in-hibernate-annotation – Derrops Oct 10 '16 at 23:24
  • This is a style choice, we wanted to have the unique constraints on the top of the file to make the unique fields clear immediately. Also, this unfortunately does not solve the issue at hand. Thanks for helping out though. – martijn9612 Oct 11 '16 at 08:38
  • It's different see the SO entry. – Derrops Oct 11 '16 at 10:07

1 Answers1

0

The problem can be that when trying to save the user hibernate checks if it is valid. If there is a problem with the username email format hibernate does not try to save it, thus id does not generate an id.

But what happens with the validation error? I think it's possibly beeing masked by the NPE you talk about in hashCode method. Try to rewrite your hashCode taking care of possible NPE, and check for hibernate constraint violations.

Ricardo Vila
  • 1,626
  • 1
  • 18
  • 34
  • 1
    So I rewrote the hashcode to be able to deal with the NPE. It works now, but it was not the solution I originally wanted. So from what I can conclude is that the `@email` annotation causes the hashcode to generate earlier and the id generation to take place later. Since without the `@email` annotation, the null check was never needed (the id was generated before the hashcode). – martijn9612 Oct 11 '16 at 13:48