Descubrí un comportamiento realmente extraño en un caso de uso relativamente simple, probablemente no puedo entenderlo debido a los conocimientos no profundos de la naturaleza primavera @Transactional, pero esto es Bastante interesante.Extraño comportamiento de la compatibilidad de la transacción de primavera para JPA + Hibernate + @ Anotación transaccional
me han dao usuario simple que se extiende clase JpaDaoSupport primavera y contiene estándar de guardar método:
@Transactional
public User save(User user) {
getJpaTemplate().persist(user);
return user;
}
Si estaba trabajando bien hasta que haya añadir nuevo método para una misma clase: Usuario getSuperUser(), este método debe devolver usuario con isAdmin == verdadero, y si no hay superusuario en db, el método debe crear uno. Eso es lo que estaba buscando como:
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;
}
Como se ve el código es extraño hacia adelante y yo estaba muy confundido cuando descubrió que no hay ninguna transacción se crea y se ha comprometido en la invocación del método Save (admin) y ningún nuevo usuario no era' t creado a pesar de la anotación @Transactional.
En resultado tenemos situación: cuando el método save() invoca desde el exterior de la clase UserDAO - Anotación @Transactional contada y el usuario creado con éxito, pero si save() invoca desde el otro método de la misma clase dao - @Transactional anotación ignorada.
Aquí cómo me cambié el método save() para forzarlo crea siempre una transacción.
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;
}
Como ve, invoco manualmente begin y commit. ¿Algunas ideas?
gracias por una explicación tan profunda, dicho sea de paso, ¿y si activare aspectj como sé que aspectj es una alternativa al proxy dinámico? no tiene sentido marcar createSuperuser como transaccional, siempre que aún lo invoque desde dentro de getSuperUser en la misma clase, pero sí - cuando marque getSuperUser transaccional - todo funciona bien – abovesun