Tôi đã làm việc với JPA (triển khai Hibernate) một thời gian rồi và mỗi lần tôi cần tạo các thực thể tôi thấy mình phải vật lộn với các vấn đề như AccessType, thuộc tính bất biến, bằng / hashCode, ....
Vì vậy, tôi quyết định thử và tìm hiểu thực tiễn tốt nhất chung cho từng vấn đề và viết nó ra để sử dụng cá nhân.
Tuy nhiên, tôi sẽ không phiền nếu ai đó nhận xét về nó hoặc cho tôi biết tôi sai ở đâu.
Lớp thực thể
thực hiện nối tiếp
Lý do: Thông số kỹ thuật nói rằng bạn phải làm, nhưng một số nhà cung cấp JPA không thực thi điều này. Hibernate là nhà cung cấp JPA không thực thi điều này, nhưng nó có thể thất bại ở đâu đó sâu trong bụng với ClassCastException, nếu serializable chưa được triển khai.
Người xây dựng
tạo một hàm tạo với tất cả các trường bắt buộc của thực thể
Lý do: Một constructor phải luôn luôn để cá thể được tạo ở trạng thái lành mạnh.
bên cạnh hàm tạo này: có một hàm tạo mặc định riêng
Lý do: Trình xây dựng mặc định được yêu cầu để Hibernate khởi tạo thực thể; riêng tư được cho phép nhưng khả năng hiển thị riêng tư (hoặc công khai) là bắt buộc để tạo proxy thời gian chạy và truy xuất dữ liệu hiệu quả mà không cần thiết bị mã byte.
Trường / Thuộc tính
Sử dụng quyền truy cập trường nói chung và quyền truy cập tài sản khi cần thiết
Lý do: đây có lẽ là vấn đề gây tranh cãi nhất vì không có lập luận rõ ràng và thuyết phục cho người này hay người kia (quyền truy cập thuộc tính so với quyền truy cập trường); tuy nhiên, truy cập trường dường như được yêu thích chung vì mã rõ ràng hơn, đóng gói tốt hơn và không cần tạo setters cho các trường không thay đổi
Bỏ qua setters cho các trường không thay đổi (không bắt buộc đối với trường loại truy cập)
- thuộc tính có thể là riêng tư
Lý do: Tôi đã từng nghe nói rằng bảo vệ tốt hơn cho hiệu suất (Hibernate) nhưng tất cả những gì tôi có thể tìm thấy trên web là: Hibernate có thể truy cập trực tiếp các phương thức truy cập công khai, riêng tư và được bảo vệ, cũng như các trường công khai, riêng tư và được bảo vệ . Sự lựa chọn tùy thuộc vào bạn và bạn có thể phù hợp với nó để phù hợp với thiết kế ứng dụng của bạn.
Bằng / hashCode
- Không bao giờ sử dụng id được tạo nếu id này chỉ được đặt khi duy trì thực thể
- Theo sở thích: sử dụng các giá trị bất biến để tạo thành Khóa doanh nghiệp duy nhất và sử dụng giá trị này để kiểm tra tính bằng nhau
- nếu Khóa doanh nghiệp duy nhất không khả dụng, hãy sử dụng UUID không tạm thời được tạo khi thực thể được khởi tạo; Xem bài viết tuyệt vời này để biết thêm thông tin.
- không bao giờ đề cập đến các thực thể liên quan (ManyToOne); nếu thực thể này (như thực thể mẹ) cần là một phần của Khóa doanh nghiệp thì chỉ so sánh ID. Gọi getId () trên proxy sẽ không kích hoạt tải thực thể, miễn là bạn đang sử dụng loại truy cập thuộc tính .
Thực thể ví dụ
@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
}
Các đề xuất khác để thêm vào danh sách này được chào đón nhiều hơn ...
CẬP NHẬT
Kể từ khi đọc bài viết này, tôi đã điều chỉnh cách thực hiện eq / hC:
- nếu có sẵn một khóa doanh nghiệp đơn giản bất biến: sử dụng khóa đó
- trong tất cả các trường hợp khác: sử dụng uuid
final
(đánh giá bằng cách bỏ qua các setters, tôi đoán bạn cũng vậy).
notNull
đến từ đâu?