当前位置: 首页 > 面试题库 >

Spring + TestNG不进行事务回滚

金令
2023-03-14
问题内容

我正在使用TestNG
6.9.9来构建回归测试环境。但是遇到使用JUnit时从未遇到过的问题。在我看来,当每个测试用例完成时,如果测试方法在与调用方法相同的事务上下文中运行,则默认情况下将自动回滚每个数据的更改。但这似乎不是事实,而且我无法找出我的代码中是否有任何错误。请帮帮我。pom.xml中的属性,指示框架的版本

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <springframework.version>4.2.4.RELEASE</springframework.version>
    <hibernate.version>4.3.11.Final</hibernate.version>
<testng.version>6.9.9</testng.version>
</properties>

显然,它们都是最新的。

我的测试课:

package com.noahwm.hkapp.api;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.testng.Assert;
import org.testng.annotations.Test;

import com.noahwm.hkapp.api.db.dao.AppUserDao;
import com.noahwm.hkapp.api.db.model.AppUser;
import com.noahwm.hkapp.api.service.AppUserService;

@ContextConfiguration(locations = { "classpath:applicationContext-test.xml" })
public class AppUserServiceTestNGTest extends AbstractTestNGSpringContextTests {

  @Autowired
  private AppUserService appUserService;

  @Test
  @Rollback
  @Transactional
  public void testApp() {
    AppUser appUser = new AppUser();
    appUser.setAge(10);
    appUser.setGender("F");
    appUser.setMobilePhone("13219201034");
    appUser.setName("HKAPP Test");
    appUserService.createUser(appUser);
    String appUserId = appUser.getId();
    Assert.assertNotNull(appUserId);
  }
}

创建一个实体实例,然后调用createUser()将其保存到数据库。根据我在JUnit中所做的事情,即使我没有将@Rollback批注放在测试方法的前面,数据也会自动回滚。

AppUser的结构为:

package com.noahwm.hkapp.api.db.model;

import javax.persistence.Column;
import javax.persistence.Entity;

@Entity(name = "APP_USERS")
public class AppUser extends BaseDataModel {

  @Column(name = "NAME")
  private String name;

  @Column(name = "GENDER")
  private String gender;

  @Column(name = "AGE")
  private Integer age;

  @Column(name = "MOBILE_PHONE")
  private String mobilePhone;

  public String getId() {
    return id;
  }

  public void setId(String id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getGender() {
    return gender;
  }

  public void setGender(String gender) {
    this.gender = gender;
  }

  public Integer getAge() {
    return age;
  }

  public void setAge(Integer age) {
    this.age = age;
  }

  public String getMobilePhone() {
    return mobilePhone;
  }

  public void setMobilePhone(String mobilePhone) {
    this.mobilePhone = mobilePhone;
  }

}

BaseDataModel.java

package com.noahwm.hkapp.api.db.model;

import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Version;

import org.hibernate.annotations.GenericGenerator;

@MappedSuperclass
public class BaseDataModel {

  @Id
  @GeneratedValue(generator = "uuid")
  @GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDGenerator")
  @Column(name = "ID", unique = true, length = 36, nullable = false)
  protected String id;

  @Version
  @Column(name = "version")
  protected Integer version;

  public String getId() {
    return id;
  }

  public void setId(String id) {
    this.id = id;
  }

  public Integer getVersion() {
    return version;
  }
}

ApplicationContext-test.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:jee="http://www.springframework.org/schema/jee"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-4.0.xsd
            http://www.springframework.org/schema/jee
            http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
            http://www.springframework.org/schema/util
            http://www.springframework.org/schema/util/spring-util-4.0.xsd">

    <bean
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location">
            <value>classpath:jdbc.test.properties</value>
        </property>
    </bean>

    <context:annotation-config />
    <context:component-scan base-package="com.noahwm.hkapp.api" />

    <aop:aspectj-autoproxy />

    <bean id="lobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler"
        lazy-init="true" />

    <bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource"
        destroy-method="close">
        <property name="driverClass" value="${jdbc.driver}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="maxConnectionsPerPartition" value="${jdbc.maxConnectionsPerPartition}" />
        <property name="minConnectionsPerPartition" value="${jdbc.minConnectionsPerPartition}" />
        <property name="partitionCount" value="${jdbc.partitionCount}" />
        <property name="acquireIncrement" value="${jdbc.acquireIncrement}" />
    </bean>

    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="packagesToScan">
            <list>
                <value>com.noahwm.hkapp.api.db.model</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                <prop key="hibernate.jdbc.batch_size">10</prop>
                <prop key="hibernate.jdbc.fetch_size">30</prop>
                <prop key="hibernate.default_batch_fetch_size">10</prop>
            </props>
        </property>
    </bean>

