Spring Boot+JPA下避免CommandLineRunner重复初始化数据库数据
解决Spring Boot初始化数据重复问题的几种方案
方案1:执行前检查数据是否存在(最直接简单)
在保存初始化数据前,通过Repository的查询方法判断数据是否已存在,仅当数据不存在时才执行创建和保存逻辑。这种方式无需额外表或配置,适配你的场景。
修改DataLoader类代码如下:
@Component @RequiredArgsConstructor public class DataLoader implements CommandLineRunner { private final RoleRepository roleRepository; private final UserRepository userRepository; @Override public void run(String... args) throws Exception { // 检查user角色是否已存在 if (!roleRepository.existsByRoleName("user")) { Role userRole = new Role(); userRole.setRoleName("user"); userRole.setDescription("test user"); roleRepository.save(userRole); // 检查目标用户是否已存在(用唯一标识phoneNumber判断) if (!userRepository.existsByPhoneNumber("xx-232323232323")) { User user = new User(); user.setFirstName("UserFirstName"); user.setLastName("UserLastName"); user.setPhoneNumber("xx-232323232323"); user.addRoleToUser(userRole); userRepository.save(user); } } } }
注意:需要在RoleRepository和UserRepository中添加对应的查询方法,比如existsByRoleName(String roleName)、existsByPhoneNumber(String phoneNumber),Spring Data JPA会自动实现这些方法。
方案2:使用初始化标记表
创建一张专门记录初始化状态的表,比如init_status,启动时先查询该表是否有初始化完成的记录,无记录则执行初始化并插入标记。适合初始化逻辑复杂、需要明确标记完成状态的场景。
- 创建标记表(可通过
schema.sql或实体类实现):
CREATE TABLE IF NOT EXISTS init_status ( id INT PRIMARY KEY AUTO_INCREMENT, init_key VARCHAR(50) UNIQUE NOT NULL, is_completed BOOLEAN NOT NULL DEFAULT FALSE, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
- 修改
DataLoader类:
@Component @RequiredArgsConstructor public class DataLoader implements CommandLineRunner { private final RoleRepository roleRepository; private final UserRepository userRepository; private final EntityManager entityManager; @Override public void run(String... args) throws Exception { // 查询是否已执行过基础数据初始化 String checkSql = "SELECT COUNT(*) FROM init_status WHERE init_key = 'base_data_init' AND is_completed = TRUE"; Long count = (Long) entityManager.createNativeQuery(checkSql).getSingleResult(); if (count == 0) { // 执行初始化逻辑 Role userRole = new Role(); userRole.setRoleName("user"); userRole.setDescription("test user"); roleRepository.save(userRole); User user = new User(); user.setFirstName("UserFirstName"); user.setLastName("UserLastName"); user.setPhoneNumber("xx-232323232323"); user.addRoleToUser(userRole); userRepository.save(user); // 标记初始化完成 String insertSql = "INSERT INTO init_status (init_key, is_completed) VALUES ('base_data_init', TRUE)"; entityManager.createNativeQuery(insertSql).executeUpdate(); entityManager.flush(); } } }
方案3:用data.sql处理关联映射(解决你的顾虑)
如果想用data.sql初始化数据,关联映射可通过SQL直接处理:
- 用自增ID时,借助数据库的自增ID获取函数(MySQL用
1029338,PostgreSQL用currval()) - 手动指定ID时,直接插入对应关联值即可
示例MySQL的data.sql:
-- 插入角色,避免重复 INSERT INTO role (role_name, description) SELECT 'user', 'test user' WHERE NOT EXISTS (SELECT 1 FROM role WHERE role_name = 'user'); -- 插入用户,避免重复 INSERT INTO user (first_name, last_name, phone_number) SELECT 'UserFirstName', 'UserLastName', 'xx-232323232323' WHERE NOT EXISTS (SELECT 1 FROM user WHERE phone_number = 'xx-232323232323'); -- 插入用户角色关联(多对多中间表),避免重复 INSERT INTO user_role (user_id, role_id) SELECT u.id, r.id FROM user u, role r WHERE u.phone_number = 'xx-232323232323' AND r.role_name = 'user' AND NOT EXISTS ( SELECT 1 FROM user_role ur WHERE ur.user_id = u.id AND ur.role_id = r.id );
注意:data.sql默认在Spring Boot启动时执行,可通过spring.sql.init.mode配置执行时机,同样需要用WHERE NOT EXISTS避免重复插入。
内容的提问来源于stack exchange,提问作者Syed Iftekharuddin
相关产品推荐
相关产品推荐

