Annotations Hibernate - Quel est le meilleur accès aux champs ou aux propriétés?

Réponses:

33

Je préfère les accesseurs, car je peux ajouter une logique métier à mes accesseurs quand j'en ai besoin. Voici un exemple:

@Entity
public class Person {

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

De plus, si vous lancez une autre bibliothèque dans le mix (comme une bibliothèque de conversion JSON ou BeanMapper ou Dozer ou une autre bibliothèque de mappage / clonage de bean basée sur les propriétés getter / setter), vous aurez la garantie que la bibliothèque est synchronisée avec la persistance manager (les deux utilisent le getter / setter).

Miguel Ping
la source
17
Notez qu'il s'agit de la manière dont l'ORM accède à vos champs / propriétés et non à votre code d'application. Avec l'accès aux champs, votre méthode getNickName () fonctionnerait exactement comme vous vous y attendez. Il n'en va pas de même si vous utilisez les 'propriétés' persistantes en dehors des getters / setters. C'est là que vous pouvez rencontrer des problèmes d'accès aux propriétés et de chargement différé. Donc non, je ne suis pas d'accord avec cet argument en général. Cependant, la dernière fois que j'ai vérifié, Hibernate avait des problèmes avec l'accès aux champs des champs @Id.
Rob Bygrave
11
cette réponse n'est pas liée à la question. Best answer s par duffymo
Janning
9
il ne devrait y avoir aucune logique métier à l'intérieur des accesseurs. ce n'est pas un comportement évident.
iuriisusuk
Pourquoi cette réponse est-elle indiquée comme correcte? Ce n'est pas vrai que vous pouvez mapper un champ et lui fournir un setter / getter de la même manière.
Lucke
246

Il y a des arguments pour les deux, mais la plupart d'entre eux proviennent de certaines exigences de l'utilisateur "et si vous avez besoin d'ajouter une logique pour", ou "xxxx interrompt l'encapsulation". Cependant, personne n'a vraiment commenté la théorie et donné un argument correctement motivé.

Que fait réellement Hibernate / JPA quand il persiste un objet - eh bien, il persiste dans l'état de l'objet. Cela signifie le stocker de manière à pouvoir être facilement reproduit.

Qu'est-ce que l'encapsulation? Les encapsulations consistent à encapsuler les données (ou l'état) avec une interface que l'application / le client peut utiliser pour accéder aux données en toute sécurité - en les maintenant cohérentes et valides.

Pensez à cela comme MS Word. MS Word maintient un modèle du document en mémoire - les documents STATE. Il présente une interface que l'utilisateur peut utiliser pour modifier le document - un ensemble de boutons, d'outils, de commandes clavier, etc. Cependant, lorsque vous choisissez de conserver (Enregistrer) ce document, il enregistre l'état interne, pas l'ensemble des touches et clics de souris utilisés pour le générer.

L'enregistrement de l'état interne de l'objet NE rompt PAS l'encapsulation - sinon vous ne comprenez pas vraiment ce que signifie l'encapsulation et pourquoi elle existe. C'est exactement comme la sérialisation d'objets.

Pour cette raison, DANS LA PLUPART DES CAS, il convient de conserver les CHAMPS et non les ACCESSOIRES. Cela signifie qu'un objet peut être recréé avec précision à partir de la base de données exactement comme il a été stocké. Il ne devrait avoir besoin d'aucune validation, car cela a été fait sur l'original lors de sa création, et avant qu'il ne soit stocké dans la base de données (sauf si, Dieu nous en préserve, vous stockez des données invalides dans la base de données !!!!). De même, il ne devrait pas être nécessaire de calculer les valeurs, car elles ont déjà été calculées avant le stockage de l'objet. L'objet doit ressembler à ce qu'il était avant d'être enregistré. En fait, en ajoutant des éléments supplémentaires dans les getters / setters, vous augmentez en fait le risque de recréer quelque chose qui n'est pas une copie exacte de l'original.

Bien sûr, cette fonctionnalité a été ajoutée pour une raison. Il peut y avoir des cas d'utilisation valides pour la persistance des accesseurs, mais ils seront généralement rares. Un exemple peut être que vous souhaitez éviter de conserver une valeur calculée, bien que vous souhaitiez peut-être demander pourquoi vous ne la calculez pas à la demande dans le getter de la valeur, ou l'initialisez paresseusement dans le getter. Personnellement, je ne peux penser à aucun bon cas d'utilisation, et aucune des réponses ici ne donne vraiment une réponse "Génie logiciel".

Martin
la source
9
La réponse du génie logiciel est la suivante: l'utilisation d'accesseurs enfreint DRY.
sourcedelica
1
@Martin J'ai une question complémentaire concernant le dernier paragraphe de votre réponse. Vous avez écrit "Un exemple peut être que vous souhaitez éviter de conserver une valeur calculée". Comment éviter de conserver une valeur calculée en ayant un accès basé sur la propriété? Je sais que vous vous disputez pour ne pas le faire, mais je ne comprends pas ici. Pouvez-vous expliquer?
Geek
4
@Geek Maintenant que je l'ai relu, je ne suis pas tout à fait sûr moi-même. Cela fait deux ans que j'ai écrit cette réponse. Je suppose qu'un meilleur exemple pourrait être celui où vous travaillez avec une base de données héritée, et les données sont présentées d'une manière différente de votre modèle d'objet - les accesseurs pourraient fournir un mappage entre les deux.
Martin
23
Un bon cas d'utilisation du mappage des accesseurs est lorsque vous devez ajouter des informations de mappage à des sous-classes de classes d'entités tierces qui ne sont liées à aucune implémentation de persistance. Les champs étant privés dans ces classes, vous devez remplacer les accesseurs et leur ajouter des annotations de mappage. Une autre option serait d'utiliser le mappage XML, mais certaines choses sont très difficiles à faire là-bas. Donc, si vous voulez des annotations et mappez des classes tierces, les sous-classer et ajouter des annotations sur les accesseurs est la voie à suivre.
Elnur Abdurrakhimov
5
@ElnurAbdurrakhimov voilà, un excellent exemple. Merci.
Martin
79

Je préfère l'accès aux champs, car de cette façon je ne suis pas obligé de fournir un getter / setter pour chaque propriété.

Une enquête rapide via Google suggère que l'accès sur le terrain est majoritaire (par exemple, http://java.dzone.com/tips/12-feb-jpa-20-why-accesstype ).

Je crois que l'accès au champ est l'idiome recommandé par Spring, mais je ne trouve pas de référence pour le confirmer.

Il y a une question relative à l'OS qui a tenté de mesurer le rendement et qui est parvenue à la conclusion qu'il n'y avait "aucune différence".

duffymo
la source
si vous ne fournissez pas de setter getter dans l'entité, alors quelle est l'utilisation de ce champ ... vous ne pouvez pas l'utiliser nulle part dans l'application, car les champs sont privés
anshulkatta
1
Est-ce que ne pas fournir un getter et un setter pour vos champs est une mauvaise pratique?, Je suppose que mon commentaire ici n'est pas toujours juste, car je supposais un champ public, alors qu'il pourrait évidemment être un champ privé auquel on n'a jamais accès.
Mathijs Segers
3
@anshulkatta Je pense que je devrais vraiment répondre à vos questions, car c'est ce qu'est l'encapsulation. Idéalement, tous vos champs devraient être privés, et si possible, ils ne devraient pas avoir de getters ou de setters - c'est le meilleur niveau d'encapsulation que vous pouvez espérer. Considérez un vérificateur de mot de passe. 2 champs privés passwordHash et failedAttempts. Les deux peuvent être privés sans getter ni setter. Ils sont utilisés par bool checkPassword (string password) qui hache, vérifie par rapport à passwordHash, puis met à jour failedAttempts et renvoie un résultat. Pas besoin d'un autre code pour accéder à ces deux champs.
Martin le
2
@anshulkatta il convient de noter qu'en POO, les getters et les setters sont un anti-pattern, ils viennent de la programmation procédurale, avoir une classe avec eux brise le principe d'encapsulation, et génère beaucoup de code standard, c'est-à-dire le même genre de code répété encore et encore. Les objets doivent être immuables, si la modification de ses propriétés est requise, elle doit être effectuée via une méthode qui fait autre chose que simplement renvoyer la valeur de la propriété.
Uriel Arvizu
Non. «Anti-pattern» dans ce cas est une question d'opinion, pas de dogme. Cela dit, l'accès sur le terrain est toujours préféré. Une meilleure idée est d'éviter complètement les solutions ORM. Apprenez à écrire du SQL approprié.
duffymo
38

Voici une situation dans laquelle vous DEVEZ utiliser des accesseurs de propriété. Imaginez que vous ayez une classe abstraite GENERIC avec beaucoup de qualités d'implémentation à hériter en 8 sous-classes concrètes:

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
 }

Maintenant, comment devez-vous annoter cette classe? La réponse est que VOUS NE POUVEZ PAS l'annoter du tout avec un accès au champ ou à la propriété car vous ne pouvez pas spécifier l'entité cible à ce stade. Vous DEVEZ annoter les implémentations concrètes. Mais puisque les propriétés persistantes sont déclarées dans cette superclasse, vous DEVEZ utiliser l'accès aux propriétés dans les sous-classes.

L'accès aux champs n'est pas une option dans une application avec des super-classes génériques abstraites.

HDave
la source
2
touche. Je n'avais pas pensé à ça. Je parie qu'Hibernate lance un sql fou pour ceux-ci.
Joseph Lust
8
Ce problème semble mécaniquement difficile à résoudre sans annoter les propriétés, mais je n'ai jamais rencontré de cas où j'avais besoin d'une classe générique abstraite avec beaucoup d'implémentation que je voulais également conserver. En règle générale, je crée une hiérarchie de classes pour rendre mon objet polymorphe (ce qui en fait une sorte de rupture générique), et pas uniquement pour la réutilisation du code. Et "beaucoup, beaucoup d'implémentation" viole souvent SRP de toute façon, auquel cas je le déplacerais probablement dans des classes séparées. Existe-t-il un exemple concret qui rend ce cas d'utilisation plus évident?
Merlyn Morgan-Graham le
Le seul exemple concret que j'ai est mon application, que je ne peux pas décrire dans un commentaire de 500 caractères ;-)
HDave
3
Vous pouvez utiliser abstract T getOneThing(), et abstract void setOneThing(T thing), et utiliser l'accès aux champs.
Arturo Volpe
33

J'ai tendance à préférer et à utiliser des accesseurs de propriété:

