java - primary - padrão hibernate




Anotações do Hibernate-Qual é o melhor acesso de campo ou propriedade? (17)

AccessType.PROPERTY: A implementação de persistência do EJB carregará o estado em sua classe por meio dos métodos "setter" do JavaBean e recuperará o estado de sua classe usando os métodos "getter" do JavaBean. Este é o padrão.

AccessType.FIELD: o estado é carregado e recuperado diretamente dos campos da sua turma. Você não precisa escrever "getters" e "setters" do JavaBean.

Esta questão é de alguma forma relacionada à questão da colocação de anotações do Hibernate .

Mas eu quero saber qual é melhor ? Acesso via propriedades ou acesso via campos? Quais são as vantagens e desvantagens de cada um?


Acho que anotar a propriedade é melhor porque a atualização dos campos interrompe diretamente o encapsulamento, mesmo quando o ORM faz isso.

Aqui está um ótimo exemplo de onde ele vai te queimar: você provavelmente quer suas anotações para validação e persistência do hibernate no mesmo lugar (campos ou propriedades). Se você quiser testar suas validações validadas pelo validador de hibernação que estão anotadas em um campo, você não pode usar uma simulação de sua entidade para isolar seu teste de unidade apenas com o validador. Ai



Aqui está uma situação em que você tem que usar os acessadores de propriedade. Imagine que você tenha uma classe abstrata GENERIC com muita bondade de implementação para herdar em 8 subclasses concretas:

public abstract class Foo<T extends Bar> {

    T oneThing;
    T anotherThing;

    // getters and setters ommited for brevity

    // Lots and lots of implementation regarding oneThing and anotherThing here
 }

Agora exatamente como você deve anotar essa classe? A resposta é que VOCÊ NÃO PODE anotá-lo com acesso de campo ou de propriedade porque você não pode especificar a entidade de destino neste momento. Você tem que anotar as implementações concretas. Mas como as propriedades persistentes são declaradas nesta superclasse, você DEVE usar o acesso à propriedade nas subclasses.

O acesso ao campo não é uma opção em um aplicativo com superclasses genéricas abstratas.


Deixe-me tentar resumir as razões mais importantes para escolher o acesso baseado em campo. Se você quiser mergulhar mais fundo, leia este artigo no meu blog: Estratégias de acesso no JPA e no Hibernate - Qual é o melhor acesso de campo ou propriedade?

O acesso baseado em campo é de longe a melhor opção. Aqui estão 5 razões para isso:

Razão 1: melhor legibilidade do seu código

Se você usar o acesso baseado em campo, anote os atributos da sua entidade com as anotações de mapeamento. Colocando a definição de todos os atributos da entidade no topo da sua classe, você obtém uma visão relativamente compacta de todos os atributos e seus mapeamentos.

Motivo 2: omitir métodos getter ou setter que não devem ser chamados por seu aplicativo

Outra vantagem do acesso baseado em campo é que seu provedor de persistência, por exemplo, Hibernate ou EclipseLink, não usa os métodos getter e setter de seus atributos de entidade. Isso significa que você não precisa fornecer nenhum método que não deva ser usado por seu código comercial. Esse é, na maioria das vezes, o caso dos métodos setter de atributos de chave primária gerados ou colunas de versão. Seu provedor de persistência gerencia os valores desses atributos e você não deve defini-los programaticamente.

Razão 3: Implementação flexível dos métodos getter e setter

Como seu provedor de persistência não chama os métodos getter e setter, eles não são obrigados a atender a nenhum requisito externo. Você pode implementar esses métodos da maneira que quiser. Isso permite implementar regras de validação específicas de negócios, acionar lógica de negócios adicional ou converter o atributo de entidade em um tipo de dados diferente.

Você pode, por exemplo, usar isso para envolver uma associação ou atributo opcional em um Java Optional .

Razão 4: Não há necessidade de marcar métodos de utilidade como @Transient

Outro benefício da estratégia de acesso baseada em campo é que você não precisa anotar seus métodos de utilitário com @Transient . Esta anotação informa ao seu provedor de persistência que um método ou atributo não faz parte do estado persistente da entidade. E como, com o acesso de tipo de campo, o estado persistente é definido pelos atributos de sua entidade, sua implementação de JPA ignora todos os métodos de sua entidade.

Razão 5: Evite erros ao trabalhar com proxies

O Hibernate usa proxies para associações de um para o outro, para que possa controlar a inicialização dessas associações. Essa abordagem funciona bem em quase todas as situações. Mas apresenta uma armadilha perigosa se você usar o acesso baseado em propriedade.

Se você usar o acesso baseado em propriedade, o Hibernate inicializará os atributos do objeto proxy quando você chamar o método getter. Esse é sempre o caso se você usar o objeto proxy em seu código comercial. Mas muitas implementações equals e hashCode acessam os atributos diretamente. Se esta for a primeira vez que você acessa qualquer um dos atributos de proxy, esses atributos ainda não serão inicializados.