    <tx:annotation-driven transaction-manager="txManager"
        proxy-target-class="true" />
    <bean id="txManager"
        class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
</beans>

事务管理器名为“ txManager”。AppUserService.java

package com.noahwm.hkapp.api.service;

import java.util.List;
import java.util.Map;

import com.noahwm.hkapp.api.db.dao.AppUserDao;
import com.noahwm.hkapp.api.db.model.AppUser;

public interface AppUserService {
  void createUser(AppUser user);

}

AppUserServiceImpl.java

package com.noahwm.hkapp.api.service.impl;

import java.util.List;
import java.util.Map;

import org.hibernate.criterion.Order;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import com.noahwm.hkapp.api.db.dao.AppUserDao;
import com.noahwm.hkapp.api.db.model.AppUser;
import com.noahwm.hkapp.api.service.AppUserService;
import com.noahwm.hkapp.api.service.EntityService;
import com.noahwm.hkapp.utils.SimpleSearchCriteria;

@Service("AppUserService")
@Transactional(propagation=Propagation.REQUIRED)
public class AppUserServiceImpl extends EntityService implements AppUserService {

  private static final Logger logger = LoggerFactory.getLogger(AppUserServiceImpl.class);

  @Autowired
  private AppUserDao dao;
  @Override
  public void createUser(AppUser user) {
    logger.debug("Creating user with name {}", user.getName());
    dao.save(user);
  }
}

AppUserDao.java

package com.noahwm.hkapp.api.db.dao;

import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.noahwm.hkapp.api.db.model.AppUser;

@Repository
@Transactional(propagation=Propagation.REQUIRED)
public class AppUserDao extends BaseDao<AppUser> {
  public void testsRollBack(AppUser appUser) throws Exception{
    save(appUser);
  }
}

BaseDao.java

package com.noahwm.hkapp.api.db.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.Resource;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.transaction.annotation.Transactional;

import com.noahwm.hkapp.api.db.model.BaseDataModel;
import com.noahwm.hkapp.utils.SimpleSearchCriteria;

class BaseDao<T extends BaseDataModel> {

  private Class<T> domainClass;

  @Resource(name = "sessionFactory")
  protected SessionFactory sessionFactory;

  protected SessionFactory getSessionFactory() {
    return sessionFactory;
  }

  public void setSessionFactory(SessionFactory sessionFactory) {
    this.sessionFactory = sessionFactory;
  }

  @SuppressWarnings("unchecked")
  public Class<T> getDomainClass() {
    if (domainClass == null) {
      Type type = this.getClass().getGenericSuperclass();
      ParameterizedType parameterizedType = (ParameterizedType) type;
      domainClass = (Class<T>) parameterizedType.getActualTypeArguments()[0];
    }
    return domainClass;
  }

  protected Session getCurrentSession() {
    return getSessionFactory().getCurrentSession();
  }

  public Criteria createCriteria() {
    return getCurrentSession().createCriteria(getDomainClass());
  }

  public void save(T o) {
    getCurrentSession().save(o);
  }

  public void update(T o) {
    getCurrentSession().update(o);
  }

  public void saveOrUpdate(T o) {
    getCurrentSession().saveOrUpdate(o);
  }

  public Object merge(Object o) {
    return getCurrentSession().merge(o);
  }

  public void delete(T o) {
    getCurrentSession().delete(o);
  }

  public T deleteById(Serializable id) {
    T o = findById(id);
    getCurrentSession().delete(o);
    return o;
  }

  public void evict(Object o) {
    getCurrentSession().evict(o);
  }

  @SuppressWarnings("unchecked")
  public List<T> findAll() {
    return createCriteria().list();
  }

  @SuppressWarnings("unchecked")
  public T findById(Serializable o) {
    List<T> results = createCriteria().add(Restrictions.idEq(o)).list();
    if (results.isEmpty()) {
      return null;
    } else {
      return results.get(0);
    }
  }

  @SuppressWarnings("unchecked")
  public T load(Serializable o) {
    return (T) getCurrentSession().load(getDomainClass(), o);
  }

  @SuppressWarnings("unchecked")
  public List<T> findBy(Map<String, Object> propertyNameValues) {
    return createCriteria().add(Restrictions.allEq(propertyNameValues)).list();
  }

  @SuppressWarnings("unchecked")
  public List<T> findBy(String propertyName, Object value) {
    return createCriteria().add(Restrictions.eq(propertyName, value)).list();
  }

