Two entities: Person, Pet.
One Person have multi pets, linked by ownerId. Owner
class extends Person
class. Person
class extends BaseEntity
. BaseEntity
used to generate unique id value. Using thymeleaf web part to input/insert more pets for one specific owners. Pets do inserted to the h2 database. but the ownerId is null.That’s the issue.
Repository code:
public interface OwnerRepository extends JpaRepository<Owner, Long>{} public interface PetRepository extends JpaRepository<Pet, Long> {}
Full Code: https://github.com/jianheMark/springpetsimplified
BaseEntity code:
@Getter @Setter @NoArgsConstructor @AllArgsConstructor @MappedSuperclass public class BaseEntity implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; public boolean isNew() { return this.id == null; } }
PetController partial code:
@GetMapping("/pets/new") public String initCreationForm(Owner owner, Model model) { Pet pet = new Pet(); owner.getPets().add(pet); pet.setOwner(owner); model.addAttribute("pet", pet); return VIEWS_PETS_CREATE_OR_UPDATE_FORM; } @PostMapping("/pets/new") public String processCreationForm(Owner owner, Pet pet, BindingResult result, ModelMap model) { if (StringUtils.hasLength(pet.getName()) && pet.isNew() && owner.getPet(pet.getName(), true) != null){ result.rejectValue("name", "duplicate", "already exists"); } owner.getPets().add(pet); if (result.hasErrors()) { model.put("pet", pet); return VIEWS_PETS_CREATE_OR_UPDATE_FORM; } else { petService.save(pet); return "redirect:/owners/" + owner.getId(); } }
Owner partial code:
@Setter @Getter @NoArgsConstructor @Entity @Table(name = "owners") public class Owner extends Person { @Builder public Owner(Long id, String firstName, String lastName, String address, String city, String telephone, Set<Pet> pets) { super(id, firstName, lastName); this.address = address; this.city = city; this.telephone = telephone; if(pets != null) { this.pets = pets; } } @Column(name = "address") private String address; @Column(name = "city") private String city; @Column(name = "telephone") private String telephone; @OneToMany(cascade = CascadeType.ALL, mappedBy = "owner") private Set<Pet> pets = new HashSet<>();
Pet code:
@Setter @Getter @NoArgsConstructor @AllArgsConstructor @Entity @Table(name = "pets") public class Pet extends BaseEntity{ @Builder public Pet(Long id, String name, Owner owner, LocalDate birthDate) { super(id); this.name = name; this.owner = owner; this.birthDate = birthDate; } @Column(name = "name") private String name; @ManyToOne @JoinColumn(name = "owner_id") private Owner owner; @Column(name = "birth_date") @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate birthDate; }
Advertisement
Answer
In Pet Controller you save the pet
but you do not set the owner
of the pet.
Change your code:
@PostMapping("/pets/new") public String processCreationForm(Owner owner, Pet pet, BindingResult result, ModelMap model) { if (StringUtils.hasLength(pet.getName()) && pet.isNew() && owner.getPet(pet.getName(), true) != null){ result.rejectValue("name", "duplicate", "already exists"); } owner.getPets().add(pet); if (result.hasErrors()) { model.put("pet", pet); return VIEWS_PETS_CREATE_OR_UPDATE_FORM; } else { pet.setOwner(owner) // This line here was missing! petService.save(pet); return "redirect:/owners/" + owner.getId(); } }
You also say owner.getPets().add(pet)
. Still you do not persist this. You do not save this information in the DB, you just do it internally for your code.
You should try debugging and setting breakpoints on your code, so that you check what you save and what you actually receive.
And try delegating all your business logic to the services. So that Controllers are used only for handling what happens on a specific endpoint call.
Good luck.