질문 : 완벽한 JPA 엔티티 만들기 [닫기]
나는 JPA (implementation Hibernate)로 한동안 작업 해 왔으며 엔티티를 생성해야 할 때마다 AccessType, 불변 속성, equals / hashCode, ... 등의 문제로 어려움을 겪고 있습니다.
그래서 저는 각 문제에 대한 일반적인 모범 사례를 찾아서 개인적으로 사용하기로 결정했습니다.
그러나 나는 누구든지 그것에 대해 언급하거나 내가 틀린 곳을 말해도 괜찮습니다.
Entity Class
- 직렬화 가능 구현:
- 이유 : 사양에 따라야한다고 명시되어 있지만 일부 JPA 공급자는이를 적용하지 않습니다. JPA 공급자로서 Hibernate는 이것을 강제하지 않지만 Serializable이 구현되지 않은 경우 ClassCastException으로 위장 깊은 곳에서 실패 할 수 있습니다.
생성자
- 엔터티의 모든 필수 필드를 사용하여 생성자를 생성합니다.
- 이유 : 생성자는 항상 생성 된 인스턴스를 정상 상태로 두어야합니다.
- 이 생성자 외에 : 패키지 전용 기본 생성자 를 갖습니다.
- 이유 : Hibernate가 엔티티를 초기화하도록하려면 기본 생성자가 필요합니다. private은 허용되지만 런타임 프록시 생성 및 바이트 코드 계측없이 효율적인 데이터 검색을 위해서는 패키지 개인 (또는 공용) 가시성이 필요합니다.
Fields/Properties
- 일반적으로 필드 액세스를 사용하고 필요할 때 속성 액세스를 사용합니다.
- 이유 : 둘 중 하나에 대해 명확하고 설득력있는 주장이 없기 때문에 아마도 가장 논쟁의 여지가있는 문제 일 것입니다 (속성 액세스 대 필드 액세스). 그러나 필드 액세스는 더 명확한 코드, 더 나은 캡슐화 및 불변 필드에 대한 setter를 만들 필요가 없기 때문에 일반적으로 선호되는 것 같습니다.
- 변경 불가능한 필드에 대한 setter 생략 (액세스 유형 필드에는 필요하지 않음)
- 속성은 비공개 일 수 있습니다.
- 이유 : 한때 protected가 (Hibernate) 성능에 더 좋다고 들었지만 웹에서 찾을 수있는 것은 Hibernate가 public, private 및 protected 접근 자 메서드뿐만 아니라 public, private 및 protected 필드에 직접 액세스 할 수 있다는 것입니다. 선택은 귀하에게 달려 있으며 귀하의 애플리케이션 설계에 맞게 선택할 수 있습니다.
Equals/hashCode
- 엔티티를 유지할 때만이 ID가 설정된 경우 생성 된 ID를 사용하지 마십시오.
- 기본 설정 : 고유 한 비즈니스 키를 형성하기 위해 변경 불가능한 값을 사용하고이를 사용하여 동등성을 테스트합니다.
- 고유 한 비즈니스 키를 사용할 수없는 경우 엔터티가 초기화 될 때 생성되는 비 일시적 UUID를 사용합니다. 자세한 내용은 이 훌륭한 기사 를 참조하십시오.
- 관련 엔티티 (ManyToOne)를 참조 하지 마십시오. 이 엔티티 (예 : 상위 엔티티)가 비즈니스 키의 일부 여야하는 경우 ID 만 비교하십시오. 프록시에서 getId ()를 호출해도 속성 액세스 유형을 사용하는 한 엔티티로드가 트리거되지 않습니다.
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
}
이 목록에 추가 할 다른 제안은 환영합니다.
최신 정보
이 기사를 읽은 후 eq / hC를 구현하는 방식을 수정했습니다.
- 변경 불가능한 단순 비즈니스 키를 사용할 수있는 경우 :
- 다른 모든 경우 : uuid 사용
답변
몇 가지 핵심 사항에 답하려고 노력할 것입니다. 이것은 여러 주요 응용 프로그램을 포함하여 긴 Hibernate / 지속성 경험에서 비롯된 것입니다.
엔티티 클래스 : Serializable 구현?
키 는 Serializable을 구현해야합니다. HttpSession으로 이동하거나 RPC / Java EE에 의해 유선으로 전송되는 항목은 Serializable을 구현해야합니다. 다른 것들 : 그다지 많지 않습니다. 중요한 일에 시간을 투자하십시오.
생성자 : 엔티티의 모든 필수 필드로 생성자를 만드시겠습니까?
응용 프로그램 논리의 생성자에는 엔터티를 만들 때 항상 알려진 몇 가지 중요한 "외래 키"또는 "유형 / 종류"필드 만 있어야합니다. 나머지는 setter 메서드를 호출하여 설정해야합니다.
생성자에 너무 많은 필드를 넣지 마십시오. 생성자는 편리해야하며 객체에 기본적인 온 전성을 부여해야합니다. 이름, 유형 및 / 또는 부모는 모두 일반적으로 유용합니다.
애플리케이션 규칙 (오늘)이 고객에게 주소를 요구하는 경우 OTOH는 세터에게 맡기십시오. 이것이 "약한 규칙"의 예입니다. 다음 주에 세부 정보 입력 화면으로 이동하기 전에 고객 개체를 만들고 싶습니까? 자신을 넘어 뜨리지 말고 알려지지 않거나 불완전하거나 "부분적으로 입력 된"데이터에 대한 가능성을 남겨 두십시오.
생성자 : 또한 개인 기본 생성자를 패키지합니까?
예, 그러나 패키지 비공개보다는 '보호됨'을 사용하십시오. 서브 클래 싱은 필요한 내부가 보이지 않을 때 정말 고통 스럽습니다.
필드 / 속성
Hibernate 및 인스턴스 외부에서 'property'필드 액세스를 사용하십시오. 인스턴스 내에서 필드를 직접 사용하십시오. 이유 : Hibernate를위한 가장 간단하고 기본적인 방법 인 표준 리플렉션이 작동하도록합니다.
애플리케이션에 '불변'필드에 관해서는 Hibernate가 여전히 이들을로드 할 수 있어야합니다. 이러한 메서드를 '비공개'로 만들거나 주석을 달아 애플리케이션 코드가 원치 않는 액세스를 만드는 것을 방지 할 수 있습니다.
참고 : equals () 함수를 작성할 때 'other'인스턴스의 값에 대해 getter를 사용하십시오! 그렇지 않으면 프록시 인스턴스에서 초기화되지 않은 / 빈 필드를 누르게됩니다.
보호 된 것이 (최대 절전 모드) 성능에 더 낫습니까?
있을 것 같지 않게.
Equals / HashCode?
이것은 엔티티가 저장되기 전에 작업하는 것과 관련이 있습니다. 이는 어려운 문제입니다. 불변 값에 대한 해싱 / 비교? 대부분의 비즈니스 애플리케이션에는 없습니다.
고객은 주소를 변경하거나 비즈니스 이름 등을 변경할 수 있지만 일반적이지 않지만 발생합니다. 데이터가 올바르게 입력되지 않은 경우에도 수정이 가능해야합니다.
일반적으로 불변으로 유지되는 몇 가지 사항은 Parenting 및 Type / Kind입니다. 일반적으로 사용자는이를 변경하지 않고 레코드를 다시 만듭니다. 그러나 이것들은 엔티티를 고유하게 식별하지 않습니다!
따라서 길고 짧게 주장 된 "불변"데이터는 사실이 아닙니다. 기본 키 / ID 필드는 이러한 보장 된 안정성 및 불변성을 제공하기 위해 정확한 목적으로 생성됩니다.
A) "빈번하게 변경되지 않는 필드"를 비교 / 해시하는 경우 UI에서 "변경 / 바인딩 된 데이터"로 작업하거나 B) "로 작업 할 때 비교 및 해싱 및 요청 처리 작업 단계에 대한 필요성을 계획하고 고려해야합니다. 저장되지 않은 데이터 ", ID를 비교 / 해시하는 경우.
Equals / HashCode-고유 한 비즈니스 키를 사용할 수없는 경우 엔터티가 초기화 될 때 생성되는 비 일시적 UUID를 사용합니다.
예, 이것은 필요할 때 좋은 전략입니다. UUID는 성능 측면에서 무료가 아니며 클러스터링은 일을 복잡하게 만듭니다.
Equals / HashCode-관련 항목을 참조하지 않습니다.
"관련 엔티티 (예 : 상위 엔티티)가 비즈니스 키의 일부 여야하는 경우 삽입 불가능하고 업데이트 불가능한 필드를 추가하여 상위 ID (ManytoOne JoinColumn과 동일한 이름 사용)를 저장하고이 ID를 동등성 검사에 사용하십시오. "
좋은 조언처럼 들립니다.
도움이 되었기를 바랍니다!
출처 : https://stackoverflow.com/questions/6033905/create-the-perfect-jpa-entity
'프로그래밍 언어 > JAVA' 카테고리의 다른 글
Java에서 Base64 데이터 디코딩 하는 방법 (0) | 2021.12.12 |
---|---|
java.util.Date를 java.time.LocalDate로 변환하는 방법 (0) | 2021.12.09 |
Java 중첩 for문 벗어나는 방법 (0) | 2021.12.08 |
자바 스크립트에서 난수 생성기 시드 (0) | 2021.12.07 |
JavaScript 변수명에 $를 붙이는 이유 (0) | 2021.12.07 |