我目前使用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);
}
}
}
在此先感谢您的帮助。 杰里米
为什么你在setup/teardown方法中手动释放连接? DataSourceUtils.getConnection应该为您提供线程绑定连接,并由Spring测试框架打开活动事务。这个连接也应该由框架关闭。 – mrembisz 2012-01-17 16:56:50
好点@mrembisz。我已经删除了对DataSourceUtils.releaseConnection的调用,但仍遇到同样的问题。 – jwmajors81 2012-01-17 17:37:15
我可以建议的唯一事情就是启用org.springframework.transaction/jdbc的调试日志记录,并确认事务和连接按预期进行访问。 – mrembisz 2012-01-17 17:51:20