  • Je peux ajouter de la logique si le besoin s'en fait sentir (comme mentionné dans la réponse acceptée).
  • cela me permet d'appeler foo.getId() sans initialiser un proxy (important lors de l'utilisation de Hibernate, jusqu'à ce que HHH-3718 soit résolu).

Inconvénient:

  • cela rend le code moins lisible, il faut par exemple parcourir une classe entière pour voir s'il y en a @Transientpar là.
Pascal Thivent
la source
mais alors si vous utilisez un fournisseur JPA qui n'a pas de "proxies", alors vous n'avez pas ce problème aka "votre fournisseur JPA vous impose".
Neil Stockton
13

Cela dépend vraiment d'un cas spécifique - les deux options sont disponibles pour une raison. OMI, cela se résume à trois cas:

  1. setter a une logique qui ne devrait pas être exécutée au moment du chargement d'une instance à partir d'une base de données; par exemple, une certaine validation de valeur se produit dans le setter, cependant les données provenant de db devraient être valides (sinon elles n'y arriveraient pas (:); dans ce cas, l'accès au champ est le plus approprié;
  2. setter a une logique qui doit toujours être invoquée, même pendant le chargement d'une instance depuis db; par exemple, la propriété en cours d'initialisation est utilisée dans le calcul d'un champ calculé (par exemple propriété - un montant monétaire, propriété calculée - un total de plusieurs propriétés monétaires de la même instance); dans ce cas, un accès à la propriété est requis.
  3. Aucun des cas ci-dessus - alors les deux options sont applicables, restez simplement cohérent (ei si l'accès au champ est le choix dans cette situation, utilisez-le tout le temps dans une situation similaire).
01es
la source
Je suis nouveau sur Hibernate et j'ai du mal avec la même question. Je pense que cet article fournit la réponse la plus claire. Je vous remercie.
Sam Levin
12

Je recommanderais fortement l'accès au champ et PAS les annotations sur les getters (accès à la propriété) si vous voulez faire autre chose dans les setters que de simplement définir la valeur (par exemple, chiffrement ou calcul).

Le problème avec l'accès à la propriété est que les setters sont également appelés lorsque l'objet est chargé. Cela a bien fonctionné pour moi pendant plusieurs mois jusqu'à ce que nous voulions introduire le cryptage. Dans notre cas d'utilisation, nous voulions crypter un champ dans le setter et le décrypter dans le getter. Le problème maintenant avec l'accès aux propriétés était que lorsque Hibernate chargeait l'objet, il appelait également l'installateur pour remplir le champ et chiffrait donc à nouveau la valeur cryptée. Cet article mentionne également ceci: Java Hibernate: Comportement de la fonction de jeu de propriétés différent en fonction de l'appelant

Cela m'a causé des maux de tête jusqu'à ce que je me souvienne de la différence entre l'accès au terrain et l'accès à la propriété. Maintenant, j'ai déplacé toutes mes annotations de l'accès à la propriété à l'accès au champ et cela fonctionne bien maintenant.

Christoph
la source
Oui - J'ai trouvé que si vous utilisez l'accès à la propriété, vous ne pouvez vraiment rien faire dans votre setter à part définir la valeur du champ.
HDave
2
+1 Tenir à l'écart des getters / setters. J'utilise projectlombok.org et je les garde à l'abri des développeurs.
bhantol
11

Je préfère utiliser l'accès aux champs pour les raisons suivantes:

  1. L' accès à la propriété peut conduire à des bugs très désagréables lors de l'implémentation de equals / hashCode et du référencement des champs directement (par opposition à leurs getters). En effet, le proxy n'est initialisé que lors de l'accès aux getters, et un accès direct par champ renverrait simplement null.

  2. L' accès aux propriétés vous oblige à annoter toutes les méthodes utilitaires (par exemple, addChild / removeChild) comme @Transient.

  3. Avec l'accès au champ, nous pouvons masquer le champ @Version en n'exposant pas du tout un getter. Un getter peut également conduire à l'ajout d'un setter, et le versionchamp ne doit jamais être défini manuellement (ce qui peut conduire à des problèmes très désagréables). Toute incrémentation de version doit être déclenchée via le verrouillage explicite OPTIMISTIC_FORCE_INCREMENT ou PESSIMISTIC_FORCE_INCREMENT .

Vlad Mihalcea
la source
1. Comment la stratégie d'accès sur le terrain empêche-t-elle cela? Cela semble être un piège général avec les proxys, quel que soit le style d'accès. 2. Etes-vous sûr, devrait-il s'agir uniquement de getters utilitaires? (mais un bon argument quand même). 3. L' exposition des accesseurs pour le versionchamp est souvent utile dans les situations où les DTO sont utilisés à la place d'entités détachées.
Dragan Bozanovic
1. À cause du moment où un proxy est initialisé. 2. 100% sûr. 3. C'est un point valable.
Vlad Mihalcea
Pardonnez mon ignorance et mon possible manque d'interprétation du texte (je ne suis pas de langue maternelle anglaise). Juste pour clarifier, l'accès aux champs se fait lorsqu'aucune méthode getter / setter n'est nécessaire, donc pour un POJO, qui est utilisé de manière holistique, les champs sont publics? Mais vous dites quelque chose dans le premier sujet et le billet de blog lié disent le contraire. Ce que j'ai compris, c'est que vous ne pouvez pas utiliser d'égal à égal lors de l'utilisation de proxys et d'accès sur le terrain.
MaikoID
Non. L'accès aux champs signifie qu'Hibernate utilise les champs via des réflexions pour lire les attributs d'entité. Pour plus de détails, consultez la section Type d'accès dans le Guide de l'utilisateur Hibernate .
Vlad Mihalcea
7

Je pense que l'annotation de la propriété est meilleure car la mise à jour des champs rompt directement l'encapsulation, même lorsque votre ORM le fait.

Voici un excellent exemple de l'endroit où cela vous brûlera: vous voulez probablement que vos annotations pour le validateur d'hibernation et la persistance au même endroit (champs ou propriétés). Si vous souhaitez tester vos validations alimentées par le validateur de mise en veille prolongée qui sont annotées sur un champ, vous ne pouvez pas utiliser une maquette de votre entité pour isoler votre test unitaire au seul validateur. Aie.

Justin Standard
la source
2
C'est pourquoi vous mettez des annotations de validation sur les accesseurs et des annotations de persistance sur les champs
fishbone
6

Je crois que l'accès à la propriété et l'accès au champ sont légèrement différents en ce qui concerne l'initialisation paresseuse.

Considérez les mappages suivants pour 2 beans de base:

<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>

Et les tests unitaires suivants:

@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();
}

Vous verrez la différence subtile dans les sélections requises:

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

Autrement dit, l'appel fb.getId()nécessite un select, alors que ce pb.getId()n'est pas le cas.

boîte à outils
la source
C'est drôle! :) Mais c'est un comportement spécifique à l'implémentation, j'en suis sûr. I
Vladimir Dyuzhev
Oui, je suppose que cela est dû au fait que seules les classes persistantes sont instrumentées. C'est dommage cependant parce que le champ id est souvent le seul champ qui n'a pas de valeur commerciale et qui n'aurait besoin d'aucun accesseur.
Maurice Perry
6

Permettez-moi d'essayer de résumer les raisons les plus importantes du choix de l'accès basé sur le terrain. Si vous souhaitez approfondir, veuillez lire cet article sur mon blog: Stratégies d'accès dans JPA et Hibernate - Qu'est-ce qui est le meilleur, l'accès au champ ou à la propriété?

L'accès sur le terrain est de loin la meilleure option. Voici 5 raisons à cela:

Raison 1: Meilleure lisibilité de votre code

Si vous utilisez l'accès basé sur les champs, vous annotez vos attributs d'entité avec vos annotations de mappage. En plaçant la définition de tous les attributs d'entité en haut de votre classe, vous obtenez une vue relativement compacte de tous les attributs et de leurs mappages.

Raison 2: Omettez les méthodes getter ou setter qui ne devraient pas être appelées par votre application

Un autre avantage de l'accès basé sur les champs est que votre fournisseur de persistance, par exemple Hibernate ou EclipseLink, n'utilise pas les méthodes getter et setter de vos attributs d'entité. Cela signifie que vous n'avez pas besoin de fournir de méthode qui ne devrait pas être utilisée par votre code d'entreprise. C'est le plus souvent le cas pour les méthodes de définition des attributs de clé primaire générés ou des colonnes de version. Votre fournisseur de persistance gère les valeurs de ces attributs et vous ne devez pas les définir par programme.

Raison 3: implémentation flexible des méthodes getter et setter

Étant donné que votre fournisseur de persistance n'appelle pas les méthodes getter et setter, ils ne sont pas obligés de répondre à des exigences externes. Vous pouvez implémenter ces méthodes comme vous le souhaitez. Cela vous permet d'implémenter des règles de validation métier, de déclencher une logique métier supplémentaire ou de convertir l'attribut d'entité en un type de données différent.

Vous pouvez, par exemple, l'utiliser pour envelopper une association ou un attribut facultatif dans un Java Optional.

Raison 4: Pas besoin de marquer les méthodes utilitaires comme @Transient

Un autre avantage de la stratégie d'accès basée sur le terrain est que vous n'avez pas besoin d'annoter vos méthodes utilitaires avec @Transient. Cette annotation indique à votre fournisseur de persistance qu'une méthode ou un attribut ne fait pas partie de l'état persistant de l'entité. Et comme avec l'accès de type champ, l'état persistant est défini par les attributs de votre entité, votre implémentation JPA ignore toutes les méthodes de votre entité.

Raison 5: éviter les bogues lorsque vous travaillez avec des proxys

Hibernate utilise des proxys pour les associations récupérées paresseusement vers un afin de pouvoir contrôler l'initialisation de ces associations. Cette approche fonctionne bien dans presque toutes les situations. Mais cela présente un piège dangereux si vous utilisez un accès basé sur la propriété.

Si vous utilisez un accès basé sur les propriétés, Hibernate initialise les attributs de l'objet proxy lorsque vous appelez la méthode getter. C'est toujours le cas si vous utilisez l'objet proxy dans votre code métier. Mais un grand nombre d' implémentations d' equals et de hashCode accèdent directement aux attributs. Si c'est la première fois que vous accédez à l'un des attributs de proxy, ces attributs ne sont toujours pas initialisés.

Thorben Janssen
la source
3

Par défaut, les fournisseurs JPA accèdent aux valeurs des champs d'entité et mappent ces champs aux colonnes de base de données à l'aide des méthodes d'accès aux propriétés JavaBean (getter) et mutator (setter) de l'entité. En tant que tels, les noms et les types des champs privés dans une entité n'ont pas d'importance pour JPA. Au lieu de cela, JPA examine uniquement les noms et les types de retour des accesseurs de propriété JavaBean. Vous pouvez le modifier à l'aide de l' @javax.persistence.Accessannotation, qui vous permet de spécifier explicitement la méthodologie d'accès que le fournisseur JPA doit utiliser.

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

Les options disponibles pour l'énumération AccessType sont PROPERTY (valeur par défaut) et FIELD. Avec PROPERTY, le fournisseur obtient et définit les valeurs de champ à l'aide des méthodes de propriété JavaBean. FIELD permet au fournisseur d'obtenir et de définir des valeurs de champ à l'aide des champs d'instance. En tant que meilleure pratique, vous devez simplement vous en tenir à la valeur par défaut et utiliser les propriétés JavaBean, sauf si vous avez une raison impérieuse de faire autrement.

Vous pouvez placer ces annotations de propriété sur les champs privés ou sur les méthodes d'accès public. Si vous utilisez AccessType.PROPERTY(par défaut) et annotez les champs privés au lieu des accesseurs JavaBean, les noms de champ doivent correspondre aux noms de propriété JavaBean. Cependant, les noms ne doivent pas nécessairement correspondre si vous annotez les accesseurs JavaBean. De même, si vous utilisez AccessType.FIELDet annotez les accesseurs JavaBean au lieu des champs, les noms de champ doivent également correspondre aux noms de propriété JavaBean. Dans ce cas, ils ne doivent pas nécessairement correspondre si vous annotez les champs. Il est préférable d'être cohérent et d'annoter les accesseurs JavaBean AccessType.PROPERTYet les champs pour AccessType.FIELD.

Il est important de ne jamais mélanger les annotations de propriété JPA et les annotations de champ JPA dans la même entité. Cela entraîne un comportement non spécifié et est très susceptible de provoquer des erreurs.

Faraz
la source
2

Sommes-nous déjà là

C'est une vieille présentation, mais Rod suggère que l'annotation sur l'accès aux propriétés encourage les modèles de domaine anémiques et ne devrait pas être la manière «par défaut» d'annoter.

Manuel Palacio
la source
2

Un autre point en faveur de l'accès aux champs est que sinon, vous êtes obligé d'exposer les setters pour les collections, ce qui, pour moi, est une mauvaise idée car changer l'instance de collection persistante en un objet non géré par Hibernate rompra définitivement la cohérence de vos données.

Je préfère donc avoir des collections en tant que champs protégés initialisés à des implémentations vides dans le constructeur par défaut et n'exposer que leurs getters. Ensuite, seules les opérations gérées comme clear(), remove(), removeAll()etc sont possibles qui ne connaissent pas faire Hibernate changements.

Jiří Vypědřík
la source
Vous n'êtes pas obligé d'exposer quoi que ce soit car les setters peuvent être protégés. De plus, ces setters ne font pas partie de l'interface en cours d'implémentation, donc même s'ils étaient publics, ils ne sont pas facilement accessibles.
HDave
2

Je préfère les champs, mais j'ai rencontré une situation qui semble me forcer à placer les annotations sur les getters.

Avec l'implémentation Hibernate JPA, @Embeddedne semble pas fonctionner sur les champs. Donc, cela doit aller sur le getter. Et une fois que vous avez mis cela sur le getter, les différentes @Columnannotations doivent également aller sur les getters. (Je pense qu'Hibernate ne veut pas mélanger les champs et les getters ici.) Et une fois que vous mettez des @Columngetters dans une classe, il est probablement logique de le faire tout au long.


la source
2

Je privilégie les accesseurs de terrain. Le code est beaucoup plus propre. Toutes les annotations peuvent être placées dans une section d'une classe et le code est beaucoup plus facile à lire.

J'ai trouvé un autre problème avec les accesseurs de propriété: si vous avez des méthodes getXYZ sur votre classe qui ne sont PAS annotées comme étant associées à des propriétés persistantes, hibernate génère SQL pour tenter d'obtenir ces propriétés, ce qui entraîne des messages d'erreur très déroutants. Deux heures perdues. Je n'ai pas écrit ce code; J'ai toujours utilisé des accesseurs de terrain dans le passé et je n'ai jamais rencontré ce problème.

Versions Hibernate utilisées dans cette application:

<!-- 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>
John Goyer
la source
2

Vous devez choisir l'accès via les champs plutôt que l'accès via les propriétés. Avec les champs, vous pouvez limiter les données envoyées et reçues. Avec via les propriétés, vous pouvez envoyer plus de données en tant qu'hôte et définir des dénominations G (qui définissent en usine la plupart des propriétés au total).

utilisateur10801787
la source
1

J'ai eu la même question concernant l'accès en veille prolongée et j'ai trouvé des réponses ici .

sundary
la source
1

Nous avons créé des beans entité et utilisé des annotations getter. Le problème que nous avons rencontré est le suivant: certaines entités ont des règles complexes pour certaines propriétés concernant le moment où elles peuvent être mises à jour. La solution était d'avoir une logique métier dans chaque setter qui détermine si la valeur réelle a changé ou non et, si tel est le cas, si la modification doit être autorisée. Bien sûr, Hibernate peut toujours définir les propriétés, nous nous sommes donc retrouvés avec deux groupes de setters. Assez laid.

En lisant les articles précédents, je vois également que référencer les propriétés depuis l'intérieur de l'entité pourrait entraîner des problèmes de chargement des collections.

En bout de ligne, je pencherais vers l'annotation des champs à l'avenir.

Steve11235
la source
0

Normalement, les beans sont POJO, donc ils ont quand même des accesseurs.

La question n'est donc pas "lequel est le meilleur?", Mais simplement "quand utiliser l'accès aux champs?". Et la réponse est "quand vous n'avez pas besoin d'un setter / getter pour le terrain!".

Vladimir Dyuzhev
la source
4
Le problème est que vous ne pouvez pas mélanger l'accès au terrain et l'accès à la propriété dans un POJO - vous devez en choisir un
Martin OConnor
Vraiment? J'ai dû l'oublier. Quoi qu'il en soit, j'utilise toujours POJO et accesseurs.
Vladimir Dyuzhev
Notez qu'avec JPA 2.0 (qui n'existait pas lorsque cette question a été posée), vous pouvez désormais mélanger les types d'accès à l'aide de l'annotation @AccessType.
mtpettyp
0

j'y pense et je choisis l'accessoire de méthode

Pourquoi?

parce que l'accesseur de champ et de methos est le même mais si plus tard j'ai besoin de logique dans le champ de chargement, j'enregistre déplacer toutes les annotations placées dans les champs

Cordialement

Grubhart

user261548
la source
0

Pour rendre vos classes plus propres, mettez l'annotation dans le champ puis utilisez @Access (AccessType.PROPERTY)

ely
la source
0

AccessType.PROPERTY: L'implémentation de persistance EJB chargera l'état dans votre classe via les méthodes JavaBean "setter" et récupérera l'état de votre classe en utilisant les méthodes "getter" JavaBean. C'est la valeur par défaut.

AccessType.FIELD: l'état est chargé et récupéré directement à partir des champs de votre classe. Vous n'avez pas besoin d'écrire des "getters" et des "setters" JavaBean.

Daria Yu
la source