J'ai découvert un comportement vraiment étrange sur un cas d'utilisation relativement simple, probablement je ne peux pas le comprendre en raison de connaissances pas profondes du printemps @Transactional nature, mais c'est plutôt interessant.Strange befaviour de soutien de transaction de printemps pour JPA + Hibernate + @ annotation transactionnelle
J'ai simples dao utilisateur qui étend la classe ressort JpaDaoSupport et contient la méthode standard d'enregistrement:
@Transactional
public User save(User user) {
getJpaTemplate().persist(user);
return user;
}
Si fonctionnait très bien jusqu'à ce que j'ai ajouter une nouvelle méthode pour une même classe: User getSuperUser(), cette méthode devrait renvoie l'utilisateur avec isAdmin == true, et s'il n'y a pas de super utilisateur dans db, la méthode devrait en créer un. Voilà comment il a été Ressemblant:
public User createSuperUser() {
User admin = null;
try {
admin = (User) getJpaTemplate().execute(new JpaCallback() {
public Object doInJpa(EntityManager em) throws PersistenceException {
return em.createQuery("select u from UserImpl u where u.admin = true").getSingleResult();
}
});
} catch (EmptyResultDataAccessException ex) {
User admin = new User('login', 'password');
admin.setAdmin(true);
save(admin); // THIS IS THE POINT WHERE STRANGE THING COMING OUT
}
return admin;
}
Comme vous voyez le code est étrange méthode avant et je suis très confus quand a découvert qu'aucune transaction a été créée et engagée sur l'invocation de sauvegarde (admin) et aucun nouvel utilisateur n » était pas t réellement créé malgré l'annotation @Transactional. En résultat, nous avons la situation: lorsque la méthode save() appelle de l'extérieur de la classe UserDAO - Annotation @Transactional comptée et l'utilisateur créé avec succès, mais si save() invoque de l'intérieur d'une autre méthode de la même classe dao - @Transactional annotation ignorée.
Voici comment j'ai changé la méthode save() pour la forcer à toujours créer une transaction.
public User save(User user) {
getJpaTemplate().execute(new JpaCallback() {
public Object doInJpa(EntityManager em) throws PersistenceException {
em.getTransaction().begin();
em.persist(user);
em.getTransaction().commit();
return null;
}
});
return user;
}
Comme vous le voyez, j'appelle manuellement begin et commit. Des idées?
merci pour une telle explication profonde, à propos, que se passe-t-il si je vais activer aspectj comme je sais que l'aspectj est une alternative au proxy dynamique. cela n'a aucun sens de marquer createSuperuser comme transactionnel, tant que je l'invoque toujours de l'intérieur de getSuperUser dans la même classe, mais oui - quand je marque getSuperUser transactionnel - tout fonctionne bien – abovesun