Skip to content

cannot save to database foreign table new record generate hibernate jpa

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;
}

The problem: The problem

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.