javadatabasespring-boothibernate-onetomanyspring-thymeleaf

Post Object with nested List object in Spring-boot and Thymeleaf


Spring/Thymeleaf beginner sorry in advance but I have 2 entities Employee and MeetingInfo. Employee has a oneToMany relationship with MeetingInfo so basically an Employee can have many MessageInfo. Using psvm I can add a new Employee with multiple MessageInfo to my database using something like this:

Employee employee1 = new Employee("Employee 1");
MeetingInfo mInfo1 = new MeetingInfo(LocalDate.of(2021, 1, 1), "First Random message");
MeetingInfo mInfo2 = new MeetingInfo(LocalDate.of(2021, 2, 2), "Second Random message");
MeetingInfo mInfo3 = new MeetingInfo(LocalDate.of(2021, 3, 3), "Third Random message");

employee1.getMeetingInfo().add(mInfo1);
employee1.getMeetingInfo().add(mInfo2);
employee1.getMeetingInfo().add(mInfo3);

employeeRepository.save(employee1);

But how can I do this with a form in thymeleaf? I can add just an employee, but cant add a new MeetingInfo object. When I do I get a passException error.

My new_employee.html

<form action="#" th:action="@{/ines/saveEmployee}" th:object="${employee}"
            method="POST">
            <input type="text" th:field="*{name}"
                placeholder="Employee Name" class="form-control mb-4 col-4">
            *** so if I remove between here***
            <input type="date" th:field="*{meetingInfo.meetingDate}"
               placeholder="Message Date" class="form-control mb-4 col-4">

            <input type="text" th:field="*{meetingInfo.message}"
               placeholder="Message" class="form-control mb-4 col-4">
            *** and here***
            *** how can I include a MessageInfo object with a new Employee?***
            <button type="submit" class="btn btn-info col-2">Save Meeting</button>
        </form>

My Controller

@GetMapping("/showNewEmployeeForm")
    public String showNewEmployeeForm(Model model) {
        Employee employee = new Employee();
        model.addAttribute("employee", employee);
        return "meeting/new_employee.html";
    }

    @PostMapping("/saveEmployee")
    public String saveEmployee(@ModelAttribute("employee") Employee employee) {
        employeeService.saveMessage(employee);
        return "redirect:/ines/employees";
    }

Employee

@Entity
@Table(name = "employee")
public class Employee {

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

    @Column(nullable = false)
    private String name;

    @OneToMany(cascade = CascadeType.ALL)
    @JoinColumn(name = "employee_id", referencedColumnName = "employee_id")
    private List<MeetingInfo> meetingInfo = new ArrayList<>();
//Constructors, getters and setters

MeetingInfo

@Entity
@Table(name = "meeting_info")
public class MeetingInfo {

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

    private String message;


    @Column(name = "meeting_date")
    private LocalDate meetingDate;
//Constructors, getters and setters

Solution

  • Saving multiple entities with a single request isn't something that you would usually want to do with your Spring Boot app, however, since I understand that this is for practice only, you could do this by using a single DTO object that would hold the information for both entities:

    public class EmployeeMeetingDTO {
    
       private String employeeName;
    
       private String meetingMessage;
    
       private LocalDate meetingDate;
    
    }
    

    Your controller could then accept just a single DTO entity from the request:

    @PostMapping("/saveEmployee")
    public String saveEmployee(@ModelAttribute("employeeDto") EmployeeMeetingDTO employeeDto) {
        employeeService.saveMessage(employeeDto);
        return "redirect:/ines/employees";
    }
    

    And you can separately create both entities in your EmployeeService class. Your Thymeleaf form would then look something like this:

    <form action="#" th:action="@{/ines/saveEmployee}" th:object="${employeeDto}"
            method="POST">
            <input type="text" th:field="*{employeeName}"
                placeholder="Employee Name" class="form-control mb-4 col-4">
           
            <input type="date" th:field="*{meetingDate}"
               placeholder="Message Date" class="form-control mb-4 col-4">
    
            <input type="text" th:field="*{meetingMessage}"
               placeholder="Message" class="form-control mb-4 col-4">
            
            <button type="submit" class="btn btn-info col-2">Save Meeting</button>
        </form>