SpringBoot集成H2数据库
前言
1、场景
在写DAO层的单元测试时,我们往往会遇到一个问题,测试用例所依赖的数据库数据被修改或删除了,或者在一个新的环境下所依赖的数据库不存在,导致单元测试无法通过。在这种情况下,使用H2内存数据库来模拟数据库环境是一个很好的解决方案。
2、H2 特点
- 只有一个jar文件,适合作为嵌入式数据库使用
- 支持标准SQL和JDBC
- 可以用于单元测试,启动很快,每一个用例执行完会自动删除内存中的数据
上代码
使用maven工程来搭建测试环境,工程目录如下:
1、核心依赖
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.0.RELEASE</version> </parent> <dependencies> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> </dependencies>
2、配置文件 application.yml
server: port: 18095 spring: application: name: service-h2 jpa: show-sql: true #打印sql datasource: url: jdbc:h2:mem:xwj_db;MODE=MySQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE #mem:连接到内存 driver-class-name: org.h2.Driver username: root password: 123456 data: classpath:db/data.sql #初始化表数据
参数说明:
- jdbc:h2:mem 使用h2的内存数据库,还有file等其它方式
- xwj_db 数据库名称
- MODE=MySQL 以 MySQL 的模式运行
3、实体类
@Entity @Getter @Setter @ToString @NoArgsConstructor public class XwjUser implements Serializable { private static final long serialVersionUID = -2169427939264532306L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; /** 名称 */ public String name; /** 年龄 */ private Integer age; private Date createDate; }
4、Repository
public interface UserRepository extends JpaRepository<XwjUser, Long>{ }
5、Service
@Service public class UserService { @Autowired private UserRepository userRepository; @Autowired private JdbcTemplate jdbcTemplate; public List<XwjUser> findAll() { return userRepository.findAll(); } public XwjUser save(XwjUser user) { return userRepository.save(user); } /** * 通过主键id查询 */ public XwjUser findById(Long id) { Optional<XwjUser> optional = userRepository.findById(id); if (optional.isPresent()) { return optional.get(); } return null; } /** * 通过名称查询 */ public List<XwjUser> findByName(String name) { String sql = "select * from xwj_user where name like ?"; Object[] paramArr = new Object[1]; paramArr[0] = name + "%"; return jdbcTemplate.query(sql, paramArr, new BeanPropertyRowMapper<>(XwjUser.class)); } }
测试
1、在 src/main/resources 目录下,创建一个目录 /db 和脚本文件 data.sql :
insert into xwj_user (id, name, age, create_date) values(11,'张三','12','2020-04-17 00:00:00'); insert into xwj_user (id, name, age, create_date) values(12,'李四','13','2020-04-17 00:00:00'); insert into xwj_user (id, name, age, create_date) values(13,'王五','25','2020-04-17 00:00:00'); insert into xwj_user (id, name, age, create_date) values(14,'赵六','17','2020-04-17 00:00:00'); insert into xwj_user (id, name, age, create_date) values(15,'吉米','30','2020-04-17 00:00:00');
2、在 src/test/java 目录下,创建一个测试类:
@RunWith(SpringRunner.class) @SpringBootTest public class H2Test { @Autowired private UserService userService; /** * 测试查询所有 */ @Test public void testFind() { List<XwjUser> list = userService.findAll(); System.out.println("list: " + list); } /** * 测试新增+查询 */ @Test public void testInsertAndFind() { // 1、新增 XwjUser user = new XwjUser(); user.setAge(19); user.setName("张三"); user.setCreateDate(new Date()); XwjUser newUser = userService.save(user); // 2、通过id查询 Long id = newUser.getId(); System.out.println("findById: " + userService.findById(id)); // 3、模糊查询(自定义sql) String name = "张"; System.out.println("findByName: " + userService.findByName(name)); } }
3、测试 H2Test#testFind 方法,日志如下:
4、测试 H2Test#testInsertAndFind 方法,日志如下:
踩坑
1、在配置文件不要配置 schema,也不要直接将 schema.sql 脚本直接放在 src/main/resources 目录下,否则初始化数据脚本(即上面的data.sql)不会生效。
问题现象:执行方法的过程中没有任何报错,schema 和 data 脚本都会被执行(故意写错脚本会抛出异常)
问题原因:待进一步分析
知识改变世界
【推荐】100%开源!大型工业跨平台软件C++源码提供,建模,组态!
【推荐】AI 的力量,开发者的翅膀:欢迎使用 AI 原生开发工具 TRAE
【推荐】2025 HarmonyOS 鸿蒙创新赛正式启动,百万大奖等你挑战
· 通过 Canvas 将后端发来的一帧帧图片渲染成“视频”的实现过程
· 当加密ID需要变成Guid:为什么我选择了AES-CBC而非GCM?
· 基于 epoll 的协程调度器——零基础深入浅出 C++20 协程
· 下划线字段在golang结构体中的应用
· SQL Server也能玩正则表达式?
· C#实现屏幕墙:同时监控多个电脑桌面(支持Windows、信创Linux、银河麒麟、统信UOS)
· 我的AI自学路线,可能对你有用
· 通过Canvas在网页中将后端发来的一帧帧图片渲染成“视频”的实现过程
· 如何做一个纯净版的ABP vNext 脚手架
· 告别 DOM 的旧时代:从零重塑 Web 渲染的未来