2012-01-17 94 views
2

我目前使用DBUnit与Spring一起使用,以便单元测试我的应用程序,但遇到了更新逻辑测试总是失败的问题,因为发生死锁数据库,我不明白为什么会这样。请注意,我已经能够通过删除由@After注释的方法来避开这个问题,因为我使用的是@TransactionConfiguration注解,所以实际上并不需要,但是我担心我误解了关于事务处理工作,因此我希望有人可以指出为什么我总是在运行我的updateTerritory方法时遇到以下异常。JPA实体的更新逻辑在使用DBUnit和Spring时失败

java.sql.SQLTransactionRollbackException: A lock could not be obtained within the time requested 

一两件事,可能有助于指出的是,我能够执行像查询数据库,没有任何锁错误插入新记录的其他行动。另外我使用的是OpenJPA,而spring会将PersistenceUnit注入到我的DAO中。我猜测,在我的DBUnit设置代码(testSetup和testTeardown)中混合使用PersistenceUnit和直接使用数据源可能是问题的一部分。我目前使用Derby作为我的数据库。

我的代码被提供如下:

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(locations = "/applicationContext.xml") 
@TransactionConfiguration(defaultRollback = true) 
public class TerritoryZoneManagerTest { 

@Autowired 
private DataSource unitTestingDataSource; 

@Autowired 
private ITerritoryZoneDaoManager mgr; 

@Before 
public void testSetup() throws DatabaseUnitException, SQLException, 
     FileNotFoundException { 
    Connection con = DataSourceUtils.getConnection(unitTestingDataSource); 
    IDatabaseConnection dbUnitCon = new DatabaseConnection(con); 

    FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder(); 
    IDataSet dataSet = builder 
      .build(new FileInputStream(
        "./src/com.company.territoryzonelookup/dao/test/TerritoryZoneManagerTest.xml")); 

    try { 

     // NOTE: There is no need to use the DatabaseOperation.DELETE 
     // functionality because spring will automatically remove all 
     // inserted records after each test case is executed. 
     DatabaseOperation.REFRESH.execute(dbUnitCon, dataSet); 
    } finally { 
     DataSourceUtils.releaseConnection(con, unitTestingDataSource); 
    } 
} 

    @After 
public void testTeardown() throws DatabaseUnitException, SQLException, 
     FileNotFoundException { 
    Connection con = DataSourceUtils.getConnection(unitTestingDataSource); 
    IDatabaseConnection dbUnitCon = new DatabaseConnection(con); 

    FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder(); 
    IDataSet dataSet = builder 
      .build(new FileInputStream(
        "./src/com.company.territoryzonelookup/dao/test/TerritoryZoneManagerTest.xml")); 

    try { 

     // NOTE: There is no need to use the DatabaseOperation.DELETE 
     // functionality because spring will automatically remove all 
     // inserted records after each test case is executed. 
     DatabaseOperation.DELETE.execute(dbUnitCon, dataSet); 
    } finally { 
     DataSourceUtils.releaseConnection(con, unitTestingDataSource); 
    } 
} 

@Test 
@Transactional 
public void updateTerritory() { 
    TerritoryZone zone = new TerritoryZone(); 
    int id = 1; 
    zone = mgr.getTerritory(id); 

    String newCity = "Congerville"; 
    zone.setCity(newCity); 
    mgr.updateTerritory(zone); 

    zone = mgr.getTerritory(id); 
    Assert.assertEquals(newCity, zone.getCity()); 
} 
} 

DAO对象提供下面以及在是有用的情况。

