在写单元测试时,往往需要在数据库中准备对应的测试数据。
我们可以在测试用例中,通过代码的方式往数据库中插入数据。但这么做会使测试代码比较臃肿。
个人觉得,通过sql脚本去导入数据,再结合@Transactional注解对数据进行回滚,是一种更好的方案。
为此,spring为我们准备了很有用的注解@Sql。
@Sql
@Sql注解可以执行SQL脚本,也可以执行SQL语句。它既可以加上类上面,也可以加在方法上面。
默认情况下,方法上的@Sql注解会覆盖类上的@Sql注解,但可以通过@SqlMergeMode注解来修改此默认行为。
@Sql有下面的属性:
- config:与注解@SqlConfig作用一样,用来配置“注释前缀”,“分隔符”等。
- executionPhase:决定SQL脚本或语句什么时候会执行,默认是BEFORE_TEST_METHOD。
- statements:配置要一起执行的SQL语句。
- scripts:配置SQL脚本路径。
- value:scripts的别名,它不能和scripts同时配置,但statements可以。
例如:
@Sql({ "/drop_schema.sql", "/create_schema.sql" })
@Sql(scripts = "/insert_data1.sql", statements = "insert into student(id, name) values (100, 'Shiva')")
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AppConfig.class)
public class SqlTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void fetchRows1() {
List<Map<String, Object>> students = jdbcTemplate.queryForList("SELECT * FROM student");
assertEquals(3, students.size());
}
@Sql("/insert_more_data1.sql")
@Test
public void fetchRows2() {
List<Map<String, Object>> students = jdbcTemplate.queryForList("SELECT * FROM student");
assertEquals(5, students.size());
}
}
drop_schema.sql :
drop table if exists student;
create_schema.sql :
CREATE TABLE student (
id INT NOT NULL,
name VARCHAR(50) NOT NULL,
PRIMARY KEY(id)
);
insert_data1.sql :
insert into student(id, name) values (101, 'Mohan');
insert into student(id, name) values (102, 'Krishna');
insert_more_data1.sql :
insert into student(id, name) values (103, 'Indra');
insert into student(id, name) values (104, 'Chandra');
@SqlConfig
@SqlConfig用于配置如何去解释@Sql注解中指定的Sql脚本。
@SqlConfig可以用于类上,也可以用于方法上。
@Sql注解也有一个config
属性,作用与@SqlConfig相同,不同的是作用域只在对应的@Sql注解范围。它的优先级也大于类注解的@SqlConfig。
- blockCommentStartDelimiter:多行注释开始字符,默认是/*。
- blockCommentEndDelimiter:多行注释结束字符,默认是*/。
- commentPrefix:单行注释前缀,默认是–。
- commentPrefixes:指定多个单行注释前缀,默认是["–"]。
- dataSource:指定脚本执行的数据库的名字,只有在多个数据源时需要指定。
- encoding:指定sql脚本文件的字符编码。
- errorMode:配置错误模式,默认是
SqlConfig.ErrorMode
的DEFAULT
。 - separator:配置脚本语句分隔符,默认是’\n’。
- transactionManager:指定
transactionManager
bean,只有有多个transactionManager
时需要指定。 - transactionMode:指定脚本执行的事务模式,默认是
SqlConfig.TransactionMode
的DEFAULT
。
例子:
@SqlConfig(commentPrefix = "#")
@Sql({ "/drop_schema.sql", "/create_schema.sql" })
@Sql(scripts = { "/insert_data2.sql" })
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AppConfig.class)
public class SqlConfigTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void fetchRows1() {
List<Map<String, Object>> students = jdbcTemplate.queryForList("SELECT * FROM student");
assertEquals(2, students.size());
}
@Sql(scripts = "/insert_more_data2.sql", config= @SqlConfig(commentPrefix = "~"))
@Test
public void fetchRows2() {
List<Map<String, Object>> students = jdbcTemplate.queryForList("SELECT * FROM student");
assertEquals(4, students.size());
}
}
insert_data2.sql :
#Insert initial data
insert into student(id, name) values (101, 'Mohan');
insert into student(id, name) values (102, 'Krishna');
@SqlMergeMode
@SqlMergeMode
可以加在类上,也可以加在方法上。用于指示方法上的@Sql和类上@Sql注解配置是否合并。方法上的@SqlMergeMode注解优先级更高。默认值是SqlMergeMode.MergeMode
的OVERRIDE
。
例:
@SqlMergeMode(MergeMode.MERGE)
@Sql({ "/drop_schema.sql", "/create_schema.sql", "/insert_data1.sql" })
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AppConfig.class)
public class SqlMergeModeTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Sql(statements = "insert into student(id, name) values (100, 'Shiva')")
@Test
public void fetchRows1() {
List<Map<String, Object>> students = jdbcTemplate.queryForList("SELECT * FROM student");
assertEquals(3, students.size());
}
@SqlMergeMode(MergeMode.OVERRIDE)
@Sql("/insert_more_data1.sql")
@Test
public void fetchRows2() {
List<Map<String, Object>> students = jdbcTemplate.queryForList("SELECT * FROM student");
assertEquals(5, students.size());
}
}