PreparedStatement.execute() 在 Spring 单元测试中挂起
我在 Spring MVC 应用程序中进行单元测试时遇到了一些问题。坦率地说,由于我缺乏从头开始编写测试套件的经验,因此我很可能错误地设计了单元测试。
我目前设计的方式是,例如,为了测试用户服务,测试套件使用原始 SQL 语句来验证数据是否正确插入/检索/更新。我遇到的问题是,执行第一个准备好的语句后,后续语句将挂在 execute()
方法上。测试的结果最终是“超出锁定等待超时;尝试重新启动事务”
根据我在网上阅读的内容,这可能是一个事务管理问题,有人没有释放锁,但是我不知道如何最好地做到这一点,甚至不知道在哪里做到这一点。
下面是一些相关代码,如果需要更多代码,请告诉我。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"/applicationContext-base.xml", "/application-security.xml"})
@TransactionConfiguration(transactionManager="txManager")
@Transactional
public class TestUserService {
@Autowired
UsersService userService;
@Autowired
DataSource dataSource;
Connection connection;
@Before
public void setup() throws Exception{
connection = dataSource.getConnection();
}
@Test
public void testCreateUser() throws Exception{
Collection<GrantedAuthorityImpl> auths = new ArrayList<GrantedAuthorityImpl>();
auths.add(new GrantedAuthorityImpl(SecurityConstants.ROLE_USER));
User user = new User("testUser", "testpassword", true, true, true, true, auths, "salt");
User tmp = userService.createUser(user);
PreparedStatement ps = connection.prepareStatement("select id, username, password, created, enabled, salt from users where id = ?");
PreparedStatement ps2 = connection.prepareStatement("select user, authority from user_authorities where user = ?");
ps.setLong(1, tmp.getId());
ps2.setLong(1, tmp.getId());
ResultSet rs = ps.executeQuery();
ResultSet rs2 = ps2.executeQuery();
rs.first();
rs2.first();
Collection<GrantedAuthorityImpl> authsFromDb = new ArrayList<GrantedAuthorityImpl>();
rs.first();
do{
authsFromDb.add(new GrantedAuthorityImpl(rs2.getString("authority")));
}while(rs2.next());
User tmp2 = new User(rs.getString("username"), rs.getString("password"), rs.getBoolean("enabled"), true, true, true, authsFromDb, rs.getString("salt"));
Assert.assertEquals(tmp.getUsername(), tmp2.getUsername());
Assert.assertEquals(tmp.getId(), tmp2.getId());
Assert.assertEquals(tmp.getPassword(), tmp2.getPassword());
Assert.assertEquals(tmp.getSalt(), tmp2.getSalt());
Assert.assertEquals(tmp.getAuthorities(), tmp2.getAuthorities());
Assert.assertEquals(tmp.isEnabled(), tmp2.isEnabled());
}
@Test
public void testSaveUser() throws Exception{
long createdTime = System.currentTimeMillis();
String insertionQry = "insert into users (username, password, created, enabled, salt) values ('chris', 'somepassword'," + createdTime + ",1,'salt')";
PreparedStatement ps = connection.prepareStatement(insertionQry, Statement.RETURN_GENERATED_KEYS);
ps.execute();
ResultSet rs = ps.getGeneratedKeys();
rs.first();
long id = rs.getLong(1);
Assert.assertEquals(true, id != 0);
String loadQry = "select id, username, password, created, enabled, salt from users where id = " + id;
ps = connection.prepareStatement(loadQry);
rs = ps.executeQuery();
rs.first();
Assert.assertEquals(rs.getString("username"), "chris");
Assert.assertEquals(rs.getString("password"), "somepassword");
Assert.assertEquals(rs.getBoolean("enabled"), true);
Assert.assertEquals(rs.getString("salt"), "salt");
User user = new User("second_username", "newpassword", false, true, true, true, AuthorityUtils.NO_AUTHORITIES, "secondsalt");
user.setId(rs.getLong("id"));
userService.saveUser(user);
ps = connection.prepareStatement(loadQry);
rs = ps.executeQuery();
rs.first();
Assert.assertEquals(rs.getString("username"), "second_username");
Assert.assertEquals(rs.getString("password"), "newpassword");
Assert.assertEquals(rs.getBoolean("enabled"), false);
Assert.assertEquals(rs.getString("salt"), "secondsalt");
}
I'm having some trouble with my unit tests in a Spring MVC application. In full disclosure, there's a good chance I'm designing my unit tests incorrectly given my lack of experience writing a test suite from the ground up.
The way I have it designed currently is, for instance, to test a user service, the test suite uses raw SQL statements to verify the data is inserted/retrieved/updated correctly. The problem I'm having is that after the frist prepared statement is executed, subsequent statements hang on the execute()
method. The result of the test ends up being "Lock wait timeout exceeded; try restarting transaction"
Based on what I've read online, this is likely a transaction management issue and somebody isn't releasing a lock but I'm not sure how best to do that or even where to do that.
Some relevant code is below, let me know if more code is necessary.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"/applicationContext-base.xml", "/application-security.xml"})
@TransactionConfiguration(transactionManager="txManager")
@Transactional
public class TestUserService {
@Autowired
UsersService userService;
@Autowired
DataSource dataSource;
Connection connection;
@Before
public void setup() throws Exception{
connection = dataSource.getConnection();
}
@Test
public void testCreateUser() throws Exception{
Collection<GrantedAuthorityImpl> auths = new ArrayList<GrantedAuthorityImpl>();
auths.add(new GrantedAuthorityImpl(SecurityConstants.ROLE_USER));
User user = new User("testUser", "testpassword", true, true, true, true, auths, "salt");
User tmp = userService.createUser(user);
PreparedStatement ps = connection.prepareStatement("select id, username, password, created, enabled, salt from users where id = ?");
PreparedStatement ps2 = connection.prepareStatement("select user, authority from user_authorities where user = ?");
ps.setLong(1, tmp.getId());
ps2.setLong(1, tmp.getId());
ResultSet rs = ps.executeQuery();
ResultSet rs2 = ps2.executeQuery();
rs.first();
rs2.first();
Collection<GrantedAuthorityImpl> authsFromDb = new ArrayList<GrantedAuthorityImpl>();
rs.first();
do{
authsFromDb.add(new GrantedAuthorityImpl(rs2.getString("authority")));
}while(rs2.next());
User tmp2 = new User(rs.getString("username"), rs.getString("password"), rs.getBoolean("enabled"), true, true, true, authsFromDb, rs.getString("salt"));
Assert.assertEquals(tmp.getUsername(), tmp2.getUsername());
Assert.assertEquals(tmp.getId(), tmp2.getId());
Assert.assertEquals(tmp.getPassword(), tmp2.getPassword());
Assert.assertEquals(tmp.getSalt(), tmp2.getSalt());
Assert.assertEquals(tmp.getAuthorities(), tmp2.getAuthorities());
Assert.assertEquals(tmp.isEnabled(), tmp2.isEnabled());
}
@Test
public void testSaveUser() throws Exception{
long createdTime = System.currentTimeMillis();
String insertionQry = "insert into users (username, password, created, enabled, salt) values ('chris', 'somepassword'," + createdTime + ",1,'salt')";
PreparedStatement ps = connection.prepareStatement(insertionQry, Statement.RETURN_GENERATED_KEYS);
ps.execute();
ResultSet rs = ps.getGeneratedKeys();
rs.first();
long id = rs.getLong(1);
Assert.assertEquals(true, id != 0);
String loadQry = "select id, username, password, created, enabled, salt from users where id = " + id;
ps = connection.prepareStatement(loadQry);
rs = ps.executeQuery();
rs.first();
Assert.assertEquals(rs.getString("username"), "chris");
Assert.assertEquals(rs.getString("password"), "somepassword");
Assert.assertEquals(rs.getBoolean("enabled"), true);
Assert.assertEquals(rs.getString("salt"), "salt");
User user = new User("second_username", "newpassword", false, true, true, true, AuthorityUtils.NO_AUTHORITIES, "secondsalt");
user.setId(rs.getLong("id"));
userService.saveUser(user);
ps = connection.prepareStatement(loadQry);
rs = ps.executeQuery();
rs.first();
Assert.assertEquals(rs.getString("username"), "second_username");
Assert.assertEquals(rs.getString("password"), "newpassword");
Assert.assertEquals(rs.getBoolean("enabled"), false);
Assert.assertEquals(rs.getString("salt"), "secondsalt");
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
为了将原始 JDBC
Connection
与 Spring 事务管理结合使用,您需要以DataSourceUtils.getConnection(dataSource)
的形式获取它们,请参阅DataSourceTransactionManagement
。也许这就是原因。因此,问题是通过
dataSource.getConnection()
获取并在测试代码中使用的Connection
与代码中使用的 Spring 管理的连接不同已测试。因此,在这些连接中执行的查询属于不同的事务,并且从单个线程在多个事务中执行查询通常会导致死锁。使用 DataSourceUtils 时,您将获得与正在测试的代码相同的 Spring 管理连接,以便所有查询都在单个事务中执行。
In order to use raw JDBC
Connection
s with Spring transaction management you need to obtain them asDataSourceUtils.getConnection(dataSource)
, seeDataSourceTransactionManagement
. Perhaps that's the cause.So, the problem is that the
Connection
obtained viadataSource.getConnection()
and used in the test code is not the same as a Spring-managed connection used in the code being tested. Thus, queries executed in these connections belong to different transactions, and executing queries in many transactions from a single thread often leads to deadlocks.When using
DataSourceUtils
you get the same Spring-managed connection as the code being tested, so that all your queries are executed in a single transaction.我发现您的测试有几个问题:
I see several problems with your testing: