Les Annotations d'hibernation-Qu'est-ce qui est meilleur, le terrain ou l'accès à la propriété?

cette question Est quelque peu liée à la question sur le Placement des mentions D'hibernation .

mais je veux savoir ce qui est meilleur ? Accès par Propriétés ou accès par champs? Quels sont les avantages et les inconvénients de chacune?

109
demandé sur Community 2009-02-27 15:44:48

23 réponses

je préfère les accesseurs, car je peux ajouter un peu de logique d'affaires à 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";
  }
}

en outre, si vous jetez d'autres libs dans le mélange (comme certains JSON-converting lib ou BeanMapper ou Dozer ou d'autres Bean mapping/cloning lib basé sur les propriétés getter/setter) vous aurez la garantie que la lib est en synchronisation avec le gestionnaire de persistance (les deux utilisent le getter/setter).

28
répondu Miguel Ping 2010-10-18 09:57:25

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

ce qui est hiberné/JPA fait réellement quand il persiste un objet - Eh bien, il persiste l'état de l'objet. Cela signifie qu'il doit être stocké de manière à pouvoir être facilement reproduit.

quoi est l'encapsulation? Encapsulations signifie 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 le gardant cohérent et valide.

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

sauvegarder l'état interne de l'objet ne brise pas l'encapsulation - sinon vous ne comprenez pas vraiment ce que l'encapsulation signifie, Et pourquoi elle existe. C'est exactement comme la sérialisation des objets.

pour cette raison, dans la plupart des cas, il est approprié de persister les champs et non les accesseurs. Cela signifie qu'un objet peut être fidèlement recréé de la base de données exactement comme elle était stockée. Il ne devrait pas avoir besoin de validation, parce que cela a été fait sur l'original quand il a été créé, et avant qu'il ait été stocké dans la base de données (à moins que, Dieu nous en garde, vous stockez des données invalides dans la base de données!!!!). De même, il ne devrait pas être nécessaire de calculer des valeurs, car elles étaient déjà calculées avant que l'objet soit stocké. L'objet doit ressembler à ce qu'il était avant d'être sauvé. En fait, en ajoutant des choses supplémentaires dans les getters/setters vous sont en fait augmenter le risque que vous allez 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 quelques cas d'utilisation valide pour persister les accesseurs, cependant, ils seront généralement rares. Un exemple peut être que vous voulez éviter de persister une valeur calculée, mais vous pouvez vouloir poser la question pourquoi vous ne le calculez pas sur demande dans le getter de la valeur, ou paresseusement l'initialiser dans la lecture. Personnellement, je ne peux pas penser à un cas de bonne utilisation, et aucune des réponses ici donnent vraiment un "génie logiciel" réponse.

221
répondu Martin 2011-05-21 21:56:24

je préfère l'accès de terrain, parce que de cette façon je ne suis pas forcé de fournir getter/setter pour chaque propriété.

un rapide sondage 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 field access est l'idiome recommandé par Spring, mais je ne trouve pas de référence pour le sauvegarder.

il y a un ainsi question qui a essayé de mesurer le rendement et est venu à la conclusion qu'il n'y a"aucune différence".

74
répondu duffymo 2017-05-23 12:03:07

Voici une situation où vous devez utiliser des accesseurs de propriété. Imaginez que vous ayez une classe abstraite générique avec beaucoup de bonté 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 exactement comment annoter cette classe? La réponse est que vous ne pouvez pas l'annoter du tout avec un accès à un champ ou à une propriété parce que vous ne pouvez pas spécifier l'entité cible à ce point. Vous devez annoter les implémentations concrètes. Mais depuis l' les propriétés persistées sont déclarées dans cette superclasse, vous devez utiliser l'accès à la propriété dans les sous-classes.

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

35
répondu HDave 2010-07-06 06:34:33

j'ai tendance à préférer et à utiliser les accesseurs de propriété:

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

Inconvénient:

  • il rend le code moins lisible, vous avez par exemple à parcourir toute une classe pour voir s'il y a @Transient autour de là.
33
répondu Pascal Thivent 2016-07-06 13:55:01