  @SuppressWarnings("unchecked")
  public List<T> find(SimpleSearchCriteria simpleSearchCriteria) {
    Criteria criteria = createCriteria();
    Iterator<Criterion> criterions = simpleSearchCriteria.iterator();
    while(criterions.hasNext()) {
      criteria.add(criterions.next());
    }
    for(Order o : simpleSearchCriteria.getOrders()) {
      criteria.addOrder(o);
    }
    if(simpleSearchCriteria.getFetchSize() != null) {
      criteria.setFetchSize(simpleSearchCriteria.getFetchSize());
    }
    if(simpleSearchCriteria.getFirstResult() != null) {
      criteria.setFirstResult(simpleSearchCriteria.getFirstResult());
    }
    if(simpleSearchCriteria.getMaxResults() != null) {
      criteria.setMaxResults(simpleSearchCriteria.getMaxResults());
    }
    if(simpleSearchCriteria.getTimeout() != null) {
      criteria.setTimeout(simpleSearchCriteria.getTimeout());
    }
    return criteria.list();
  }

  public T findFirst(SimpleSearchCriteria simpleSearchCriteria) {
    simpleSearchCriteria.setMaxResults(1);
    List<T> results = find(simpleSearchCriteria);
    if(results.isEmpty()) {
      return null;
    } else {
      return results.get(0);
    }
  }

  public Object callNamedQuery(String sql, Map<String, Object> parameter) {
    Query query = getCurrentSession().createSQLQuery(sql);
    for(Entry<String, Object> entry:parameter.entrySet()){
      query.setParameter(entry.getKey(), entry.getValue());
    }
    return query.executeUpdate();
  }
}

这是数据库初始化脚本:

CREATE TABLE "APP_USERS" (
"ID" VARCHAR(36),
"NAME" VARCHAR(50),
"GENDER" VARCHAR(1),
"AGE" NUMERIC(3,0),
"MOBILE_PHONE" VARCHAR(20),
    VERSION INTEGER)

如您所见,这是一个非常常见的Spring TestNG集成测试。但是无法使用自动回滚功能,这使我感到非常困惑。


问题答案:

感谢M.
Deinum。为了解决我的问题,我只需将类AbstractTestNGSpringContextTests替换为AbstractTransactionalTestNGSpringContextTests。

package com.noahwm.hkapp.api;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.testng.Assert;
import org.testng.annotations.Test;

import com.noahwm.hkapp.api.db.dao.AppUserDao;
import com.noahwm.hkapp.api.db.model.AppUser;
import com.noahwm.hkapp.api.service.AppUserService;

@ContextConfiguration(locations = { "classpath:applicationContext-test.xml" })
public class AppUserServiceTestNGTest extends AbstractTransactionalTestNGSpringContextTests {

  @Autowired
  private AppUserService appUserService;

  @Test
  @Rollback
  @Transactional
  public void testApp() {
    AppUser appUser = new AppUser();
    appUser.setAge(10);
    appUser.setGender("F");
    appUser.setMobilePhone("13219201034");
    appUser.setName("HKAPP Test");
    appUserService.createUser(appUser);
    String appUserId = appUser.getId();
    Assert.assertNotNull(appUserId);
  }
}


 类似资料:
  • 在我的一般问题之后,我有一个使用Spring的特定问题,我想在下面每次执行DAO方法后回滚特定的测试方法。 添加和未能回滚插入 此外,在之前/之后获取连接并回滚也不会产生影响 我应该如何使用TestNG框架回滚单元测试?大多数答案使用JUnit的 我未能使用TestNG自动接线: 但是成功地使用了和include配置类includes jdbcTemplate/DataStource TestNG

  • 问题内容: 有没有一种方法可以不使用ROLLBACK来回滚到事务的先前状态? 信息: ROLLBACK TRANSACTION请求没有相应的BEGIN TRANSACTION。 任何输入都会有很大的帮助。 谢谢 !!! 问题答案: 要在明确将IMPLICIT_TRANSACTIONS设置为ON时扩展gerrytans答案,可以使用ROLLBACK。请参阅与此相关的MSDN doco 。请注意,这不

  • 我们有一个Spring事务回滚问题,其中回滚似乎不起作用 在用注释的服务层方法中,我调用三个不同的类来插入3条记录 中间插入从第四个表执行get以填充描述字段,但此get失败。我希望第一次插入会回滚,但它似乎没有发生 几点: 获取方法抛出运行时异常 我们使用和中定义的。Bean是在中创建的,它被导入到 在层 中没有 注释 我们已经使用了

  • 这很好,但并不总是在代码中抛出运行时异常。因此,我挖掘并发现如下所示的rollbackFor; 现在,我必须更改所有代码,以使用RollBackfor更改@Transactional。但是还有其他方法可以将所有@transaction advice属性更改为rollbackFor=exception.class吗?

  • 下面是一个使用JdbcTemplate的示例。我的问题是,由于runTimeException,doMultipleCalls()在第3步失败,一切是否都会回滚。我只有对多个调用的事务注释,而没有对其他调用的注释。所有人是否共享同一个事务会话?。如果没有,那么如何跨多个服务器传递相同的事务会话?