2010-01-16 4 views
13

J'observe un comportement très étrange avec une classe d'entité et charge un objet de cette classe avec JPA (hibernate entitymanager 3.3.1.ga). La classe a un champ (imbriqué), qui est initialisé dans la déclaration. Le setter du champ implémente une vérification nulle (c'est-à-dire qu'il lève une exception lorsqu'une valeur nulle est définie).Comportement JPA étrange, champ initialisé est nul

... 
@Entity 
public class Participant extends BaseEntity implements Comparable<Participant> { 
    ... 
    @Embedded 
private AmsData amsData = new AmsData(); 

public void setAmsData(AmsData amsData) { 
    Checks.verifyArgNotNull(amsData, "amsdata"); 
    this.amsData = amsData; 
} 
    ... 
} 

Quand je reçois cet objet avec JPA, le champ est nul, s'il n'y a pas de données dans le DB pour les champs spécifiés dans l'objet incorporé.

... 
public class ParticipantJpaDao implements ParticipantDao { 
@PersistenceContext 
private EntityManager em; 

@Override 
public Participant getParticipant(Long id) { 
    return em.find(Participant.class, id); 
} 
    ... 
} 

Je débogué le processus avec un point d'observation sur le terrain (devrait arrêter lors de l'accès ou modifié le champ), et je vois une modification lorsque le champ est initialisé, mais quand je reçois le résultat de l'appel découverte , le champ est nul.

Quelqu'un peut-il expliquer, pourquoi c'est ainsi? Comment puis-je m'assurer que le champ n'est pas nul, même s'il n'y a pas de données pour les champs de l'objet incorporé dans la base de données (en plus de le définir manuellement après l'appel find).

+0

ce pourrait être dû à votre moteur de persistance des associations de chargement paresseux? –

+0

J'utilise un chargement paresseux, mais il s'agit d'un champ intégré, donc stocké dans la même table. Y a-t-il une possibilité d'annoter ceci pour être emporté avec impatience? – Dominik

+0

Problème lié à Hibernate (provenant de la réponse supprimée): [HHH-7610: Option pour définir le champ @Embedded null lorsque toutes les colonnes sont NULL] (https://hibernate.atlassian.net/browse/HHH-7610) – sleske

Répondre

16

La spécification JPA ne dit pas explicitement comment gérer un ensemble de colonnes représentant un objet intégrable qui sont toutes vides. Il peut signaler une référence NULL ou une instance d'objet avec tous les champs NULL. Hibernate choisit une référence nulle dans ce cas, bien que d'autres implémentations JPA puissent choisir cette dernière.

La raison pour laquelle votre setter n'est jamais appelé est parce que Hibernate accède à votre champ via la réflexion, en contournant le setter que vous avez implémenté. Il le fait parce que vous utilisez l'accès basé sur les champs plutôt que sur l'accès basé sur les propriétés.

La réponse du Tchad fournirait la fonctionnalité que vous recherchez, mais il y a une mise en garde (voir ci-dessous).

» ... L'état persistant d'une entité est accessible par la persistance exécution du fournisseur [1], soit par JavaBeans propriété de style accesseurs ou par les variables d'instance. Un type d'accès simple (champ ou une propriété accès) applique à une hiérarchie d'entités. Lorsque annotations sont utilisées, le placement des les annotations cartographiques soit sur les champs persistants ou persistants propriétés de la classe d'entité spécifie le type d'accès comme étant soit o feld [Spécification EJB3 persistance ] r propriété fondée sur l'accès respectivement ... »

donc en déplaçant les annotations vers le poseur, vous dites JPA que vous voulez utiliser l'accès basée sur la propriété au lieu de feld accès basé. Vous devez savoir, cependant, que l'accès basé sur un champ - tel que vous l'appliquez actuellement - est préféré à l'accès basé sur la propriété. Il existe plusieurs raisons pour lesquelles l'accès basé sur les propriétés est déconseillé, mais l'une d'entre elles est que vous êtes obligé d'ajouter des getters et des setters pour tous vos champs d'entité persistants, mais vous ne voulez pas que ces mêmes champs puissent être modifiés par des clients externes. En d'autres termes, l'utilisation de l'accès basé sur les propriétés de JPA vous oblige à affaiblir l'encapsulation de votre entité.

+0

mais pourquoi il a mis le champ à null? – Dominik

+0

J'ai mis à jour la réponse pour expliquer pourquoi le champ pourrait être nul. La raison la plus courante est que vous avez dans votre base de données des enregistrements antérieurs à l'ajout de votre champ ** amsData **. – rcampbell

+0

Je viens de stocker une nouvelle entité avec un objet AmsData non nul (mais vide) et j'obtiens le même résultat. Et oui, la classe AmsData est annotée embeddable. – Dominik

1

Eh bien, il est possible que votre objet soit construit deux fois en coulisses. Les implémentations JPA définissent généralement ces champs directement.

Je pense que vous devez mettre les annotations sur les Getters et les setters eux-mêmes si vous voulez qu'ils soient utilisés. Voir cette réponse:

Empty constructors and setters on JPA Entites

+0

L'objet incorporé n'a aussi que des colonnes et pas d'associations, qui pourraient être récupérées avec impatience. – Dominik

3

La réponse est (grâce à rcampell), si toutes les données d'un objet incorporé est nulle (dans le db), l'objet incorporé sera également nulle, bien que lors de son initialisation dans la déclaration. La seule solution semble être de régler l'objet manuellement.

@Override 
public Participant getParticipant(Long id) { 
    Participant participant = em.find(Participant.class, id); 
    if(participant != null && participant.getAmsData() == null) 
    { 
     participant.setAmsData(new AmsData()); 
    } 
    return participant; 
} 

se sent toujours étrange pour moi ...

+0

Avait le même problème il y a quelques semaines. Cela fera l'affaire. +1 – whiskeysierra

+0

Cette solution déclenche l'instruction de mise à jour SQL vers le serveur de base de données, ce qui pose des problèmes tels que la concurrence entre les threads et les problèmes de performance. –