2010-07-08 20 views
7

J'ai rencontré un cas plutôt étrange dans Java EE 6 où l'utilisation de la méthode find de JPA EntityManager avec l'ID principal d'une entité renvoie null, mais en utilisant les critères API pour sélectionner toutes les entités avec cet ID fonctionne très bien.EntityManager.find ne peut pas trouver l'entité, mais en utilisant l'API Criteria

Voici le code que je utilise pour find:

// Always returns null, even for records I know for sure are in there. 
user = em.find(User.class, userId); 

... et voici le code que je utilise avec l'API Critères:

CriteriaBuilder builder = em.getCriteriaBuilder(); 
CriteriaQuery<User> criteria = builder.createQuery(User.class); 
Root<User> u = criteria.from(User.class); 
TypedQuery<User> query = em.createQuery(
    criteria.select(u).where(builder.equal(u.get("id"), userId))); 
user = query.getSingleResult(); 

Toute idée pourquoi find retours null mais Criteria trouve l'utilisateur? J'ai essayé ces deux méthodes alternatives exactement au même endroit dans le programme.

Voici les parties pertinentes de l'entité utilisateur:

@Entity 
@Table(name = "USERS") 
@Access(AccessType.PROPERTY) 
public class User implements Serializable { 
    ... 
    private Long id; 
    ... 
    @Id 
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_id_generator") 
    @SequenceGenerator(name = "user_id_generator", sequenceName = "user_sequence", allocationSize = 1) 
    @Column(name="id") 
    public Long getId() { 
     return this.id; 
    } 

    public void setId(Long id) { 
     this.id = id; 
    } 
    ... 
} 
+0

Vous ne savez pas si cela fait une différence, mais userId est-il long ou peut-être un entier dans votre code? –

+0

C'est un long, j'ai vérifié cela. – cdmckay

+0

Je viens de rencontrer ce problème avec Hibernate 4.1.8.Final – molholm

Répondre

11

J'ai compris le problème. Il était dû à un champ dans la base de données étant null où il n'aurait pas dû être autorisé. Cela était dû au fait que je l'ai édité à la main. Après avoir ajouté une valeur à ce champ, le problème est parti.

+2

Vous pouvez appliquer NOT NULL pour cette colonne pour la sauvegarde future. – ChuongPham

+0

acclamations pour le partage, sauvé ma journée – MarianP

+0

@ChuongPham: 100% d'accord. Ce n'était pas ma base de données, mais cela m'a appris l'utilité d'utiliser des contraintes pour renforcer l'intégrité des données;) – cdmckay

0

Une raison pourrait être que le champ « id » n'a pas été correctement marqué comme l'identifiant de l'entité utilisateur.

3

Quel fournisseur utilisez-vous?

Où allez-vous trouver ce exécutaient, ou d'une transaction? Rincez-vous et nettoyez-vous la ME avant la découverte?

En utilisant EclipseLink en tant que fournisseur, et mon propre modèle similaire, je ne suis pas en mesure de reproduire cela.

En supposant que votre fournisseur peut ouvrir une session SQL, vous voyez SQL aller à la DB sur la trouver? À quoi ressemble le SQL, et s'exécute-t-il correctement dans SQL Plus etc ...

+0

Je devrais être le vidage de l'EM avant la découverte? J'utilise Hibernate comme fournisseur. – cdmckay

+0

Je l'utilise dans un EJB '@ Stateless'. Le EM est décoré avec un @PersistanceContext (unitName = "xxx_persistence") '. Je vais me référer aux logs SQL et voir ce que je peux trouver. – cdmckay

+0

Ok J'ai vérifié le SQL et j'ai été très surpris de voir la différence entre les deux requêtes: la version "find" faisait un milliard de jointures alors que la requête "criteria" ne faisait que saisir la table users. Quelqu'un d'autre a écrit les fichiers d'entités, donc je vais devoir les parcourir pour trouver ce qui les cause. – cdmckay

0

Comme une vérification de santé mentale déboguez votre code, en prenant le temps avant d'exécuter la recherche de lancer une requête manuelle sur la base de données pour vous assurer que un enregistrement Utilisateur approprié est présent avec l'ID que vous attendez.

Si ce n'est pas dans la base de données, assurez-vous que le gestionnaire d'entités a été rincée ou a été commise la transaction en cours. Par exemple, si vous utilisez Hibernate en tant que fournisseur, il est possible que l'objet soit "conservé" simplement en mémoire cache et que les modifications n'aient pas été réellement transférées dans la base de données. En conséquence, les critères passant par l'implémentation de Hibernate récupèreront l'objet, mais la recherche du gestionnaire d'entités ne sera pas capable de localiser l'objet.

+0

Ceci est une bonne suggestion, mais je sais que ce n'était pas mis en cache parce que je pourrais aller voir dans la base de données avant même de démarrer l'application. – cdmckay

1

Vérifiez bien que vous passez un Long dans l'extrait suivant:

// Always returns null, even for records I know for sure are in there. 
user = em.find(User.class, userId); 

Si cela ne fonctionne pas, activer la journalisation SQL pour voir ce qui se passe et comparer le comportement dans les deux cas.

+0

J'ai débuggé et vérifié qu'il était en effet un long. – cdmckay

+0

@cdmckay: J'ai couru votre code de mon côté et je ne peux pas reproduire (comme prévu pour être honnête). Testé avec Hibernate EM 3.5.3-Final. –

+0

Oui, je pense que cela a à voir avec la façon dont l'entité User est annotée. – cdmckay

3

Je confirme la solution. Il m'est arrivé la même chose.J'ai eu des colonnes marquées NOT NULL, puis pendant le test dans mon application, j'ai désactivé la limitation dans la base de données pour deux colonnes (clés étrangères), mais sans modifier l'attribut (optional = false) de la relation @ManyToOne dans ma classe d'entité. Après avoir supprimé l'attribut, le modèle était cohérent avec la base de données, tout a commencé à fonctionner correctement. Étrange que l'environnement ne produise pas d'avertissement ou d'exception de quelque sorte.