Essa pergunta está um pouco relacionada à pergunta de posicionamento de anotação do Hibernate .
Mas eu quero saber qual é o melhor ? Acesso via propriedades ou acesso via campos? Quais são as vantagens e desvantagens de cada um?
Essa pergunta está um pouco relacionada à pergunta de posicionamento de anotação do Hibernate .
Mas eu quero saber qual é o melhor ? Acesso via propriedades ou acesso via campos? Quais são as vantagens e desvantagens de cada um?
Respostas:
Prefiro acessadores, pois posso adicionar alguma lógica comercial aos meus acessadores sempre que necessário. 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ê lançar outras bibliotecas na mistura (como algumas bibliotecas de conversão JSON ou BeanMapper ou Dozer ou outra biblioteca de mapeamento / clonagem de beans com base nas propriedades getter / setter), você terá a garantia de que a biblioteca está sincronizada com a persistência gerente (ambos usam o getter / setter).
Existem argumentos para ambos, mas a maioria deles decorre 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 a teoria e deu um argumento devidamente fundamentado.
O que o Hibernate / JPA está realmente fazendo quando persiste em um objeto - bem, ele persiste no estado do objeto. Isso significa armazená-lo de uma maneira que possa ser facilmente reproduzido.
O que é encapsulamento? Encapsulamentos significa encapsular os dados (ou estado) 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. 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ê escolhe persistir (Salvar), o documento salva o estado interno, não o conjunto de teclas pressionadas e cliques do mouse usados para gerá-lo.
Salvar o estado interno do objeto NÃO quebra o encapsulamento - caso contrário, você realmente não entende o que significa encapsulamento e por que ele existe. É exatamente como serialização de objetos.
Por esse motivo, na maioria dos casos, é apropriado persistir nos CAMPOS e não nos ACESSORES. 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 validação, porque isso foi feito no original quando foi criado e antes de ser armazenado no banco de dados (a menos que Deus não permita, você está armazenando dados inválidos no banco de dados !!!!). Da mesma forma, não deve ser necessário calcular valores, pois eles já foram calculados antes do armazenamento do objeto. O objeto deve ter a mesma aparência que antes de ser salvo. De fato, adicionando itens adicionais aos getters / setters, você aumenta o risco de recriar algo que não é uma cópia exata do original.
Obviamente, essa funcionalidade foi adicionada por um motivo. Pode haver alguns casos de uso válidos para persistir os acessadores; no entanto, eles geralmente são raros. Um exemplo pode ser que você deseja evitar a persistência de um valor calculado, embora faça a pergunta por que não o calcula sob demanda no getter do valor ou inicializa preguiçosamente no getter. Pessoalmente, não consigo pensar em nenhum bom caso de uso, e nenhuma das respostas aqui realmente fornece uma resposta "Engenharia de software".
Prefiro o acesso ao campo, porque dessa forma não sou obrigado a fornecer getter / setter para cada propriedade.
Uma pesquisa rápida via 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 pelo Spring, mas não consigo encontrar uma referência para fazer backup disso.
Há uma pergunta relacionada ao SO que tentou medir o desempenho e chegou à conclusão de que "não há diferença".
Aqui está uma situação em que você PRECISA usar acessadores de propriedades. Imagine que você tenha uma classe abstrata GENERIC com muitas vantagens 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 esta classe? A resposta é: VOCÊ NÃO PODE anotá-la com acesso a campos ou propriedades, porque não é possível especificar a entidade de destino neste momento. Você deve anotar as implementações concretas. Mas como as propriedades persistidas 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.
abstract T getOneThing()
, e abstract void setOneThing(T thing)
, e usar o acesso ao campo.
Costumo preferir e usar acessadores de propriedades:
foo.getId()
sem inicializar um proxy (importante ao usar o Hibernate, até que o HHH-3718 seja resolvido).Recua:
@Transient
por aí.Isso realmente depende de um caso específico - ambas as opções estão disponíveis por um motivo. OMI resume-se a três casos:
Eu recomendo fortemente o acesso ao campo e NÃO as anotações nos 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 para mim por muitos meses, até que desejamos introduzir a criptografia. No 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 estava chamando o setter para preencher o campo e, portanto, estava criptografando o valor criptografado novamente. Esta publicação também menciona o seguinte: Java Hibernate: comportamento da função do conjunto de propriedades diferentes, 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 mudei todas as minhas anotações do acesso à propriedade para o acesso ao campo e agora funciona bem.
Prefiro usar o acesso ao campo pelos seguintes motivos:
O acesso à propriedade pode levar a erros muito desagradáveis ao implementar equals / hashCode e referenciar campos diretamente (ao contrário de seus getters). Isso ocorre porque o proxy é inicializado apenas quando os getters são acessados, e um acesso direto ao campo retornaria nulo.
O acesso à propriedade requer que você anote todos os métodos utilitários (por exemplo, addChild / removeChild) como @Transient
.
Com o acesso ao campo, podemos ocultar o campo @Version não expondo um getter. Um getter também pode levar à adição de um setter, e o version
campo nunca deve ser definido manualmente (o que pode levar a problemas muito desagradáveis). Todo o incremento de versão deve ser acionado por meio do bloqueio explícito OPTIMISTIC_FORCE_INCREMENT ou PESSIMISTIC_FORCE_INCREMENT .
version
campo geralmente é útil em situações em que os DTOs são usados em vez de entidades desanexadas.
Acho que anotar a propriedade é melhor porque a atualização de campos quebra diretamente o encapsulamento, mesmo quando o ORM faz isso.
Aqui está um ótimo exemplo de onde ele será gravado: você provavelmente deseja suas anotações para validador de hibernação e persistência no mesmo local (campos ou propriedades). Se você deseja testar as validações ativadas pelo validador de hibernação que estão anotadas em um campo, não é possível usar uma simulação da sua entidade para isolar seu teste de unidade apenas para o validador. Ai.
Eu acredito que o acesso à propriedade vs. o acesso ao campo é sutilmente diferente no que diz respeito à inicialização lenta.
Considere os seguintes mapeamentos para 2 beans básicos:
<hibernate-mapping package="org.nkl.model" default-access="field">
<class name="FieldBean" table="FIELD_BEAN">
<id name="id">
<generator class="sequence" />
</id>
<property name="message" />
</class>
</hibernate-mapping>
<hibernate-mapping package="org.nkl.model" default-access="property">
<class name="PropBean" table="PROP_BEAN">
<id name="id">
<generator class="sequence" />
</id>
<property name="message" />
</class>
</hibernate-mapping>
E os seguintes testes de unidade:
@Test
public void testFieldBean() {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
FieldBean fb = new FieldBean("field");
Long id = (Long) session.save(fb);
tx.commit();
session.close();
session = sessionFactory.openSession();
tx = session.beginTransaction();
fb = (FieldBean) session.load(FieldBean.class, id);
System.out.println(fb.getId());
tx.commit();
session.close();
}
@Test
public void testPropBean() {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
PropBean pb = new PropBean("prop");
Long id = (Long) session.save(pb);
tx.commit();
session.close();
session = sessionFactory.openSession();
tx = session.beginTransaction();
pb = (PropBean) session.load(PropBean.class, id);
System.out.println(pb.getId());
tx.commit();
session.close();
}
Você verá a diferença sutil nas seleções necessárias:
Hibernate:
call next value for hibernate_sequence
Hibernate:
insert
into
FIELD_BEAN
(message, id)
values
(?, ?)
Hibernate:
select
fieldbean0_.id as id1_0_,
fieldbean0_.message as message1_0_
from
FIELD_BEAN fieldbean0_
where
fieldbean0_.id=?
0
Hibernate:
call next value for hibernate_sequence
Hibernate:
insert
into
PROP_BEAN
(message, id)
values
(?, ?)
1
Ou seja, chamar fb.getId()
exige uma seleção, enquanto pb.getId()
não exige .
Deixe-me tentar resumir os motivos mais importantes para escolher o acesso baseado em campo. Se você quiser se aprofundar, leia este artigo no meu blog: Estratégias de acesso no JPA e no Hibernate - Qual é o melhor acesso a campos ou propriedades?
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 seus atributos de entidade com suas anotações de mapeamento. Ao colocar 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.
Razão 2: Omita métodos getter ou setter que não devem ser chamados pelo 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 dos atributos de sua entidade. Isso significa que você não precisa fornecer nenhum método que não deva ser usado pelo seu código comercial. Geralmente, esse é 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 forçados a atender a nenhum requisito externo. Você pode implementar esses métodos da maneira que desejar. Isso permite implementar regras de validação específicas de negócios, acionar lógica de negócios adicionais ou converter o atributo de entidade em um tipo de dados diferente.
Você pode, por exemplo, usá-lo para agrupar uma associação ou atributo opcional em um Java Optional
.
Razão 4: Não há necessidade de marcar métodos utilitários como @Transient
Outro benefício da estratégia de acesso baseado em campo é que você não precisa anotar seus métodos utilitários com @Transient
. Esta anotação informa ao provedor de persistência que um método ou atributo não faz parte do estado persistente da entidade. E como com o acesso por tipo de campo, o estado persistente é definido pelos atributos da sua entidade, sua implementação JPA ignora todos os métodos da sua entidade.
Razão 5: Evite erros ao trabalhar com proxies
O Hibernate usa proxies para associações um-buscadas preguiçosamente, para que ele 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 propriedades.
Se você usar o acesso baseado em propriedades, 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 no seu código comercial. Mas muitas implementações iguais e hashCode acessam os atributos diretamente. Se esta é a primeira vez que você acessa algum dos atributos de proxy, esses atributos ainda não foram inicializados.
Por padrão, os provedores JPA acessam os valores dos campos da entidade e mapeiam esses campos para as colunas do banco de dados usando os métodos acessador de propriedade JavaBean (getter) e mutator (setter) da entidade. Como tal, os nomes e tipos dos campos particulares em uma entidade não importam para a JPA. Em vez disso, o JPA examina apenas os nomes e tipos de retorno dos acessadores de propriedade JavaBean. Você pode alterar isso usando a @javax.persistence.Access
anotação, 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 a enumeração 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. FIELD faz com que o provedor obtenha e defina valores de campo usando os campos da instância. Como prática recomendada, você deve manter o padrão e usar as propriedades JavaBean, a menos que tenha um motivo convincente para fazer o contrário.
Você pode colocar essas anotações de propriedade nos campos particulares 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 dos campos deverão corresponder aos nomes das propriedades 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 apenas ser consistente e anotar os acessadores JavaBean AccessType.PROPERTY
e os campos para
AccessType.FIELD
.
É importante que você nunca deve misturar anotações de propriedade JPA e anotações de campo JPA na mesma entidade. Fazer isso resulta em comportamento não especificado e é muito provável que cause erros.
Essa é uma apresentação antiga, mas Rod sugere que a anotação no acesso à propriedade incentiva modelos de domínio anêmicos e não deve ser a maneira "padrão" de fazer anotações.
Outro ponto a favor do acesso ao campo é que, caso contrário, você será 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.
Portanto, prefiro ter coleções como campos protegidos inicializadas para implementações vazias no construtor padrão e expor apenas seus getters. Operações de então, só conseguiu, como clear()
, remove()
, removeAll()
etc são possíveis que nunca vai fazer Hibernate inconsciente de mudanças.
Eu prefiro campos, mas me deparei com uma situação que parece me forçar a colocar as anotações nos getters.
Com a implementação do Hibernate JPA, @Embedded
parece não funcionar nos campos. Então isso tem que seguir em frente. E uma vez que você coloca isso no getter, as várias @Column
anotações também precisam ir no getter. (Acho que o Hibernate não quer misturar campos e getters aqui.) E uma vez que você coloca @Column
getters em uma classe, provavelmente faz sentido fazer isso o tempo todo.
Eu sou a favor dos acessadores 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.
Encontrei outro problema com os acessadores de propriedades: se você possui métodos getXYZ em sua classe que NÃO estã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 perdidas. Eu não escrevi esse código; Eu sempre usei acessadores de campo no passado e nunca encontrei 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>
Você deve escolher o acesso via campos em vez do acesso via propriedades. Com os campos, você pode limitar os dados enviados e recebidos. Com as propriedades via, você pode enviar mais dados como host e definir denominações G (que a fábrica define a maioria das propriedades no total).
Eu tive a mesma pergunta sobre o tipo de acesso no hibernate e encontrei algumas respostas aqui .
Eu resolvi a inicialização lenta e o acesso ao campo aqui Hibernate um-para-um: getId () sem buscar o objeto inteiro
Criamos beans de entidade e usamos anotações getter. O problema que encontramos é o seguinte: algumas entidades têm regras complexas para algumas propriedades sobre quando podem ser atualizadas. A solução foi ter alguma lógica de negócios em cada setter que determine se o valor real foi alterado ou não e, se houver, se a alteração deve ser permitida. Obviamente, o Hibernate sempre pode definir as propriedades, então acabamos com dois grupos de configuradores. Bonito feio.
Lendo postagens anteriores, também vejo que a referência às propriedades de dentro da entidade pode levar a problemas com o carregamento de coleções.
Resumindo, gostaria de anotar os campos no futuro.
Normalmente, os beans são POJO, portanto, eles têm acessadores de qualquer maneira.
Portanto, a questão não é "qual é o melhor?", Mas simplesmente "quando usar o acesso ao campo?". E a resposta é "quando você não precisa de um setter / getter para o campo!".
Eu estou pensando sobre isso e eu escolho o método accesor
porque?
porque o campo e o methos accesor são os mesmos, mas se mais tarde precisar de alguma lógica no campo de carregamento, salve mover todas as anotações colocadas nos campos
Saudações
Grubhart
Ambos :
A especificação EJB3 requer que você declare anotações no tipo de elemento que será acessado, ou seja, o método getter se você usar o acesso à propriedade, o campo se você usar o acesso ao campo.
https://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#entity-mapping
AccessType.PROPERTY: A implementação de persistência EJB carregará o estado em sua classe através dos métodos JavaBean "setter" e recuperará o estado da sua classe usando os métodos JavaBean "getter". Esse é o padrão.
AccessType.FIELD: O estado é carregado e recuperado diretamente dos campos da sua classe. Você não precisa escrever "getters" e "setters" do JavaBean.