【发布时间】:2011-07-04 04:28:53
【问题描述】:
我正在尝试测试异步运行的服务方法 (@Async)。
这里是异步方法:
@Async
@Transactional(propagation=Propagation.SUPPORTS, isolation = Isolation.READ_UNCOMMITTED)
public Future<UserPrefs> checkLanguagePreference(long id) {
UserPrefs prefs = prefsDao.retrieveUserPreferences(id);
if(prefs == null || !StringUtils.hasLength(prefs.getLanguage())) {
//Save a new sms-command object
SmsBean command = SmsHelper.buildSmsCommand();
if(! smsDao.checkSameCommandExists(id, command)) {
smsDao.saveSms(id, new SmsBean[] {command}); //Will wait until Lock wait timeout
}
}
return new AsyncResult<UserPrefs>(prefs);
}
这里是调用异步的测试方法:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(location = "...")
@TransactionConfiguration(transactionManager = "txManager", defaultRollback = false)
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@TestExecutionListeners( { DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class })
public class MessagingServiceTest {
@Before
public void setUp() {
//Avant tout mettre tout les sms en lu
smsDao.deleteAllSms(1);
sessionFactory.getCurrentSession().flush();
//On vérifie bien qu'il n y a plus de sms
List<SmsBean> list = smsDao.getNewSmsList(1);
assertEquals(0,list.size());
}
@Test
public void checkLanguagePreferenceTest() throws InterruptedException, ExecutionException {
User user = (User) sessionFactory.getCurrentSession().load(User.class, new Long(1));//idUser = 1
// We explicitly blank the preference from db
prefsDao.saveLanguagePref(new UserPrefs("",user));
Future<UserPrefs> prefs = messagingService.checkLanguagePreference(user.getId());
System.out.println("wait completion of async task");
prefs.get();
System.out.println("Async task has finished");
}
}
执行 prefs.get() 时,出现此错误:
原因:org.springframework.orm.hibernate3.HibernateJdbcException:Hibernate 数据访问时的 JDBC 异常:SQL 的 SQLException [插入 SmsBean(目标、消息、来源、sens、状态、USER_ID)值(?、?、? , ?, ?, ?)]; SQL 状态 [41000];错误代码[1205];无法插入:
引起:java.sql.SQLException: Lock wait timeout exceeded;尝试重启事务
在 com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1075)
在 com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3562)
在 com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3494)
在 com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1960)
在 com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2114)
在 com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2696)
在 com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2105)
在 com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2398)
在 com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2316)
在 com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2301)
在 org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:101)
在 org.hibernate.id.IdentityGenerator$GetGeneratedKeysDelegate.executeAndExtract(IdentityGenerator.java:94)
在 org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:57)
... 39 更多
出现这种情况是因为 setup 方法中的 smsDao.deleteAllSms 持有 sms 表的锁。
我怎样才能正确避免这种锁定超时并能够成功运行我的测试?
感谢您的帮助。
仅供参考,这是一些控制台输出:
调试 - 添加带有属性的事务方法“checkLanguagePreferenceTest”:PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED; '' 调试 - 显式事务定义 [PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED; ''] 找到测试上下文 [[TestContext@b76fa testClass = MessagingServiceTest, locations = array['file:src/main/resources/myapp-context.xml', 'file:src/main/resources/myapp-data.xml ', 'file:src/main/resources/myapp-services.xml'], testInstance = fr.myapp.service.MessagingServiceTest@b01d43, testMethod = checkLanguagePreferenceTest@MessagingServiceTest, testException = [null]]] 调试 - 为测试类 [class fr.myapp.service.MessagingServiceTest] 检索到 @TransactionConfiguration [@org.springframework.test.context.transaction.Tran sactionConfiguration(defaultRollback=false, transactionManager=txManager)] 调试 - 为类 [class fr.myapp.service.MessagingServiceTest] 检索到 TransactionConfigurationAttributes [[TransactionConfigurationAttributes@5f7d3f transactionManagerName = 'txManager', defaultRollback = false]] 调试 - 返回单例 bean 'txManager' 的缓存实例 调试 - 创建名称为 [checkLanguagePreferenceTest] 的新事务:PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED; '' 调试 - 为 Hibernate 事务打开新会话 [org.hibernate.impl.SessionImpl@666a53] 调试 - 准备 Hibernate 会话的 JDBC 连接 [org.hibernate.impl.SessionImpl@666a53] 调试 - 将 JDBC 连接 [org.apache.commons.dbcp.PoolableConnection@1bde3d2] 的隔离级别更改为 2 调试 - 将 Hibernate 事务公开为 JDBC 事务 [org.apache.commons.dbcp.PoolableConnection@1bde3d2] 调试 - 没有方法级 @Rollback 覆盖:使用默认回滚 [false] 测试上下文 [[TestContext@b76fa testClass = MessagingServiceTest, locations = array['file:src/main/resources/myapp-context.xml', 'file :src/main/resources/myapp-data.xml', 'file:src/main/resources/myapp-services.xml'], testInstance = fr.myapp.service.MessagingServiceTest@b01d43, testMethod = checkLanguagePreferenceTest@MessagingServiceTest, testException = [空]]] INFO - 开始事务(1):事务管理器 [org.springframework.orm.hibernate3.HibernateTransa ctionManager@17753a8];回滚 [假] 休眠:从 SmsBean 中删除 USER_ID=? 休眠:选择 user0_.id 作为 id3_1_,user0_.email 作为 email3_1_,user0_.login 作为 login3_1_,user0_.passwd 作为 passwd3_1_,smsbeans1_.USER_ID 作为 USER7_3_3_,smsbeans1_.id 作为 id3_,smsbeans1_.id 作为 id0_0_,smsbeans1_.destination 作为 destinat , smsbeans1_.message 作为 message0_0_, smsbeans1_.origin 作为 origin0_0_, smsbeans1_.sens 作为 sens0_0_, smsbeans1_.status 作为 status0_0_, smsbeans1_.USER_ID 作为 USER7_0_0_ 来自用户 user0_ 左外连接 SmsBean smsbeans1_ on user0_.id=smsbeans1_.USER_id =? 休眠:从用户 user0_ 中选择 user0_.id 作为 id3_,user0_.email 作为 email3_,user0_.login 作为 login3_,user0_.passwd 作为 passwd3_ 从用户 user0_ where user0_.login=? 休眠:从 user_prefs userprefs0_ where userprefs0_.USER_ID=? 等待异步任务完成 调试 - 返回单例 bean 'txManager' 的缓存实例 信息 - 检查语言偏好(1) 调试 - 打开休眠会话 调试 - 为新的 Hibernate 会话注册 Spring 事务同步 休眠:从 user_prefs userprefs0_ where userprefs0_.USER_ID=? 休眠:选择 user0_.id 作为 id3_1_,user0_.email 作为 email3_1_,user0_.login 作为 login3_1_,user0_.passwd 作为 passwd3_1_,smsbeans1_.USER_ID 作为 USER7_3_3_,smsbeans1_.id 作为 id3_,smsbeans1_.id 作为 id0_0_,smsbeans1_.destination 作为 destinat , smsbeans1_.message 作为 message0_0_, smsbeans1_.origin 作为 origin0_0_, smsbeans1_.sens 作为 sens0_0_, smsbeans1_.status 作为 status0_0_, smsbeans1_.USER_ID 作为 USER7_0_0_ 来自用户 user0_ 左外连接 SmsBean smsbeans1_ on user0_.id=smsbeans1_.USER_id =? INFO - 检查相同的短信命令是否已经存在 Hibernate: select * from smsbean S where S.USER_ID=?和 S.status=?和 S.message=? DEBUG - 在事务同步时刷新 Hibernate Session //这里死锁: Hibernate: 插入 SmsBean (destination, message, origin, sens, status, USER_ID) 值 (?, ?, ?, ?, ?, ?) 调试 - 关闭休眠会话 58799 [SimpleAsyncTaskExecutor-1] 警告 org.hibernate.util.JDBCExceptionReporter - SQL 错误:1205,SQLState:41000 58799 [SimpleAsyncTaskExecutor-1] 错误 org.hibernate.util.JDBCExceptionReporter - 超过锁定等待超时;尝试重启事务已解决,但仅供参考,我之前在 MySQL 论坛上创建了一个线程,从 DBMS 的角度来看我为什么会出现这种死锁。这是链接(也很好解释):
http://forums.mysql.com/read.php?97,409237,409237#msg-409237
【问题讨论】:
标签: java mysql hibernate spring junit