如果我们在调用函数中有@Transaction,那么被调用的函数是否还在事务中?

时间:2012-06-25 18:45:07

标签: java spring transactions

我知道我们需要尽可能缩短@Transactional边界。这是代码:

我通过Spring依赖注入使用userDAO对象:

private static ApplicationContext context ;
private UserDAO userDAO;

public TransactionsTest() {
    userDAO = (UserDAO) context.getBean("userDAO");
} 

我从testSaveUserAccounts()类调用TransactionsTest尝试使用userDAO来插入/更新数据。

案例1:

@Transactional
public void testSaveUserAccounts() {
    UserAccounts userAccounts = new UserAccounts();
    userAccounts.setCommunityId(10L);
    userDAO.saveObject(userAccounts);
}

// This method is inside UserDAO
        public void saveObject(Object object) {
        entityManager.merge(object);
    }

案例2:

@Transactional
public void testSaveUserAccounts() {
    UserAccounts userAccounts = new UserAccounts(); 
    userAccounts.setCommunityId(10L);
    userDAO.saveObject(userAccounts);
}

// This method is inside UserDAO
    @Transactional(propagation=Propagation.REQUIRED)
    public void saveObject(Object object) {
        entityManager.merge(object);
    }

Spring Context:

    <tx:annotation-driven transaction-manager="transactionManager" />

        <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
            <property name="entityManagerFactory" ref="entityManagerFactory" />
        </bean>

        <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

        <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
            <property name="dataSource" ref="dataSourceLocal" />
            <property name="jpaVendorAdapter">
                <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
            </property>
            <property name="persistenceUnitName" value="spring-jpa" />
      </bean>

UserDAO:

@Repository
public class UserDAO extends BaseDAO {

    @Transactional(propagation=Propagation.REQUIRED)
    public void saveObject(Object object) {
        entityManager.merge(object);
    }

}

BaseDAO:

public abstract class BaseDAO {

    protected EntityManager entityManager;
    protected HashMap<String,Long>  eventIdMap = new HashMap<String,Long>();


    @PersistenceContext
    public void setEntityManager(EntityManager entityManager) {
        this. entityManager = entityManager;
    }


    public <T> T getById(final Class<T> clazz, final Serializable id) {
        T object = clazz.cast(entityManager.find(clazz, id));
        return object;
    }


    @Transactional
    public Object save(Object ob) {
        Object object = entityManager.merge(ob);
        return object;
    }


    @Transactional
    public void persist(Object ob) {
        entityManager.persist(ob);
    }


    @SuppressWarnings("unchecked")
    public <T>  ArrayList<T> getAll(final Class<T> clazz) {
        String hqlQuery = "from "+ clazz.getSimpleName();
        ArrayList<T> list  = (ArrayList<T>)entityManager.createQuery(hqlQuery).getResultList();
        return list;
    }

}

我一直在围绕几个事务边界REQUIREDREQUIRES_NEWSUPPORTS等进行实验,但无法自信地说明案例1的原因(当调用method2时, method1的事务边界不合并数据,而在案例2中解决了这个问题。

为什么我必须在内部方法中指定@Transactional,因为我已经在事务边界内标记了调用函数?

2 个答案:

答案 0 :(得分:1)

您的事务测试类不是Spring Bean,这就是案例1不起作用的原因。 Spring需要检测到一个方法上有@Transactional,并且当spring将bean注册到spring bean工厂时它会这样做。

另外请记住,除非使用AspectJ加载时编织或AspectJ编译时编织,否则在同一个bean中执行基于代理的AOP调用将不会被事务方面捕获。

同样将@Transactional放在你的Dao上并不是一个好主意,因为事务边界最好在服务层标记。原因是特定服务方法可能需要与多个Dao进行交互,并且您希望这些Dao的操作成为服务层启动的tx的一部分,而不是必须分析Dao以查看传播行为是什么。

您可以发布测试类的完整代码吗?

答案 1 :(得分:0)

@Transactional在本地不执行任何操作,只有在从其他服务调用时才有效。换句话说,您必须保留事务注释的当前上下文以执行任何操作。因此,在两种情况下,调用方法1都是相同的,如果从另一个服务调用method2,则情况2只会执行任何操作。