我正在使用JUnit4和AbstractTransactionalJUnit4SpringContextTests,并且想要创建简单的CRUD单元测试 - 如下所示。但是,对SQL更新的调用正在导致事务管理器为我的测试实例生成一个新的ID。由于在更新过程中没有传回新ID,因此我不再拥有测试行的主键 - 这会阻止测试的其余部分。使用JUnit在Spring中测试CRUD时获取同一行的多个ID
有没有办法阻止事务管理器在更新被调用时为本书生成新的ID? (做不到这一点是有没有更好的方法来测试CRUD?)
@Test
public void testCRUDBook() {
Book b1 = new Book(title, author);
BookFactory factory = database.getBookFactory();
int id = factory.createBook(b1);
Book b2 = factory.readBook(id);
assertEquals(b1.getTitle(), b2.getTitle());
assertEquals(b1.getAuthor(), b2.getAuthor());
b2.setTitle("title 2");
b2.setAuthor("author 2");
assertTrue(factory.updateBook(b2));
// The problem arises here as updating the book record causes a new Id
// to be generated so querying by Id is no longer possible.
Book b3 = factory.readBook(b2.getId());
assertEquals(b3.getTitle(), "title 2");
assertEquals(b3.getAuthor(), "author 2");
assertTrue(factory.deleteBook(b3));
}
书看起来是这样的:
public class Book {
private int id;
private String title;
private String author;
public Book() {}
// NEW
public Book(String title, String author) {
this.title = title;
this.author = author;
}
// READ
public Book(int id, String title, String author) {
this.id = id;
this.title = title;
this.author = author;
}
// GENERIC ACCESSORS (left out for brevity)
}
为了完整 - 工厂:
public class BookFactory extends BaseDatabaseFactory {
@Transactional
public int createBook(final Book b) {
final String insertSQL = "INSERT INTO book (author, title) VALUES (?, ?)";
KeyHolder keyHolder = new GeneratedKeyHolder();
try {
int update = jdbcTemplate.update(
new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
PreparedStatement ps = connection.prepareStatement(insertSQL, PreparedStatement.RETURN_GENERATED_KEYS);
ps.setString(1, b.getTitle());
ps.setString(2, b.getAuthor());
return ps;
}
},
keyHolder);
if (update != 1) {
throw new IllegalStateException("Adding book record to database resulted in " + update + " records.");
}
return keyHolder.getKey().intValue();
} catch (DataAccessException ex) {
String msg = "Falied to create new book:" + b.toString() + "ex:" + ex.getMessage();
throw new RuntimeException(msg);
}
}
private static class BookRowMapper implements RowMapper {
@Override
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
Book b = new Book();
b.setId(rs.getInt("id"));
b.setTitle(rs.getString("title"));
b.setAuthor(rs.getString("author"));
return b;
}
}
@Transactional
public Book readBook(int id) {
Book b = null;
try {
String sql = "SELECT * FROM book WHERE id = " + id;
b = (Book) jdbcTemplate.queryForObject(sql, new Object[]{}, new BookRowMapper());
} catch (DataAccessException ex) {
throw new RuntimeException("Falied to locate book.", ex);
}
return b;
}
@Transactional
public boolean updateBook(final Book b) {
final String updateSQL = "UPDATE book SET author = ?, title = ? WHERE id = ?";
try {
jdbcTemplate.update(
new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
PreparedStatement ps = connection.prepareStatement(updateSQL);
ps.setString(1, b.getAuthor());
ps.setString(2, b.getTitle());
ps.setInt(3, b.getId());
return ps;
}
});
} catch (DataAccessException ex) {
log.error("Falied to update Book:" + b.toString(), ex);
return false;
}
return true;
}
@Transactional
public boolean deleteBook(final Book b) {
try {
jdbcTemplate.update("DELETE FROM book WHERE id = ?", b.getId());
} catch (DataAccessException ex) {
log.error("Falied to delete Book:" + b.toString(), ex);
return false;
}
return true;
}
}
错误看起来很熟悉,您可以发布Book的代码吗? – Reimeus 2012-07-11 21:30:22
@Reimeus当然,非常喜欢POJO。你想让我发布工厂吗? – Colin 2012-07-11 21:42:36