You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Thymeleaf表单下拉框数据无法插入MySQL,手动操作正常求排查方案

问题描述

页面表单包含金额、备注、日期输入框及从数据库加载的下拉框,下拉框显示正常。用户选择下拉项提交后,Transaction表的category字段为NULL,其他字段保存正常,但可在MySQL中手动为该字段赋值。曾出现"Cannot add or update a child row a foreign key constraint fails"错误,现已解决但无报错信息,仅category字段未保存。

相关代码

Thymeleaf表单代码

<form action="#" th:action="@{/api/transaction/saveTransaction/{walletId} (walletId=${id})}"
      th:object="${transaction}" method="POST">
    <input type="text" th:field="*{amount}" placeholder="Enter amount" class="form-control mb-4 col-4">

    <input type="text" th:field="*{note}" placeholder="Enter note" class="form-control mb-4 col-4">
    <input type="date" th:field="*{date}" class="form-control mb-4 col-4">
    <select class="form-control" id="dropOperator">
        <option value="0">Select category</option>
        <option th:each="categories : ${categories}" th:value="${categories.name}"
                th:text="${categories.name}"></option>
    </select>

    <button type="submit" class="btn btn-info col-2"> Save Wallet</button>
</form>

保存表单的Controller代码

@PostMapping("/saveTransaction/{walletId}")
public String saveTransaction(@PathVariable(value = "walletId") long walletId, 
@ModelAttribute("transaction") Transaction transaction) {

    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    UserDetailsImpl user = (UserDetailsImpl) authentication.getPrincipal();
    long userId = user.getId();

    Wallet wallet = walletService.getWalletById(walletId);

    transaction.setWallet(wallet);
    transactionService.saveTransaction(transaction);
    return "redirect:/api/wallet/userWallet/balance/" + userId;
}

显示表单的Controller代码

@GetMapping("/showNewTransactionForm/{id}")
public String showNewTransactionForm(@PathVariable(value = "id") long id, Transaction transaction, Model model) {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    UserDetailsImpl user = (UserDetailsImpl) authentication.getPrincipal();
    long userId = user.getId();
    model.addAttribute("userId", userId);

    model.addAttribute("transaction", transaction);
    List<Category> categories = categoryService.getAllCategories();
    model.addAttribute("categories", categories);

    return "new_transaction";
}

Category实体类

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "category_id")
private Long id;

@Column(name = "category_name")
private String name;

@OneToMany(cascade = CascadeType.ALL, mappedBy = "category")
private List<Transaction> transactions;

Transaction实体类

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "transaction_id")
private Long id;

private double amount;

private String note;

@DateTimeFormat(pattern = "yyyy-MM-dd")
@Column(name = "date")
private Date date;

@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "category_name", referencedColumnName = "category_name")
private Category category;

@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name="wallet_id", nullable=false)
private Wallet wallet;

排查解决建议

  • 修复下拉框与实体的绑定:当前下拉框未通过th:field绑定到Transaction的category属性,导致提交时category参数无法被Spring MVC封装到实体中。修改下拉框代码:

    <select class="form-control" id="dropOperator" th:field="*{category}">
        <option value="">Select category</option>
        <option th:each="cat : ${categories}" th:value="${cat.name}" th:text="${cat.name}"></option>
    </select>
    

    同时需确保CategoryService实现了findByName(String name)方法,让Spring能通过提交的名称查询到对应的Category实体。

  • 手动处理category参数转换:如果Spring无法自动将字符串类型的分类名称转为Category实体,可手动处理:

    1. 修改下拉框的name属性:
      <select class="form-control" id="dropOperator" name="categoryName">
          <option value="">Select category</option>
          <option th:each="cat : ${categories}" th:value="${cat.name}" th:text="${cat.name}"></option>
      </select>
      
    2. 在Controller中接收参数并关联实体:
      @PostMapping("/saveTransaction/{walletId}")
      public String saveTransaction(@PathVariable(value = "walletId") long walletId, 
      @ModelAttribute("transaction") Transaction transaction,
      @RequestParam("categoryName") String categoryName) {
      
          // 原有逻辑...
          if (!categoryName.isEmpty()) {
              Category category = categoryService.getCategoryByName(categoryName);
              transaction.setCategory(category);
          }
          transactionService.saveTransaction(transaction);
          return "redirect:/api/wallet/userWallet/balance/" + userId;
      }
      
  • 优化实体关联配置:当前用分类名称作为外键关联,建议改为用主键category_id关联,更稳定且避免名称重复问题:

    1. 修改Transaction实体的关联配置:
      @ManyToOne(cascade = CascadeType.ALL)
      @JoinColumn(name = "category_id", referencedColumnName = "category_id")
      private Category category;
      
    2. 下拉框使用分类ID作为value:
      <select class="form-control" id="dropOperator" th:field="*{category}">
          <option value="">Select category</option>
          <option th:each="cat : ${categories}" th:value="${cat.id}" th:text="${cat.name}"></option>
      </select>
      
  • 检查Service层保存逻辑:确认transactionService.saveTransaction方法中没有无意中将category字段设为null,确保repository的save方法正确执行。

内容的提问来源于stack exchange,提问作者asu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 16:16:10