创建完美的JPA实体

问题:

我一直在使用JPA(实现Hibernate)一段时间,每次我需要创建实体,我发现自己正在努力解决AccessType,不可变属性,equals / hashCode,…等问题。
所以我决定尝试找出每个问题的一般最佳做法,并写下来供个人使用。
我不介意任何人对此发表评论,或者告诉我我错了什么。

实体类

  • 实现Serializable
    原因:The specification says you have to, but some JPA providers do not enforce this. Hibernate as JPA provider does not enforce this, but it can fail somewhere deep in its stomach with ClassCastException, if Serializable has not been implemented.

构造函数

  • 创建一个具有实体的所有必需字段的构造函数
     Reason: A constructor should always leave the instance created in a sane state.
  • 除了这个构造函数:有一个私有默认构造函数包
     Reason: Default constructor is required to have Hibernate initialize the entity; private is allowed but package private (or public) visibility is required for runtime proxy generation and efficient data retrieval without bytecode instrumentation.

字段/属性

  • 新新新200新200新新200新新200新新200新200新新200新200新新200新新200新新200新新200新新200新新200新新新200新新200新
     Reason: this is probably the most debatable issue since there are no clear and convincing arguments for one or the other (property access vs field access); however, field access seems to be general favourite because of clearer code, better encapsulation and no need to create setters for immutable fields
  • 省略不可变字段的设置器(访问类型字段不需要)
  • 属性可能是私人
    原因:我曾听说过(Hibernate)的保护效果更好,但是我可以在网路上找到:Hibernate can access public, private, and protected accessor methods, as well as public, private and protected fields directly. The choice is up to you and you can match it to fit your application design.

等于/的hashCode

  • 如果仅在持久化实体时才设置此ID,则不要使用生成的ID
  • 优先选择:使用不可变值形成一个唯一的商业密钥,并用它来测试平等
  • 如果唯一的商业密钥不可用,则使用在实体初始化时创建的非瞬态UUID;有关详细信息,请参阅this great article
  •  决不指相关实体(ManyToOne);如果这个实体(像一个父实体)需要成为业务密钥的一部分,那么只比较ID。只要使用property access type,代理服务器调用getId()就不会触发实体的加载。

示例实体

@Entity
@Table(name = "ROOM")
public class Room implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue
    @Column(name = "room_id")
    private Integer id;

    @Column(name = "number") 
    private String number; //immutable

    @Column(name = "capacity")
    private Integer capacity;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "building_id")
    private Building building; //immutable

    Room() {
        // default constructor
    }

    public Room(Building building, String number) {
        // constructor with required field
        notNull(building, "Method called with null parameter (application)");
        notNull(number, "Method called with null parameter (name)");

        this.building = building;
        this.number = number;
    }

    @Override
    public boolean equals(final Object otherObj) {
        if ((otherObj == null) || !(otherObj instanceof Room)) {
            return false;
        }
        // a room can be uniquely identified by it's number and the building it belongs to; normally I would use a UUID in any case but this is just to illustrate the usage of getId()
        final Room other = (Room) otherObj;
        return new EqualsBuilder().append(getNumber(), other.getNumber())
                .append(getBuilding().getId(), other.getBuilding().getId())
                .isEquals();
        //this assumes that Building.id is annotated with @Access(value = AccessType.PROPERTY) 
    }

    public Building getBuilding() {
        return building;
    }


    public Integer getId() {
        return id;
    }

    public String getNumber() {
        return number;
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(getNumber()).append(getBuilding().getId()).toHashCode();
    }

    public void setCapacity(Integer capacity) {
        this.capacity = capacity;
    }

    //no setters for number, building nor id

}

其他建议添加到这个列表是更受欢迎的…

回答:

JPA 2.0 Specification表示:

该规范不包含对实体的equals和hashCode方法的实现的要求,只对主键类和映射键,我知道。

 
 
Code问答: http://codewenda.com/topics/python/
Stackoverflow: Create the perfect JPA entity

*转载请注明本文链接以及stackoverflow的英文链接

发表评论

电子邮件地址不会被公开。 必填项已用*标注

58 − 49 =