Spring Security多用户注册触发MySQL唯一键重复错误咨询
解决MySQL唯一键冲突:Duplicate entry 'USER' for key 'UK_8sewwnpamngi6b1dwaa88askk'
这个错误的核心原因很明确:你的Role表的name字段被设置了唯一约束(从错误里的UK_前缀就能看出来),但你每次注册用户时都在创建一个全新的Role("USER")实例,再通过CascadeType.ALL级联保存到数据库——第一次注册时没问题,会插入一条name为USER的角色记录;第二次注册时,又尝试插入一条相同name的角色,直接触发唯一键冲突。
结合你的代码和需求(允许多个用户拥有相同角色),给你几个关键的解决步骤:
1. 给Role添加Repository,实现角色复用
首先需要一个RoleRepository来查询已存在的角色,避免重复创建:
package com.example.demo.models.services; import com.example.demo.models.Role; import org.springframework.data.jpa.repository.JpaRepository; import java.util.Optional; public interface RoleRepository extends JpaRepository<Role, Integer> { // 根据角色名查询角色 Optional<Role> findByName(String name); }
2. 修改UserService,查询复用角色
把原来每次新建Role的逻辑改成"先查后用":如果角色存在就直接关联,不存在再创建并保存:
package com.example.demo.models.services; import com.example.demo.models.Customer; import com.example.demo.models.Role; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; import java.util.Optional; @Service public class UserService { @Autowired private UserRepository userRepository; @Autowired private RoleRepository roleRepository; // 注入新的RoleRepository public void createUser(Customer customer) { // 查询是否存在USER角色,不存在则创建并保存 Optional<Role> userRoleOpt = roleRepository.findByName("USER"); Role userRole = userRoleOpt.orElseGet(() -> roleRepository.save(new Role("USER"))); List<Role> roles = new ArrayList<>(); BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); customer.setPassword(encoder.encode(customer.getPassword())); roles.add(userRole); customer.setRoles(roles); userRepository.save(customer); } public void createAdmin(Customer customer) { // 同理处理ADMIN角色 Optional<Role> adminRoleOpt = roleRepository.findByName("ADMIN"); Role adminRole = adminRoleOpt.orElseGet(() -> roleRepository.save(new Role("ADMIN"))); List<Role> roles = new ArrayList<>(); BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); customer.setPassword(encoder.encode(customer.getPassword())); roles.add(adminRole); customer.setRoles(roles); userRepository.save(customer); } }
3. 调整关联关系的级联类型(可选但推荐)
原来的@ManyToMany(cascade = CascadeType.ALL)权限太大了——它会级联执行所有操作(包括新增、更新、删除),这在角色已经存在时容易出问题。建议把级联类型改成只做必要的操作,或者完全去掉(如果角色是预先初始化好的):
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) @JoinTable(name = "USER_ROLES", joinColumns = { @JoinColumn(name = "CUSTOMER_EMAIL", referencedColumnName = "email") }, inverseJoinColumns = { @JoinColumn(name = "ROLE_NAME", referencedColumnName = "name") }) private List<Role> roles;
4. 可选:预先初始化角色数据
如果你的系统角色是固定的(比如只有USER和ADMIN),可以在项目启动时就把这些角色插入数据库,这样注册用户时直接查询使用即可。比如用Spring的CommandLineRunner:
package com.example.demo; import com.example.demo.models.Role; import com.example.demo.models.services.RoleRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Component public class RoleInitializer implements CommandLineRunner { @Autowired private RoleRepository roleRepository; @Override public void run(String... args) throws Exception { // 初始化USER角色 if (!roleRepository.findByName("USER").isPresent()) { roleRepository.save(new Role("USER")); } // 初始化ADMIN角色 if (!roleRepository.findByName("ADMIN").isPresent()) { roleRepository.save(new Role("ADMIN")); } } }
这样修改后,每次注册用户时都会复用已有的角色记录,不会再触发唯一键冲突,同时也符合你"多个用户拥有相同角色"的需求。
内容的提问来源于stack exchange,提问作者user2411290
相关产品推荐
相关产品推荐

