为什么在Hibernate/JPA实体中避免使用Lombok @EqualsAndHashCode?

19-02-17 banq
              

Lombok 是一个非常受欢迎和有用的图书馆。尽管如此,请注意Lombok @EqualsAndHashCode对实体的影响可能会带来严重问题。

实体应实施equals()和hashCode()。主要问题是Hibernate要求实体在其所有状态转换(瞬态,附加,分离 和删除)中等于自身。使用Lombok @EqualsAndHashCode不会尊重此要求。

避免下面这些方法

1. 避免使用的Lombok默认行为@EqualsAndHashCode:

@Entity
@EqualsAndHashCode
public class LombokDefaultProduct implements Serializable {

    private static final long serialVersionUID = 1L;

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

    private String name;
    private String code;

2. 避免使用只有主键的Lombok@EqualsAndHashCode

@Entity
@EqualsAndHashCode(exclude = {"name", "code"})
public class LombokIdProduct implements Serializable {

    private static final long serialVersionUID = 1L;

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

    private String name;
    private String code;

3. 避免使用默认equals()和hashCode(),下面实体没有编写自己的这两个方法,使用的是默认,不推荐这样做:

@Entity
public class DefaultProduct implements Serializable {

    private static final long serialVersionUID = 1L;

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

    private String name;
    private String code;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

}
4. 避免使用只依赖主键的equals()和hashCode():

@Entity
public class IdProduct implements Serializable {

    private static final long serialVersionUID = 1L;

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

    private String name;
    private String code;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 89 * hash + Objects.hashCode(this.id);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final IdProduct other = (IdProduct) obj;
        if (!Objects.equals(this.id, other.id)) {
            return false;
        }
        return true;
    }
    
}

推荐这些方法:1. 依靠@NaturalId

@Entity
public class NaturalIdProduct implements Serializable {

    private static final long serialVersionUID = 1L;

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

    private String name;

    @NaturalId
    @Column(nullable = false, updatable = false, unique = true, length = 50)
    private String code;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof NaturalIdProduct)) {
            return false;
        }
        NaturalIdProduct naturalIdProduct = (NaturalIdProduct) o;
        return Objects.equals(getCode(), naturalIdProduct.getCode());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getCode());
    }

}

2. 依靠主键:

@Entity
public class GoodProduct implements Serializable {

    private static final long serialVersionUID = 1L;

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

    private String name;
    private String code;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    @Override
    public boolean equals(Object obj) {
        
        if (this == obj) {
            return true;
        }
        
        if (!(obj instanceof GoodProduct)) {
            return false;
        }
        
        return id != null && id.equals(((GoodProduct) obj).id);
    }

    @Override
    public int hashCode() {
        return 2018;
    }

}

良好的equals()和hashCode():

源代码可以在这里找到