Lance Toth
Lance Toth

Reputation: 440

Hibernate one-to-many bidirectional connection gives new parent when saving from child

I'm trying to add children to a pre-existing parent via freemarker. But every time I try, I get the following error:

org.postgresql.util.PSQLException: ERROR: insert or update on table "CHILD TABLE" violates foreign key constraint "KEY" Detail: Key (id)=(WRONG NUMBER) is not present in table "PARENT TABLE".

I seems to give a new parent id sequentially every time, instead of the parent id that is given and present in the child object before childRepository.save(child); is called.

Code snippets:

Parent:

@Entity
@Table(name = "parent")
@EntityListeners(AuditingEntityListener.class)
public class Parent {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;

@OneToMany(mappedBy = "parent")
private Set<Child> children = new HashSet<Child>();

Child:

@Entity
@Table(name = "child")
@EntityListeners(AuditingEntityListener.class)
public class Child{

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;

@ManyToOne
@JoinColumn(name = "parent_id", nullable = false)
private Parent parent;

Freemarker Controller:

 @Autowired
    private ChildRepository childRepository;
    
    @RequestMapping("/addchild/{id}")
    public String addChild(Model model, @PathVariable(value = "id") Long parentId) {
        String method = "addChild";
        Utils.log(method, "started");
        
        parent parent= parentRepository.findById(parentId)
                .orElseThrow(() -> new IllegalArgumentException("parent " + parentId + " not found"));
        Utils.log(method, "loaded " + parent);
        child child = new child();
        child.setparent(parent);
        
        model.addAttribute("child", child);
        return "child";
    }
    
    @PostMapping("/savechild")
    public String savechild(Model model, child child) {
        String method = "savechild";
        Utils.log(method, "started");
        
        child.getparent().addchild(child);
        
        child = childRepository.save(child);
        
        Utils.log(method, "saved " + child);
        
        return "redirect:/parent/" + child.getparent().getId();
    }

Upvotes: 0

Views: 346

Answers (2)

Lance Toth
Lance Toth

Reputation: 440

The error was a mistaken constraint, presumably an artifact from an earlier version of the code

Upvotes: 0

Marius Schmidt
Marius Schmidt

Reputation: 663

I have not used Freemarker, but the PostMapping code looks like a Spring MVC Controller to me, that contains the wrong code based on a misconception. See, the PostMapping gets some input from e.g. a HTML form, and constructs a child instance, with all fields set according to the form input.

But as HTTP is a stateless protocol, the controller method knows absolutely nothing more, than you feed in, and your parent information from the previous GET call is completely lost. So for adding children on POST, you also first have to fetch the parent.

I would have expected to see something like that

@Autowired private ParentRepository parentRepository;
@Autowired private ChildrenRepository childrenRepository;

@RequestMapping("/parents/{parentId}/children/{childId}")
public String getChild(Model model, @PathVariable(value = "parentId") Long parentId, @PathVariable(value = "childId") Long childId) {

    // Fetch a specific child for a specific parent fully determined by URL
    Child child = childrenRepository.findByParentIdAndId(parentId, childId).orElseThrow(() -> new IllegalArgumentException("parent " + parentId + "child " + childId + " not found"));
   
    model.addAttribute("child", child);
    return "child";
}

// The parent to add to is referenced by its url, the child constructed by form data
@PostMapping("/parents/{id}")
public String saveChild(Model model, @PathVariable(value = "id") Long parentId, child child) {
    // Fetch the parent you want to add a child to
    parent parent = parentRepository.findById(parentId)
            .orElseThrow(() -> new IllegalArgumentException("parent " + parentId + " not found"));
    // Add the child constructed from form data and send to parent specific url
    parent.addchild(child);
    // Save parent with updated list of children
    parentRepository.save(parent)
    //
    return "redirect:/parents/" + parent.getId();
}

Upvotes: 1

Related Questions