对JPA + Hibernate + @Transactional注释的Spring事务支持的奇怪befaviour

abo*_*sun 8 java aop spring hibernate jpa

我发现,在相对简单的例子真是奇怪的行为,可能是我无法理解,因为春天@Transactional自然不深的知识,但是这是很有趣的.

我有简单的User dao,它扩展了spring JpaDaoSupport类并包含标准的save方法:

@Transactional
public User save(User user) {
    getJpaTemplate().persist(user);
    return user;
}
Run Code Online (Sandbox Code Playgroud)

如果工作正常,直到我已经添加新的方法,以同一类:用户getSuperUser(),这个方法应该isAdmin返回用户== true,并且如果在数据库没有超级用户,此方法应该创建一个.那是它的样子:

 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;
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的代码是奇怪向前时发现,没有交易被创建,并承诺在保存(管理)方法的调用,并没有新的用户实际上并没有,尽管@Transactional注解创建我感到很困惑.

在结果,我们有情况:当save()方法从UserDAO的类之外调用 - 计@Transactional注解的用户成功创建和,但如果保存()从同一个DAO类的其他方法的调用内 - @Transactional注解忽略.

这里我是如何更改save()方法来强制它始终创建事务.

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;
}
Run Code Online (Sandbox Code Playgroud)

如您所见,我手动调用begin和commit.有任何想法吗?

Boz*_*zho 9

  1. @Transactional仅考虑来自对象外部的调用.对于内线电话,它不是.要解决这个问题,只需添加@Transactional到您的入口点即可.
  2. 不要@Transactional用于DAO - 而是在Service类上使用它.


tkr*_*tkr 7

我认为带有注释的声明性事务是在代理的基础上实现的.

如果通过动态代理访问DAO,它会检查是否有注释并用事务包装它.

如果你从班级内部打电话给你的班级,就无法拦截这个电话.

要避免此问题,您也可以使用注释标记createSuperuser方法.