Estou trabalhando com o JPA (implementação Hibernate) há algum tempo e, toda vez que preciso criar entidades, me deparei com problemas como AccessType, propriedades imutáveis, equals / hashCode, ....
Por isso, decidi tentar descobrir as melhores práticas gerais para cada problema e anotá-las para uso pessoal.
No entanto, eu não me importaria que alguém comentasse ou me dissesse onde estou errado.
Classe de entidade
implementar Serializable
Motivo: a especificação diz que você precisa, mas alguns provedores de JPA não impõem isso. O hibernar como provedor de JPA não impõe isso, mas pode falhar em algum lugar profundo no estômago com ClassCastException, se Serializable não tiver sido implementado.
Construtores
crie um construtor com todos os campos obrigatórios da entidade
Razão: Um construtor deve sempre deixar a instância criada em um estado sadio.
além deste construtor: tenha um construtor padrão privado do pacote
Razão: O construtor padrão é necessário para o Hibernate inicializar a entidade; private é permitido, mas a visibilidade do pacote privado (ou público) é necessária para a geração do proxy de tempo de execução e recuperação eficiente de dados sem a instrumentação de bytecode.
Campos / Propriedades
Use o acesso ao campo em geral e acesso à propriedade quando necessário
Razão: esta é provavelmente a questão mais discutível, pois não há argumentos claros e convincentes para um ou outro (acesso à propriedade versus acesso ao campo); no entanto, o acesso ao campo parece ser o favorito geral por causa do código mais claro, melhor encapsulamento e não há necessidade de criar setters para campos imutáveis
Omita setters para campos imutáveis (não é necessário para o campo de tipo de acesso)
- as propriedades podem ser privadas
Motivo: Uma vez ouvi dizer que protegido é melhor para o desempenho (Hibernate), mas tudo o que posso encontrar na web é: O Hibernate pode acessar métodos de acessador públicos, privados e protegidos, bem como campos públicos, privados e protegidos diretamente . A escolha é sua e você pode combiná-la para se adequar ao design do aplicativo.
Equals / hashCode
- Nunca use um ID gerado se esse ID estiver definido apenas ao persistir na entidade
- Por preferência: use valores imutáveis para formar uma Chave de Negócios exclusiva e use-a para testar a igualdade
- se uma Chave Comercial exclusiva não estiver disponível, use um UUID não transitório, criado quando a entidade é inicializada; Veja este ótimo artigo para obter mais informações.
- nunca se refira a entidades relacionadas (ManyToOne); se essa entidade (como uma entidade pai) precisar fazer parte da Chave de negócios, compare apenas os IDs. Chamar getId () em um proxy não acionará o carregamento da entidade, desde que você esteja usando o tipo de acesso à propriedade .
Entidade de exemplo
@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
}
Outras sugestões para adicionar a esta lista são mais que bem-vindas ...
ATUALIZAR
Desde a leitura deste artigo , adaptei minha maneira de implementar o eq / hC:
- se uma chave comercial simples imutável estiver disponível: use esse
- em todos os outros casos: use um uuid
final
(a julgar pela sua omissão de levantadores, acho que você também).
notNull
vem?