Shoju
Shoju

Reputation: 45

Insert into daughter table with parent id with JPA in Spring Boot

I have a problem that after trying many formulas and ... I ran out of ideas and would like to see if anyone can help me in this.

I am trying to insert a parent and child table with JPA but not adding the id of the parent table to the daughter. Insert the record in the daughter too but the id leaves me null. These would be the two tables.

The Author table only has two columns and a 1 / N relationship with the Book daughter table indicating a HashSet: La tabla Autor, solo tiene dos columnas y una relación 1/N con la tabla hija Libro indicando un HashSet:

@Entity
@Table(name="author")
public class Author {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="id", nullable = false, insertable=false)
    private int id;
    
    @Column(name="name", columnDefinition = "varchar(127)")
    private String name;

    @Column(name="description", columnDefinition = "varchar(255)")
    private String description;

    @OneToMany(mappedBy = "author", cascade=CascadeType.ALL, orphanRemoval = true)
    private Set<Book> book = new HashSet<Book>();

    /* Getters y Setters y Constructor */

}

The Book table, I tell you to add a column that will be the foreign key of Author and that will be called autor_id. It is this field that is not automatically filled with the parent id:

@Entity
@Table(name="book")
public class Book {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="id", nullable = false, insertable=false)
    private int id;
    
    @Column(name="title", columnDefinition = "varchar(50)")
    private String title;

    @Column(name="description", columnDefinition = "varchar(255)")
    private String description;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(foreignKey = @ForeignKey(name = "fk_book_author_id"), name="author_id", referencedColumnName = "id", columnDefinition = "int")
    private Author author;

    /* Getters y Setters y Constructor */

}

The Controller, I simply have a GetMapping to load the list per screen and the Post where I create the author and the book and add the latter to the author to later make a save in the Repository call:

@Controller
@RequestMapping("/authors")
public class Authors {

    @Autowired
    private AuthorRepository authorRepository;

    @GetMapping
    public String findAll(Model model) {

        extracted(model);

        return "authors";

    }

    @PostMapping
    public String save(Model model) {

        Author author = new Author();
        author.setName("Name");
        author.setDescription("Description Author");

        Book book = new Book();
        book.setTitle("Title");
        book.setDescription("Description Book");

        Set<Book> hsBook = new HashSet<Book>();
        hsBook.add(book);

        author.setBook(hsBook);

        authorRepository.save(author);

        extracted(model);

        return "authors";

    }

    private void extracted(Model model) {

        model.addAttribute("authors", authorRepository.findAll());

    }

}

The little Repository to add here except for the extends JpaRepository:

@Repository
public interface AuthorRepository extends JpaRepository<Author, Integer> {

}

This saves me in the Author table:

id = 1, name = "Name", description = "Description Author"

And in the Book table:

id = 1, author_id = null, title = "Title", description = "Description Book"

I have tried in several ways but I cannot transfer the id of the Author table to the foreign key.

What am I doing wrong?

Thank you very much for your time.

Upvotes: 1

Views: 3972

Answers (2)

srp
srp

Reputation: 753

You forgot to assign reference of author to book as mentioned by @Eklavya.

It is best practise to introduce add and remove utility methods to avoid this state propagation issue.

@Entity
@Table(name = "author")
public class Author {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false, insertable = false)
    private int id;

    @Column(name = "name", columnDefinition = "varchar(127)")
    private String name;

    @Column(name = "description", columnDefinition = "varchar(255)")
    private String description;

    @OneToMany(mappedBy = "author", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<Object> books = new HashSet<Book>();

    /* Getters y Setters y Constructor */

    public void addBook(Book book) {
        books.add(book);
        book.setAuthor(this);
    }

    public void removeBook(Book book) {
        books.add(book);
        book.setAuthor(null);
    }

}

Then your save method becomes

@PostMapping
    public String save(Model model) {

        Author author = new Author();
        author.setName("Name");
        author.setDescription("Description Author");

        Book book = new Book();
        book.setTitle("Title");
        book.setDescription("Description Book");

        author.addBook(book);

        authorRepository.save(author);

        extracted(model);

        return "authors";

    }

I hope this is helpful.

Upvotes: 0

Eklavya
Eklavya

Reputation: 18420

You need to sync both parent and child for bidirectional relationships.

Set author in book object also before save the author

book.setAuthor(author);

Upvotes: 2

Related Questions