cela dépend vraiment un cas spécifique -- les deux options sont disponibles pour une raison. IMO il 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 validation de valeur se produit dans le setter, cependant les données provenant de db devraient être valides (sinon il n'y arriverait pas (: ); dans ce cas, l'accès au champ est le plus approprié;
  2. setter a une certaine logique qui devrait toujours être invoquée, même lors du chargement d'une instance à partir de db; par exemple, la propriété initialisée est utilisée dans le calcul d'un certain champ calculé (par exemple, la propriété -- un montant monétaire, une propriété calculée -- un total de plusieurs propriétés monétaires de la même instance); dans ce cas, l'accès à la propriété est requis.
  3. aucun des cas ci-dessus -- alors les deux options sont applicables, il suffit de rester cohérent (E. I. si l'accès au champ est le choix dans ce situation de l'utiliser tout le temps dans une situation similaire).
13
répondu 01es 2009-02-27 18:43:11

je recommande fortement l'accès sur le terrain et non les annotations sur les getters (accès à la propriété) si vous voulez faire quoi que ce soit de plus dans les setters que de simplement définir la valeur (par exemple cryptage ou calcul).

le problème avec l'accès à la propriété est que les setters sont aussi appelés lorsque l'objet est chargé. Cela a fonctionné pour moi très bien pendant de nombreux mois jusqu'à ce que nous voulions introduire le cryptage. Dans notre cas d'utilisation nous voulions crypter un champ dans le setter et décrypter dans la lecture. Le problème avec l'accès à la propriété était que lorsque Hibernate chargeait l'objet, il appelait aussi le setter pour remplir le champ et chiffrait à nouveau la valeur chiffrée. Ce billet mentionne aussi ceci:: Java Hibernate: propriété de la fonction de comportement en fonction de qui est de l'appeler

cela m'a causé des maux de tête jusqu'à ce que je me souvienne de la différence entre l'accès sur le terrain et l'accès à la propriété. Maintenant que j'ai déménagé toutes mes annotations de l'accès à la propriété à l'accès aux champs et ça marche bien maintenant.

12
répondu Christoph 2017-05-23 12:34:41

je pense que l'annotation de la propriété est préférable parce que la mise à jour des champs casse directement l'encapsulation, même lorsque votre ORM le fait.

voici un bon exemple de l'endroit où il vous brûlera: vous voulez probablement vos annotations pour hibernate validator & persistence au même endroit (soit des champs ou des propriétés). Si vous voulez tester vos validations alimentées par hibernate validator qui sont annotées sur un champ, vous ne pouvez pas utiliser une maquette de votre entité pour isoler votre test d'unité juste le validateur. Ouch.

7
répondu Justin Standard 2009-04-23 12:16:29

je crois que l'accès à la propriété vs d'accès sur le terrain est légèrement différente en ce qui concerne paresseux initialisation.

prendre en considération les correspondances suivantes pour 2 fèves 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 essais 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

, c'est-à-dire qu'appeler fb.getId() nécessite un select, alors que pb.getId() n'en nécessite pas.

6
répondu toolkit 2015-09-30 02:15:13

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

  1. "le " accès à la propriété peut entraîner des bogues très désagréables lors de la mise en œuvre de equals/hashCode et référencement des champs directement (par opposition à travers leurs getters). Ceci est dû au fait que le proxy n'est initialisé que lorsque les getters sont accessibles, et qu'un accès direct à un champ retournerait simplement null.

  2. le accès à la propriété exige que vous annotiez toutes les utility methods (par exemple addChild/removeChild) comme @Transient .

  3. avec l'accès aux champs, nous pouvons cacher le champ @Version en n'exposant pas du tout un getter. Un getter peut aussi conduire à ajouter un setter, et le champ version ne devrait jamais être défini manuellement (ce qui peut conduire à des problèmes très désagréables). Toutes les versions incrémentation doit être déclenché par OPTIMISTIC_FORCE_ increment ou PESSIMISTIC_FORCE_ increment verrouillage explicite.

5
répondu Vlad Mihalcea 2018-01-04 18:26:50

sommes-nous encore là

C'est une ancienne présentation mais Rod suggère que l'annotation sur l'accès à la propriété encourage les modèles de domaine anémiques et ne devrait pas être la façon" par défaut " d'annoter.

2
répondu Manuel Palacio 2011-01-12 09:02:29

un autre point en faveur de l'accès sur le terrain est que sinon vous êtes forcé d'exposer les setters pour les collections ainsi que ce qui, pour moi, est une mauvaise idée comme changer l'instance de collecte persistante à un objet non géré par Hibernate va certainement briser la cohérence de vos données.

donc je préfère avoir des collections comme champs protégés initialisés pour vider les implémentations dans le constructeur par défaut et n'exposer que leurs getters. Ensuite, seulement les opérations gérées comme clear() , remove() , removeAll() etc sont possibles qui ne feront jamais hiberner ignorant des changements.

2
répondu Jiří Vypědřík 2013-06-11 06:08:19

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

avec la mise en œuvre Hibernée de L'app, @Embedded ne semble pas fonctionner sur les champs. Alors qui doit aller sur le getter. Et une fois que vous mettez cela sur le getter, alors les diverses annotations @Column doivent aller sur les getter aussi. (Je pense que les Hibernés ne veulent pas mélanger les champs et les castors ici.) Et une fois que vous êtes de mettre @Column sur les méthodes de lecture en dans une classe, c'est probablement logique de faire ça tout au long.

2
répondu Willie Wheeler 2014-06-01 10:58:22

je préfère les accès de terrain. Le code est beaucoup plus propre. Toutes les annotations peuvent être placées en une seule 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, résultant en quelques messages d'erreur très confus. Deux heures perdues. Je ne gravais pas ce code; j'ai toujours utilisé des accès de terrain dans le passé et je n'ai jamais rencontré ce problème.

versions Hibernées 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>
2
répondu John Goyer 2015-09-30 02:15:51

par défaut, les fournisseurs JPA accèdent aux valeurs des champs entity et les mappent vers les colonnes de la base de données . en utilisant les méthodes JavaBean property accessor (getter) et mutator (setter) de l'entité. En tant que tel, l' les noms et les types des champs privés d'une entité n'ont pas d'importance pour L'app. Au lieu de cela, JPA regarde seulement les noms et les types de retour des accesseurs de propriété JavaBean. Vous pouvez modifier ceci en utilisant l'annotation @javax.persistence.Access , qui vous permet de spécifier explicitement l'accès méthodologie que le fournisseur JPA devrait employer.

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

les options disponibles pour L'AccessType enum sont la propriété (la valeur par défaut) et le champ. Avec Propriété, le fournisseur obtient et définit des valeurs de champ en utilisant les méthodes de propriété JavaBean. DOMAINE font de le fournisseur obtient et définit les valeurs des champs en utilisant les champs d'instance. Comme une pratique exemplaire, vous devriez simplement coller par défaut et utiliser JavaBean propriétés, sauf si vous avez une raison valable de faire autrement.

You pouvez mettre ces biens annotations sur les champs privés ou publics des méthodes accesseurs. Si vous utilisez AccessType.PROPERTY (par défaut) et annotez les champs privés à la place du JavaBean accesseurs, les noms des champs doivent correspondre aux noms des propriétés Javabéennes. Toutefois, les noms ne sont pas doivent correspondre si vous annotez les accesseurs JavaBean. De même, si vous utilisez AccessType.FIELD et annoter les accesseurs JavaBean au lieu des champs, les noms de champ doivent aussi correspondre à la JavaBean propriété nom. Dans ce cas, ils n'ont pas à correspondre si vous annoter les champs. Il est préférable de juste être cohérent et annoter les accesseurs JavaBean pour AccessType.PROPERTY et 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 se traduit par un comportement non spécifié et est très susceptible de causer des erreurs.

2
répondu Desert 2017-04-06 13:42:43

j'ai eu la même question concernant accesstype en hibernation et trouvé quelques réponses ici .

1
répondu sundary 2010-04-27 17:33:48

j'ai résolu paresseux d'initialisation et d'accès sur le terrain ici Hibernate one-to-one: getId() sans la corvée d'ensemble de l'objet

1
répondu xmedeko 2017-05-23 11:47:20

nous avons créé des haricots entités 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 certaine logique commerciale dans chaque setter qui détermine si oui ou non la valeur réelle a changé et, si oui, si le changement devrait être autorisé. Bien sûr, Hibernate peut toujours définir les propriétés, donc nous avons fini avec deux groupes de setters. Assez laid.

Reading poteaux précédents, je vois aussi que la référence des propriétés de l'intérieur de l'entité pourrait conduire à des problèmes avec les collections ne chargent pas.

bref, je me pencherais vers l'annotation des champs à l'avenir.

1
répondu Steve11235 2012-07-16 15:09:30

normalement les haricots sont POJO, donc ils ont des accessoires de toute façon.

donc la question n'est pas " lequel est le meilleur?"mais tout simplement "lors de l'utilisation sur le terrain?". Et la réponse est "quand vous n'avez pas besoin d'un setter/getter pour le domaine!".

0
répondu Vladimir Dyuzhev 2009-02-27 18:48:03

je pensais à ce sujet et je choisir la méthode accesor

pourquoi?

parce que field et methos accesseor sont les mêmes mais si plus tard j'ai besoin d'un peu de logique dans le champ de charge, je sauve déplacer toute annotation placée dans les champs

concerne

Grubhart

0
répondu user261548 2010-01-29 23:43:17

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

0
répondu ely 2011-07-20 06:48:47

les deux :

la spécification EJB3 exige que vous déclariez les annotations sur l'élément tapez qui sera accédé, c.-à-d. la méthode getter si vous utilisez la propriété accès, le champ si vous utilisez l'accès au champ.

https://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#entity-mapping

0
répondu moueza 2018-08-18 04:09:05

Accessstype.Propriété: L'implémentation de la 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 JavaBean "getter". C'est la valeur par défaut.

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

0
répondu Vicky 2018-08-20 12:43:52