Od jakiegoś czasu pracuję z JPA (implementacja Hibernacja) i za każdym razem, gdy muszę tworzyć encje, mam problemy z AccessType, niezmiennymi właściwościami, equals / hashCode, ....
Postanowiłem więc znaleźć ogólną najlepszą praktykę dla każdego numeru i zapisać ją na własny użytek.
Nie miałbym jednak nic przeciwko komentowaniu go lub mówieniu, gdzie się mylę.
Klasa podmiotu
implementować Serializable
Powód: specyfikacja mówi, że musisz, ale niektórzy dostawcy JPA tego nie egzekwują. Hibernacja jako dostawca JPA nie wymusza tego, ale może zawieść gdzieś głęboko w brzuchu z ClassCastException, jeśli Serializable nie został zaimplementowany.
Konstruktory
utwórz konstruktor ze wszystkimi wymaganymi polami encji
Powód: Konstruktor powinien zawsze pozostawić instancję utworzoną w stanie normalnym.
poza tym konstruktorem: mieć pakiet prywatny konstruktor domyślny
Powód: Domyślny konstruktor jest wymagany, aby Hibernacja zainicjowała jednostkę; private jest dozwolone, ale widoczność pakietu prywatnego (lub publicznego) jest wymagana do generowania proxy środowiska wykonawczego i efektywnego wyszukiwania danych bez oprzyrządowania z kodem bajtowym.
Pola / właściwości
Użyj ogólnego dostępu do pola i dostępu do nieruchomości w razie potrzeby
Powód: jest to prawdopodobnie najbardziej dyskusyjna kwestia, ponieważ nie ma jasnych i przekonujących argumentów dla jednego lub drugiego (dostęp do nieruchomości a dostęp do pola); jednak dostęp do pól wydaje się być ulubionym ze względu na bardziej przejrzysty kod, lepszą enkapsulację i brak potrzeby tworzenia ustawień dla niezmiennych pól
Pomiń setery dla niezmiennych pól (niewymagane dla pola typu dostępu)
- właściwości mogą być prywatne
Powód: Kiedyś słyszałem, że funkcja chroniona jest lepsza pod kątem (Hibernacji) wydajności, ale wszystko, co mogę znaleźć w Internecie, to: Hibernacja może uzyskać dostęp do publicznych, prywatnych i chronionych metod akcesorów, a także bezpośrednio do pól publicznych, prywatnych i chronionych . Wybór należy do Ciebie i możesz dopasować go do swojego projektu aplikacji.
Equals / hashCode
- Nigdy nie używaj wygenerowanego identyfikatora, jeśli ten identyfikator jest ustawiony tylko podczas utrwalania encji
- Preferencje: użyj niezmiennych wartości, aby utworzyć unikalny klucz biznesowy i użyj go do przetestowania równości
- jeżeli unikalny Klucz Biznesowy nie jest dostępny, należy użyć przejściowego UUID, który jest tworzony podczas inicjowania jednostki; Zobacz ten świetny artykuł, aby uzyskać więcej informacji.
- nigdy nie odnoszą się do powiązanych podmiotów (ManyToOne); jeśli ten podmiot (podobnie jak podmiot nadrzędny) musi być częścią klucza biznesowego, porównaj tylko identyfikatory. Wywołanie getId () na proxy nie spowoduje załadowania encji, o ile korzystasz z typu dostępu do właściwości .
Przykładowa jednostka
@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
}
Inne sugestie dotyczące dodania do tej listy są mile widziane ...
AKTUALIZACJA
Od czasu przeczytania tego artykułu dostosowałem swój sposób implementacji eq / hC:
- jeśli dostępny jest niezmienny prosty klucz biznesowy: użyj tego
- we wszystkich innych przypadkach: użyj UUID
final
(sądząc po twoim pominięciu seterów, zgaduję, że ty też).
notNull
pochodzi?