Eu penso sobre isso e eu escolho o método accesor

porque?

porque campo e methos accesor é o mesmo, mas se depois eu precisar de alguma lógica no campo de carga, salvo salvar todas as anotações colocadas nos campos

Saudações

Grubhart


Eu prefiro o acesso de campo, porque dessa forma eu não sou forçado a fornecer getter / setter para cada propriedade.

Uma pesquisa rápida pelo Google sugere que o acesso ao campo é a maioria (por exemplo, http://java.dzone.com/tips/12-feb-jpa-20-why-accesstype ).

Acredito que o acesso ao campo é o idioma recomendado pela Spring, mas não consigo encontrar uma referência para comprovar isso.

Há uma questão SO relacionada que tentou medir o desempenho e chegou à conclusão de que "não há diferença".


Eu prefiro os acessadores, desde que eu possa adicionar alguma lógica de negócios aos meus acessadores sempre que eu precisar. Aqui está um exemplo:

@Entity
public class Person {

  @Column("nickName")
  public String getNickName(){
     if(this.name != null) return generateFunnyNick(this.name);
     else return "John Doe";
  }
}

Além disso, se você jogar outras libs no mix (como alguma biblioteca de conversão JSON ou BeanMapper ou Dozer ou outro lib de mapeamento / clonagem de bean baseado nas propriedades getter / setter) você terá a garantia de que a lib está sincronizada com a persistência gerente (ambos usam o getter / setter).


Eu recomendaria fortemente o acesso ao campo e NOT anotações sobre os getters (acesso à propriedade) se você quiser fazer algo mais nos setters do que apenas definir o valor (por exemplo, criptografia ou cálculo).

O problema com o acesso à propriedade é que os setters também são chamados quando o objeto é carregado. Isso funcionou bem para mim por muitos meses até que quiséssemos introduzir a criptografia. Em nosso caso de uso, queríamos criptografar um campo no setter e descriptografá-lo no getter. O problema agora com o acesso à propriedade era que quando o Hibernate carregava o objeto, ele também chamava o setter para preencher o campo e, assim, estava criptografando o valor criptografado novamente. Este post também menciona isso: Java Hibernate: Diferente comportamento da função de conjunto de propriedades dependendo de quem está chamando

Isso me causou dores de cabeça até que me lembrei da diferença entre o acesso ao campo e o acesso à propriedade. Agora eu mudei todas as minhas anotações do acesso à propriedade para o acesso ao campo e funciona bem agora.


Eu tenho a tendência de preferir e usar os acessadores de propriedade:

  • Eu posso adicionar lógica se houver necessidade (como mencionado na resposta aceita).
  • isso me permite chamar foo.getId() sem inicializar um proxy (importante ao usar o Hibernate, até que o HHH-3718 seja resolvido).

Recua:

  • Isso torna o código menos legível, você tem, por exemplo, para procurar uma classe inteira para ver se há @Transient por lá.


Existem argumentos para ambos, mas a maioria deles se origina de certos requisitos do usuário "e se você precisar adicionar lógica para" ou "xxxx quebra o encapsulamento". No entanto, ninguém realmente comentou sobre a teoria e deu um argumento devidamente fundamentado.

O que o Hibernate / JPA realmente está fazendo quando persiste um objeto - bem, ele está persistindo no STATE do objeto. Isso significa armazená-lo de uma maneira que possa ser facilmente reproduzido.

O que é encapsulamento? Encapsulamentos significa encapsular os dados (ou estados) com uma interface que o aplicativo / cliente pode usar para acessar os dados com segurança - mantendo-os consistentes e válidos.

Pense nisso como o MS Word. O MS Word mantém um modelo do documento na memória - os documentos STATE. Ele apresenta uma interface que o usuário pode usar para modificar o documento - um conjunto de botões, ferramentas, comandos de teclado etc. No entanto, quando você opta por persistir (Salvar) esse documento, ele salva o estado interno, não o conjunto de pressionamentos de tecla e cliques do mouse usados ​​para gerá-lo.

Salvar o estado interno do objeto NÃO quebra o encapsulamento - caso contrário, você não entende realmente o que significa encapsulamento e por que ele existe. É exatamente como a serialização de objetos.

Por esta razão, NA MAIORIA DOS CASOS, é apropriado persistir os CAMPOS e não os ACESSÓRIOS. Isso significa que um objeto pode ser recriado com precisão do banco de dados exatamente da maneira como foi armazenado. Ele não deve precisar de nenhuma validação, porque isso foi feito no original quando foi criado e antes de ser armazenado no banco de dados (a menos que, Deus me livre, você esteja armazenando dados inválidos no banco de dados !!!!). Da mesma forma, não deve haver necessidade de calcular valores, pois eles já foram calculados antes do objeto ser armazenado. O objeto deve ficar exatamente como antes de ser salvo. De fato, adicionando coisas adicionais aos getters / setters você está aumentando o risco de recriar algo que não é uma cópia exata do original.

Claro, essa funcionalidade foi adicionada por um motivo. Pode haver alguns casos de uso válidos para persistir os acessadores, no entanto, eles normalmente serão raros. Um exemplo pode ser que você queira evitar a persistência de um valor calculado, embora possa querer fazer a pergunta por que não calcula o valor sob demanda no getter do valor ou inicializa-o preguiçosamente no getter. Pessoalmente, não consigo pensar em nenhum bom caso de uso, e nenhuma das respostas aqui fornece uma resposta de "Engenharia de Software".


Normalmente beans são POJO, então eles têm acessadores de qualquer maneira.

Então a questão não é "qual é o melhor?", Mas simplesmente "quando usar o acesso de campo?". E a resposta é "quando você não precisa de setter / getter para o campo!".


Outro ponto a favor do acesso de campo é que, do contrário, você é forçado a expor setters para coleções, o que, para mim, é uma má idéia, pois alterar a instância de coleção persistente para um objeto não gerenciado pelo Hibernate definitivamente quebrará a consistência dos dados.

Então eu prefiro ter coleções como campos protegidos inicializados para esvaziar implementações no construtor padrão e expor apenas seus getters. Então, somente operações gerenciadas como clear() , remove() , removeAll() etc são possíveis e nunca tornarão o Hibernate inconsciente de mudanças.


Por padrão, os provedores JPA acessam os valores dos campos de entidade e mapeiam esses campos para as colunas do banco de dados usando os métodos de acessador (getter) e mutator (setter) da propriedade JavaBean da entidade. Como tal, os nomes e tipos dos campos privados em uma entidade não importam para o JPA. Em vez disso, o JPA examina apenas os nomes e os tipos de retorno dos acessores da propriedade JavaBean. Você pode alterar isso usando a anotação @javax.persistence.Access , que permite especificar explicitamente a metodologia de acesso que o provedor JPA deve empregar.

@Entity
@Access(AccessType.FIELD)
public class SomeEntity implements Serializable
{
...
}

As opções disponíveis para o enum AccessType são PROPERTY (o padrão) e FIELD. Com PROPERTY, o provedor obtém e define valores de campo usando os métodos de propriedade JavaBean. O FIELD faz com que o provedor obtenha e defina valores de campo usando os campos da instância. Como prática recomendada, você deve se ater ao padrão e usar as propriedades do JavaBean, a menos que tenha um motivo convincente para fazer o contrário.

Você pode colocar essas anotações de propriedades nos campos privados ou nos métodos de acesso público. Se você usar AccessType.PROPERTY (padrão) e anotar os campos privados em vez dos acessadores JavaBean, os nomes de campos deverão corresponder aos nomes de propriedade JavaBean. No entanto, os nomes não precisam corresponder se você anotar os acessadores JavaBean. Da mesma forma, se você usar AccessType.FIELD e anotar os acessadores JavaBean em vez dos campos, os nomes dos campos também deverão corresponder aos nomes das propriedades JavaBean. Nesse caso, eles não precisam corresponder se você anotar os campos. É melhor ser consistente e anotar os acessadores JavaBean para AccessType.PROPERTY e os campos para AccessType.FIELD .

É importante que você nunca misture anotações de propriedade JPA e anotações de campo JPA na mesma entidade. Isso resulta em um comportamento não especificado e é muito provável que cause erros.


Sou a favor de assessores de campo. O código é muito mais limpo. Todas as anotações podem ser colocadas em uma seção de uma classe e o código é muito mais fácil de ler.

Eu encontrei um outro problema com os acessadores de propriedade: se você tem métodos getXYZ em sua classe que NÃO são anotados como associados a propriedades persistentes, o hibernate gera sql para tentar obter essas propriedades, resultando em algumas mensagens de erro muito confusas. Duas horas desperdiçadas. Eu não escrevi este código; Eu sempre usei assessores de campo no passado e nunca me deparo com esse problema.

Versões de hibernação usadas neste aplicativo:

<!-- hibernate -->
<hibernate-core.version>3.3.2.GA</hibernate-core.version>
<hibernate-annotations.version>3.4.0.GA</hibernate-annotations.version>
<hibernate-commons-annotations.version>3.1.0.GA</hibernate-commons-annotations.version>
<hibernate-entitymanager.version>3.4.0.GA</hibernate-entitymanager.version>

Já estamos lá

Essa é uma apresentação antiga, mas Rod sugere que a anotação sobre o acesso à propriedade incentiva modelos de domínio anêmico e não deve ser a maneira "padrão" de anotar.





annotations