나는 JPA (implementation Hibernate)와 얼마 동안 일해 왔으며 엔티티를 만들어야 할 때마다 AccessType, 불변 속성, equals / hashCode 등의 문제로 어려움을 겪고있는 것으로 나타났습니다.
그래서 각 문제에 대한 일반적인 모범 사례를 찾아서 개인적인 용도로 작성하기로 결정했습니다.
그러나 나는 누군가 그것에 대해 의견을 말하거나 내가 틀렸다는 것을 말하지 않아도됩니다.
엔터티 클래스
직렬화 가능 구현
이유 : 사양에 따라야하지만 일부 JPA 제공자는이를 강제하지 않습니다. JPA 공급자로서의 최대 절전 모드는 이것을 강제하지 않지만 Serializable이 구현되지 않은 경우 ClassCastException으로 위장 어딘가에서 실패 할 수 있습니다.
생성자
엔터티의 모든 필수 필드로 생성자를 만듭니다.
이유 : 생성자는 항상 인스턴스를 정상 상태로 두어야합니다.
이 생성자 외에 : 패키지 개인 기본 생성자를 가짐
이유 : 기본 생성자는 최대 절전 모드에서 엔티티를 초기화해야합니다. private은 허용되지만 런타임 프록시 생성 및 바이트 코드 계측없이 효율적인 데이터 검색을 위해서는 패키지 private (또는 public) 가시성이 필요합니다.
분야 / 재산
필요한 경우 일반적으로 현장 접근 및 재산 접근
이유 : 하나 또는 다른 하나에 대한 분명하고 설득력있는 주장이 없기 때문에 이것은 아마도 가장 논쟁의 여지가있는 문제 일 것입니다 (속성 액세스 대 필드 액세스). 그러나 더 명확한 코드, 더 나은 캡슐화 및 불변 필드에 대한 세터를 만들 필요가 없기 때문에 필드 액세스가 일반적으로 선호되는 것 같습니다.
변경 불가능한 필드에 대한 세터 생략 (액세스 유형 필드에는 필요하지 않음)
- 속성은 개인이 될 수 있습니다
한번 보호가 더 나은 (최대 절전 모드) 성능이라고 들었하지만 웹에서 찾을 수있는 모든은 다음과 같습니다 : 이유 공공, 민간 및 보호 분야에 직접뿐만 아니라, 최대 절전 모드가 액세스 할 수 공개, 개인 및 방법 접근 보호 . 선택은 당신에게 달려 있으며 당신은 그것을 당신의 어플리케이션 디자인에 맞출 수 있습니다.
같음 / 해시 코드
- 이 ID가 엔티티를 유지할 때만 설정된 경우 생성 된 ID를 사용하지 마십시오
- 기본 설정 : 변경 불가능한 값을 사용하여 고유 한 비즈니스 키를 형성하고이를 사용하여 동등성 테스트
- 고유 한 비즈니스 키를 사용할 수없는 경우 엔터티가 초기화 될 때 생성되는 비 과도 UUID 를 사용하십시오 . 자세한 내용은 이 훌륭한 기사 를 참조하십시오.
- 관련 엔티티 (ManyToOne)를 참조 하지 마십시오 . 이 엔티티 (부모 엔티티와 같은)가 비즈니스 키의 일부 여야하는 경우 ID 만 비교하십시오. 속성 액세스 유형을 사용하는 한 프록시에서 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
}
이 목록에 추가 할 다른 제안은 환영 이상입니다 ...
최신 정보
이 기사를 읽은 후 eq / hC 구현 방법을 조정했습니다.
- 불변의 간단한 비즈니스 키를 사용할 수있는 경우 :
- 다른 모든 경우 : uuid 사용
final
입니다 (세터의 생략으로 판단하면 당신도 그렇게 생각합니다).
notNull
에서 왔습니까?