SpringBoot Controller获取用户参数创建关联支行账户写入MySQL
银行账户开户功能实现修复方案
现有代码核心问题
- 实体关联映射不符合业务规则:一个支行可以开立多个账户,原代码用
@OneToOne做一对一映射完全错误,正确关联关系应为支行对账户是一对多,账户对支行是多对一 - 开户逻辑缺失核心步骤:原代码没有根据用户输入的支行名称查询已存在的上线支行,也没有给账户对象设置关联的支行,保存时外键字段为空或写入错误数据
- 级联配置错误:账户到支行的关联配置了
CascadeType.ALL,会导致删除/修改账户时连带操作支行数据,引发数据异常 - 缺少基础校验:支行创建没有重名校验,开户没有支行存在性校验
分步修复实现
1. 修正实体类关联映射
首先调整BranchDetails实体,把原一对一关联改为一对多:
@Entity @Table( name = "branchdetails") public class BranchDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer branchId; // 加唯一约束,避免同名支行重复创建 @Column( name = "branchName", unique = true, nullable = false) private String branchName; // 一个支行对应多个账户,mappedBy指向账户实体中关联支行的属性名 @OneToMany(mappedBy = "branchDetails") private List<AccountDetails> accountList = new ArrayList<>(); // 自行补充get、set方法 }
再调整AccountDetails实体,把原一对一关联改为多对一,移除错误的级联配置:
@Entity @Table(name = "accountdetails") public class AccountDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer customerId; @Column(name = "customerName", nullable = false) private String customerName; // 多个账户归属同一个支行,外键为accountdetails表的branchId字段 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "branchId", nullable = false) private BranchDetails branchDetails; // 自行补充get、set方法 }
说明:主键类型从基础类型
int改为包装类Integer,是为了避免默认值0干扰JPA的持久化状态判断;属性名统一遵循Java驼峰命名规范,避免反射、序列化时出现匹配错误。
2. 补充Repository层查询方法
在支行的Repository接口中添加按名称查询支行的方法,用于开户时匹配支行ID:
public interface BranchRepository extends JpaRepository<BranchDetails, Integer> { // 根据支行名称查询已存在的上线支行 Optional<BranchDetails> findByBranchName(String branchName); }
账户Repository正常继承JpaRepository<AccountDetails, Integer>即可,无需额外添加基础方法。
3. 修正Controller层业务逻辑
不要直接用实体类接收前端请求参数,避免恶意参数注入;补全支行校验、关联逻辑:
@Controller public class ApiController{ @Resource private BranchRepository branchRepository; @Resource private AccountRepository accountRepository; @RequestMapping(value = "/branchcreation", method = RequestMethod.POST ,consumes = {"application/x-www-form-urlencoded"}) @ResponseBody public String brCreation(String branchname){ // 校验重名 if(branchRepository.findByBranchName(branchname).isPresent()){ return "支行已存在,创建失败"; } BranchDetails br = new BranchDetails(); br.setBranchName(branchname); branchRepository.save(br); return "Sucesspage"; } @RequestMapping(value = "/accountcreation", method = RequestMethod.POST) @ResponseBody public String AcCreation(String branchname, String customername){ // 第一步:根据用户输入的支行名称查询已存在的支行 Optional<BranchDetails> branchOpt = branchRepository.findByBranchName(branchname); if(!branchOpt.isPresent()){ return "输入的支行不存在,请确认支行名称后重试"; } // 第二步:构建账户对象,设置关联的支行 AccountDetails ad = new AccountDetails(); ad.setCustomerName(customername); ad.setBranchDetails(branchOpt.get()); // 第三步:保存账户,JPA会自动提取支行的branchId写入外键字段 accountRepository.save(ad); return "Sucesspage"; } }
关键注意点
- 不需要手动给账户对象set branchId,只要给关联属性设置从数据库查询到的持久态
BranchDetails对象,JPA保存时会自动提取主键值写入外键字段 - 多对一关联不要配置
CascadeType.ALL,否则会出现保存账户时重复创建支行、删除账户时连带删除支行的严重问题 - 支行名称必须加数据库层面的唯一约束,避免并发场景下创建出同名支行,导致按名称查询时返回多条结果无法匹配
内容的提问来源于stack exchange,提问作者igmani
相关产品推荐
相关产品推荐

