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.
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.
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.
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)
@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:
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.