Я работаю с JPA (реализация Hibernate) уже некоторое время, и каждый раз, когда мне нужно создать сущности, я сталкиваюсь с такими проблемами, как AccessType, неизменяемые свойства, equals / hashCode, ....
Поэтому я решил попытаться найти общие рекомендации по каждому вопросу и записать их для личного использования.
Однако я не против, чтобы кто-то прокомментировал это или сказал мне, где я неправ.
Класс сущности
реализовать Сериализуемый
Причина: в спецификации сказано, что вы должны это сделать, но некоторые провайдеры JPA этого не соблюдают. Hibernate как провайдер JPA не применяет это, но может потерпеть неудачу где-то глубоко в желудке с ClassCastException, если Serializable не был реализован.
Конструкторы
создать конструктор со всеми обязательными полями объекта
Причина: конструктор всегда должен оставлять экземпляр, созданный в нормальном состоянии.
кроме этого конструктора: есть закрытый конструктор по умолчанию для пакета
Причина: конструктор по умолчанию требуется, чтобы Hibernate инициализировал сущность; private разрешен, но для генерации прокси во время выполнения и эффективного извлечения данных без инструментирования байт-кодом требуется частная (или публичная) видимость пакета.
Поля / Свойства
Используйте общий доступ к полю и доступ к свойству при необходимости
Причина: это, вероятно, самая дискуссионная проблема, поскольку нет ясных и убедительных аргументов для одного или другого (доступ к свойству или доступ к полю); однако, доступ к полям, кажется, является общим фаворитом из-за более ясного кода, лучшей инкапсуляции и нет необходимости создавать установщики для неизменных полей
Установщики пропуска для неизменяемых полей (не требуется для поля типа доступа)
- свойства могут быть частными
Причина: я однажды слышал, что защищенный лучше для производительности (Hibernate), но все, что я могу найти в Интернете: Hibernate может получить доступ к общедоступным, частным и защищенным методам доступа, а также к публичным, частным и защищенным полям напрямую , Выбор за вами, и вы можете сопоставить его с дизайном вашего приложения.
Равно / хэш-код
- Никогда не используйте сгенерированный идентификатор, если этот идентификатор установлен только при сохранении объекта
- По предпочтению: используйте неизменяемые значения для формирования уникального бизнес-ключа и используйте его для проверки равенства
- если уникальный бизнес-ключ недоступен, используйте непереходный UUID, который создается при инициализации объекта; Смотрите эту замечательную статью для получения дополнительной информации.
- никогда не ссылаться на связанные объекты (ManyToOne); если этот объект (как родительский объект) должен быть частью бизнес-ключа, сравните только идентификаторы. Вызов 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
берутся?