@Repository 
public class TerritoryZoneDaoManager implements ITerritoryZoneDaoManager { 

/* 
@Autowired 
private EntityManagerFactory emf; 
*/ 

/* 
* @PersistenceUnit EntityManagerFactory emf; 
* 
* @PersistenceContext private EntityManager getEntityManager(){ return 
* emf.createEntityManager(); } 
*/ 

@PersistenceContext 
private EntityManager em; 

private EntityManager getEntityManager() { 
    // return emf.createEntityManager(); 
    return em; 
} 

/* (non-Javadoc) 
* @see com.company.territoryzonelookup.dao.ITerritoryZoneManager#addTerritory(com.company.territoryzonelookup.dao.TerritoryZone) 
*/ 
@Override 
public TerritoryZone addTerritory(TerritoryZone territoryZone) { 
    EntityManager em = getEntityManager(); 
    em.persist(territoryZone); 
    return territoryZone; 
} 

/* (non-Javadoc) 
* @see com.company.territoryzonelookup.dao.ITerritoryZoneManager#getTerritory(int) 
*/ 
@Override 
public TerritoryZone getTerritory(int id) { 
    TerritoryZone obj = null; 
    Query query = getEntityManager().createNamedQuery("selectById"); 
    query.setParameter("id", id); 
    obj = (TerritoryZone) query.getSingleResult(); 
    return obj; 
} 

/* (non-Javadoc) 
* @see com.company.territoryzonelookup.dao.ITerritoryZoneManager#updateTerritory(com.company.territoryzonelookup.dao.TerritoryZone) 
*/ 
@Override 
public TerritoryZone updateTerritory(TerritoryZone territoryZone){ 
    getEntityManager().merge(territoryZone); 
    return territoryZone; 
} 

/* (non-Javadoc) 
* @see com.company.territoryzonelookup.dao.ITerritoryZoneManager#getActiveTerritoriesByStateZipLob(java.lang.String, java.lang.String, java.util.Date, java.lang.String) 
*/ 
@Override 
public List<TerritoryZone> getActiveTerritoriesByStateZipLob(String stateCd, String zipCode, Date effectiveDate, String lobCd){ 
    List<TerritoryZone> territoryList; 

    Query query = getEntityManager().createNamedQuery("selectActiveByZipStateLob"); 
    query.setParameter("zipCode", zipCode); 
    query.setParameter("state", stateCd); 
    query.setParameter("lob",lobCd); 
    query.setParameter("effectiveDate", effectiveDate); 

    territoryList = (List<TerritoryZone>) query.getResultList(); 

    return territoryList; 
} 

/* (non-Javadoc) 
* @see com.company.territoryzonelookup.dao.ITerritoryZoneManager#deleteAll() 
*/ 
@Override 
public void deleteAll(){ 
    Query query = getEntityManager().createNativeQuery("Delete from TerritoryZone"); 
    query.executeUpdate(); 
} 

/*** 
* the load method will remove all existing records from the database and then will reload it using it the data passed. 
* @param terrList 
*/ 
public void load(List<TerritoryZone> terrList){ 
    deleteAll(); 
    for (TerritoryZone terr:terrList){ 
     addTerritory(terr); 
    } 
} 

} 

在此先感谢您的帮助。 杰里米

+0

为什么你在setup/teardown方法中手动释放连接? DataSourceUtils.getConnection应该为您提供线程绑定连接,并由Spring测试框架打开活动事务。这个连接也应该由框架关闭。 – mrembisz 2012-01-17 16:56:50

+0

好点@mrembisz。我已经删除了对DataSourceUtils.releaseConnection的调用,但仍遇到同样的问题。 – jwmajors81 2012-01-17 17:37:15

+0

我可以建议的唯一事情就是启用org.springframework.transaction/jdbc的调试日志记录,并确认事务和连接按预期进行访问。 – mrembisz 2012-01-17 17:51:20

回答

1

jwmajors81

我不知道什么是错与缺乏一些细节你的单元测试代码。

我还使用了spring单元测试和dbunit为我的himvc框架,一个基于spring3和hibernate的RAD框架。这里是我的单元测试超类的代码,

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(locations = {"classpath:config/application-test-config.xml"}) 
@Transactional 
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true) 
public class HiMVCTransactionalUnitTest extends AbstractTransactionalJUnit4SpringContextTests{ 
    @Autowired 
    protected DBUnitHelper dbHelper; 

    protected void loadFixture(){ 

     try{ 
      String fixtureFile=this.dbHelper.getDataFile(); 
      if(fixtureFile==null){ 
       fixtureFile=this.getDefaultXMLFixtureFile(); 
       this.dbHelper.setDataFile(fixtureFile); 
      } 
      if(this.dbHelper.isDataFileExisted()){ 
       if(this.dbHelper.isMSSQL()){ 
        HiMVCInsertIdentityOperation operation=new HiMVCInsertIdentityOperation(DatabaseOperation.CLEAN_INSERT); 
        operation.setInTransaction(true); 
        this.dbHelper.executeDBOperation(operation); 
       }else{ 
        this.dbHelper.executeDBOperation(DatabaseOperation.CLEAN_INSERT); 
       } 
      } 
     }catch(Exception x){ 
      x.printStackTrace(); 
     } 
    } 

... 
} 

我使用@Transactional注释在类的声明,并同时指定transactionManager的。我写了一个DBUnitHelper来包装数据加载的dbunit细节。

这里是一个单元测试样品:

public class MyTest extends HiMVCTransactionalUnitTest{ 
    @Before 
    public void setup(){ 
     super.loadFixture(); 
    } 
    //other testing methods 
} 

希望这些代码有帮助的。