2017-05-29 2 views
0

J'utilise JPA 2.0 avec SpringBoot pour l'un de mes projets. Comme l'application nécessitait beaucoup de requêtes en lecture seule, j'ai décidé de créer une classe singleton (Autowired) avec entityManager marqué @PersistenceContext (type = PersistenceContextType.EXTENDED).JPA entité gérée jetant une exception de pointeur nul

Cette même instance du gestionnaire d'entités étendues était à son tour utilisée par les nombreux DAO (peut-être dans plusieurs threads) pour leurs requêtes en lecture seule. Puisque les entités étaient utilisées dans le contexte de persistance étendue, les appels pour développer davantage (hydrater, si vous voulez) sur l'entité n'ont jamais donné LazyInitializationException. De plus, puisque les entités, une fois chargées, étaient toujours présentes dans la mémoire active du contexte de persistance étendue, on obtenait d'excellentes performances.

Cependant, avec le temps qui passe, j'ai parfois commencé à obtenir NullPointerException lors de l'accès à l'un des champs de l'une des entités gérées. Le comportement était non déterministe.

 // .. earlier code skipped for brevity 
     Child child = refNode.getFirstChild(); 

     if (child != null) { 
      // Print confirms that the object is managed. In fact, earlier merely 
      // inserting print statement would dissolve the error. 
      System.out.printf ("Child ID = %s %s\n", child.getChildID(), childDAO.getContext(child)); 

      // NPE while accessing latestVersion attribute of 'Child'. 
      // Values does exist in DB. 
      Unode un = child.getLatestVersion().getuNode(); 
      return un; 
     } 

La partie pertinente de l'entité « enfant » est la suivante:

public class Child implements Serializable { 
     private static final long serialVersionUID = 1L; 

     @Id 
     @GeneratedValue(strategy=GenerationType.AUTO) 
     private int childID; 
     ... 
     @ManyToOne(cascade={CascadeType.ALL}) 
     @JoinColumn(name="latestVersionID") 
     private Version latestVersion; 

Et oui, j'ai aussi reçu l'erreur suivante assez généreusement.

HHH000099: an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session): org.hibernate.AssertionFailure: possible non-threadsafe access to the session 

de fin, je reçois également « ConcurrentModificationException », accesss fil dangereux et des erreurs similaires dans toutes sortes de parties jusque-là propre du code. J'ai également essayé de sécuriser mon thread de gestionnaire d'entités de contexte étendu en utilisant synchronized (mutex) à tous les accès, mais sans aide.

Mon plus grand souci est une entité gérée (dans un contexte étendu) donnant une exception pointée nulle. Bien que les exceptions apparaissent de manière non déterministe, elles semblent souvent centrées autour des endroits où 'Child' ou 'Child.LatestVersion' est accédé.

J'ai essayé, chargement paresseux, chargement effrayant etc., mais d'aucune utilité. Ma compréhension (jusqu'à ce point) était que les entités forward sont chargées avec empressement, inverser paresseusement les entités, et jamais une exception pointée nulle lorsque l'entité est gérée (dans n'importe quel contexte - étendu ou transactionnel).

Toute aide serait sincèrement appréciée.

Répondre

0

La réponse est plutôt simple: EntityManager n'est pas thread-safe. Voir par exemple ici: https://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/transactions.html#transactions-basics-issues

Ce que vous devriez faire est que vous devriez cesser d'essayer de le réutiliser. Le EntityManager est (contrairement à EntityManagerFactory) peu coûteux à créer.

+0

Crizzis, merci. Le gestionnaire d'entités étendu est utilisé uniquement pour les requêtes en lecture seule car le contexte de persistance fonctionne comme un excellent cache. Aucune opération de mise à jour/insertion n'est effectuée à l'aide de l'extension em. De plus, l'accès à em via DI devrait le rendre sûr de toute façon (bien que la sécurité des threads ne soit pas requise pour les requêtes en lecture seule), n'est-ce pas? En outre, thread sûr ou non, l'entité gérée devrait jamais à travers une exception de pointeur nul? –

+0

Si vous utilisez un objet non thread-safe à partir de plusieurs threads, il ne sera pas thread-safe. Je ne peux pas voir comment DI est censé résoudre ce problème. La sécurité des threads dépend aussi de savoir si la * implémentation interne * de 'EntityManager' est sensible au thread ou pas, si elle peut être utilisée avec des requêtes en lecture seule est une question pour les gens' Hibernate'.Enfin, si vous utilisez une API comme vous n'êtes pas censé le faire, personne ne peut garantir ce qui devrait ou ne devrait jamais arriver – crizzis

+0

De plus, si vous avez un cache partagé pour les entités récupérées par reads, vous réalisez que les opérations d'écriture qui ne passent pas par ce cache le désynchroniseront? Peut-être que vous devriez envisager d'utiliser le cache de deuxième niveau: http://docs.oracle.com/javaee/6/tutorial/doc/gkjio.html – crizzis