创建完美的JPA实体 [英] Create the perfect JPA entity

查看:117
本文介绍了创建完美的JPA实体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在使用JPA(实现Hibernate)一段时间,每次我需要创建实体时,我发现自己正在与AccessType,不可变属性,equals / hashCode等等问题挣扎。

因此,我决定尝试找出每个问题的一般最佳做法,并将其写下供个人使用。

我不介意任何人对此发表评论或告诉我我在哪里'm错了。

实体类




  • 实现Serializable p>

    原因:规范说明您必须执行此操作,但有些JPA提供程序不执行此操作。 Hibernate作为JPA提供程序不会强制执行此操作,但如果Serializable尚未实现,它可能会在ClassCastException的深处发生故障。




构造函数




  • 使用实体的所有必填字段创建构造函数

    原因:构造函数应该始终保持创建的实例处于正常状态。

  • 除了这个构造函数:有一个包的私有默认构造函数



    原因:缺省构造函数需要Hibernate初始化实体;私有是允许的,但包私有(或公共)可见性是运行时代理生成和高效数据检索所需的,而不需要字节码检测。



字段/属性




  • 在需要时使用字段访问权限和属性访问权限



    原因:这可能是最值得商榷的问题,因为对于其中一个(属性访问vs字段访问)没有明确和有说服力的论点。然而,由于代码更清晰,封装更好,并且不需要为不可变字段创建setter,所以字段访问似乎是最受欢迎的。 不可变字段(访问类型字段不需要)

  • 属性可能是私有的
    原因:我曾经听说受保护对于(Hibernate)性能更好但我可以在网上找到的是:Hibernate可以直接访问公共,私有和受保护的访问方法,以及公共,私有和受保护的字段。



Equals / hashCode

这个选择取决于您,您可以将它匹配以适合您的应用程序设计。 >


  • 如果仅在持久化实体时才设置该ID,则不要使用生成的ID

  • 首选项:使用不可变值以形成唯一的业务关键字并使用它来测试相等性如果唯一的业务关键字不可用,请使用非暂时性的 UUID 来测试关键字

  • 实体初始化;请参阅这个伟大的文章以获得更多信息。
  • 从来没有引用相关实体(ManyToOne);如果此实体(如父实体)需要成为业务密钥的一部分,则仅比较ID。在代理上调用getId()不会触发实体的加载,只要您使用属性访问类型


示例实体



  @Entity 
@Table(name =ROOM)
public class Room实现Serializable {

private static final long serialVersionUID = 1L;

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

@Column(name =number)
私人字符串编号; //不可变

@Column(name =capacity)
private整数容量;

@ManyToOne(fetch = FetchType.LAZY,可选= false)
@JoinColumn(name =building_id)
私人建筑物; //不可变

房间(){
//默认构造函数
}

公共房间(建筑物,字符串编号){
//带有必需字段的构造函数
notNull(building,使用null参数(应用程序)调用的方法);
notNull(number,Method with null parameter(name));

this.building =建筑;
this.number = number;

$ b @Override
public boolean equals(final Object otherObj){
if((otherObj == null)||!(otherObj instanceof Room)){
返回false;
}
//房间可以由它的号码和它所属的建筑物唯一标识;通常我会在任何情况下使用UUID,但这只是为了说明getId()
的使用情况。最终房间其他=(房间)otherObj;
返回新的EqualsBuilder()。append(getNumber(),other.getNumber())
.append(getBuilding()。getId(),other.getBuilding()。getId())
.isEquals();
//假设Building.id被注解为@Access(value = AccessType.PROPERTY)
}

public Building getBuilding(){
return building;
}


public Integer getId(){
return id;
}

public String getNumber(){
return number;

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

public void setCapacity(整数容量){
this.capacity = capacity;
}

//无编号,建筑物或编号的设置者

}

添加到此列表的其他建议不仅仅是欢迎...


$ b

更新



由于阅读这篇文章我调整了我实施eq / hC的方式:


  • 如果有不可变的简单商业密钥可用:在所有其他情况下使用
  • :使用uuid


解决方案

JPA 2.0规范表示:



  • 实体类必须具有无参数构造函数。它也可能有其他的构造函数。无参数构造函数必须是公共的或保护的。

  • 实体类必须是顶级类。枚举或接口不能被指定为实体

  • 实体类不能是final。没有方法或实体变量的实体类可能是最终的。

  • 如果一个实体实例要作为一个分离的对象被传递(例如,通过一个远程接口),实体类必须实现Serializable接口。
  • 抽象类和具体类都可以是实体。实体可以扩展非实体类以及实体类,非实体类可以扩展实体类。

据我所知,规范不包含有关实体的equals和hashCode方法的实现的要求,仅针对主键类和映射键。


I've been working with JPA (implementation Hibernate) for some time now and each time I need to create entities I find myself struggling with issues as AccessType, immutable properties, equals/hashCode, ... .
So I decided to try and find out the general best practice for each issue and write this down for personal use.
I would not mind however for anyone to comment on it or to tell me where I'm wrong.

Entity Class

  • implement Serializable

    Reason: 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.

Constructors

  • create a constructor with all required fields of the entity

    Reason: A constructor should always leave the instance created in a sane state.

  • besides this constructor: have a package private default constructor

    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.

Fields/Properties

  • Use field access in general and property access when needed

    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

  • Omit setters for immutable fields (not required for access type field)

  • properties may be private
    Reason: I once heard that protected is better for (Hibernate) performance but all I can find on the web is: 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.

Equals/hashCode

  • Never use a generated id if this id is only set when persisting the entity
  • By preference: use immutable values to form a unique Business Key and use this to test equality
  • if a unique Business Key is not available use a non-transient UUID which is created when the entity is initialised; See this great article for more information.
  • never refer to related entities (ManyToOne); if this entity (like a parent entity) needs to be part of the Business Key then compare the ID's only. Calling getId() on a proxy will not trigger the loading of the entity, as long as you're using property access type.

Example Entity

@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

}

Other suggestions to add to this list are more than welcome...

UPDATE

Since reading this article I have adapted my way of implementing eq/hC:

  • if an immutable simple business key is available: use that
  • in all other cases: use a uuid

解决方案

The JPA 2.0 Specification states that:

  • The entity class must have a no-arg constructor. It may have other constructors as well. The no-arg constructor must be public or protected.
  • The entity class must a be top-level class. An enum or interface must not be designated as an entity.
  • The entity class must not be final. No methods or persistent instance variables of the entity class may be final.
  • If an entity instance is to be passed by value as a detached object (e.g., through a remote interface), the entity class must implement the Serializable interface.
  • Both abstract and concrete classes can be entities. Entities may extend non-entity classes as well as entity classes, and non-entity classes may extend entity classes.

The specification contains no requirements about the implementation of equals and hashCode methods for entities, only for primary key classes and map keys as far as I know.

这篇关于创建完美的JPA实体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