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

Spring Boot+Thymeleaf应用表单无法保存至MySQL数据库求助

Spring Boot + Thymeleaf CRUD表单无法保存数据到MySQL问题排查

我用Spring Boot和Thymeleaf搭建了一个简易CRUD应用,但提交表单时数据无法保存到MySQL数据库,相关代码如下:

Controller代码

@Controller
public class ClientController {
private static final Logger LOG = LoggerFactory.getLogger(ClientController.class);

@Autowired
private ClientRepository clientRepository;


@GetMapping("/patients")
public String getAllClients(Model model) {
    List<Client> clients = clientRepository.findAll();
    model.addAttribute("clients", clients);
    return "patient/list-patients";
}

@GetMapping("/add-patient")
public String addClient(Model model) {
    model.addAttribute("client", new Client());
    return "patient/add-patient";
}


@PostMapping("/add-patient")
public String addClient(@Valid @ModelAttribute Client client, BindingResult bindingResult) {
    LOG.info("Saving client: {}", client);
    if (bindingResult.hasErrors()) {
    return "patient/add-patient";
    }
    clientRepository.save(client);
    return "redirect:/add-patient";
}

@GetMapping("/edit/{id}")
public String editClient(@PathVariable("id") Long id, Model model) {
    model.addAttribute("client", clientRepository.findById(id).orElse(null));
    return "patient-edit";
}


    @GetMapping("/delete/{id}")
    public String deleteClient(@PathVariable("id") Long id) {
        clientRepository.deleteById(id);
        return "redirect:/patients";
    }
}

Model代码

@Entity
@Data
public class Client {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String patientId;
    
    @Size(min=2, max=30)
    private String firstName;

    @Size(min=2, max=30)
    private String lastName;
    private String otherName;
    private String gender;
    private String email;
    private Date dateOfBirth;
    private String ageGroup;
    private String phoneNumber;
    private String address;
    
//    Identity
    private String cardType;
    private String cardNumber;
    private String iDCardNumber;
    
    
    //getters and setters
}

表单代码

<div>
    <form action="#" th:action="@{/add-patient}" method="post">
    <p>
    <label for="firstName">First Name:</label>
    <input type="text" id="firstName" name="firstName" />
    </p>
    <p>
    <label for="lastName">Last Name:</label>
    <input type="text" id="lastName" name="lastName" />
    </p>
    <p>
    <label for="otherName">Other Name(s):</label>
    <input type="text" id="otherName" name="otherName" />
    </p>
    <p>
    <label for="dateOfBirth">Date of Birth:</label>
    <input type="date" id="dateOfBirth" name="dateOfBirth" />
    </p>
    <p>
    <label for="address">Address:</label>
    <input type="text" id="address" name="address" />
    </p>
    <p>
    <label for="email">Email:</label>
    <input type="email" id="email" name="email" />
    </p>
    <p>
    <label for="phoneNumber">Phone Number:</label>
    <input type="tel" id="phoneNumber" name="phoneNumber" />
    </p>
    <p>
    <label for="gender">Gender:</label>
    <select id="gender" name="gender">
    <option value="Male">Male</option>
    <option value="Female">Female</option>
    <option value="Other">Other</option>
    </select>
    </p>
    <p>
    <label for="ageGroup">Age Group:</label>
    <select id="ageGroup" name="ageGroup">
    <option value="Under 18">Under 18</option>
    <option value="18-25">18 - 25</option>
    <option value="26-35">26 - 35</option>
    <option value="36-45">36 - 45</option>
    <option value="46-55">46 - 55</option>
    <option value="Over 55">Over 55</option>
    </select>
    </p>
    <p>
    <label for="cardType">Card Type:</label>
    <select id="cardType" name="cardType">
    <option value="passport">Passport</option>
    <option value="Ghana Card">Ghana Card</option>
    </select>
    </p>
    <p>
    <label for="cardNumber">Card Number:</label>
    <input type="text" id="cardNumber" name="cardNumber" />
    </p>
    <input type="submit" value="Save" />
    </form>
</div>

排查与解决步骤

  1. 校验错误排查

    • 当前Controller仅判断BindingResult有错误就返回表单,但未打印具体错误信息。在if (bindingResult.hasErrors())块中添加日志打印,查看是否是校验规则触发导致未执行保存:
      bindingResult.getAllErrors().forEach(error -> LOG.error("校验失败: {}", error.getDefaultMessage()));
      
    • 注意firstName和lastName有@Size(min=2)校验,若输入字符数不足2位,会直接触发校验失败,中断保存流程。
  2. 日期类型绑定修复

    • Model中dateOfBirth使用java.util.Date,与表单的type="date"返回的日期格式存在绑定兼容性问题。建议替换为java.time.LocalDate并添加格式化注解:
      @DateTimeFormat(pattern = "yyyy-MM-dd")
      private LocalDate dateOfBirth;
      
  3. Repository层确认

    • 确保ClientRepository正确继承JpaRepository<Client, Long>,否则save()方法无法生效:
      public interface ClientRepository extends JpaRepository<Client, Long> {
      }
      
  4. 数据库配置与日志验证

    • 检查application.properties/application.yml中的MySQL配置是否正确,开启SQL日志查看是否有插入语句执行:
      spring.datasource.url=jdbc:mysql://localhost:3306/你的数据库名?useSSL=false&serverTimezone=UTC
      spring.datasource.username=数据库用户名
      spring.datasource.password=数据库密码
      spring.jpa.hibernate.ddl-auto=update
      spring.jpa.show-sql=true
      
    • 若show-sql未输出INSERT语句,说明代码未走到clientRepository.save()步骤。
  5. 表单字段完整性检查

    • Model中patientId和iDCardNumber字段在表单中没有对应输入项,若数据库中这两个字段被设置为NOT NULL,会导致插入失败。可通过以下方式处理:
      • 在数据库中将这两个字段设置为允许为空;
      • 在表单中添加对应输入框;
      • 在Model字段上添加@Nullable注解(需引入Spring相关依赖)。
  6. 重定向路径优化

    • 当前保存成功后重定向到/add-patient,无法直观看到保存结果。建议改为重定向到列表页/patients:
      return "redirect:/patients";
      
  7. 视图路径修正

    • editClient方法返回的视图名是patient-edit,与其他视图的路径格式(patient/xxx)不一致,需确认视图文件实际路径是否为patient/patient-edit.html,若不是则修正返回值:
      return "patient/patient-edit";
      

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 18:05:23