1

I am creating simple REST API. I want to create an object via post method. Object contains other object. When I want to post it, I am receiving EntityNotFoundException which is thrown when nested object does not exist.

Code of object that I want to post:

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book {
    private String title;

    @ManyToOne
    private Author author;

    @Id
    @Column(unique = true)
    private String isbn;
}

Service of this object:

@Service
@RequiredArgsConstructor
public class BookServiceImpl implements BookService {
    private final BookRepository bookRepository;
    private final AuthorRepository authorRepository;

    @Override
    public Book save(Book book) {

        try {
            Author byId = authorRepository.getById(book.getAuthor().getId());
        } catch (EntityNotFoundException e) {
            authorRepository.save(book.getAuthor());
        }

        return bookRepository.save(book);
    }
}

After using post method I get this error:

javax.persistence.EntityNotFoundException: Unable to find com.jakubkolacz.qualificationTask.domain.dao.Author with id 0
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$JpaEntityNotFoundDelegate.handleEntityNotFound(EntityManagerFactoryBuilderImpl.java:163) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
    at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:216) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]

I thought that using try catch to check if object exist, and saving author if necessary will help but it did not. My question is where should I add some code to solve the problem. I understand why it is happening but do not know how to resolve it. The necessary thing is that I can not create service to add authors manually, they have to be added to repo only during adding new book.

pcsutar
  • 1,715
  • 2
  • 9
  • 14
  • You need to use cascade insert. It is for example explained here: [Hibernate: How to insert OnetoMany children by cascade](https://stackoverflow.com/questions/41794985/hibernate-how-to-insert-onetomany-children-by-cascade) – Piotr Filochowski Jul 22 '21 at 13:53
  • additionally to @Piotr's hint: try/catch might need more transaction handling as you would wish to do. – juwil Jul 22 '21 at 13:58
  • so i just need to add this to my code: @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST) ? – Jakub Kołacz Jul 22 '21 at 14:12

2 Answers2

0

The problem is that the save operation is not being cascaded down to the author object. You should add a cascade type inside ManyToOne annotation:

@ManyToOne(cascade = CascadeType.ALL)
Vivek Sadh
  • 4,230
  • 3
  • 32
  • 49
0

Exception handling in Spring

  • If you are specifically wondering how to handle exceptions in Spring, then I would highly recommend THIS tutorial.

Entity Creation

  • First I would like to point out two minor problems with your entity creation.

1)@ManyToOne : while it is not necessary, I always like to annotate a many-to-one relationship with the @JoinColumn annotation. It just acts as a simple and friendly visual reminder that (assuming your relationship is bidirectional) this side is the owner of the relationship(has the foreign key)

2)@Id : as it currently stands, the JPA provider(lets assume hibernate) assumes that you the developer are taking care of assigning a unique identifier to the id field. Granted, this is sometimes neccessary when dealing with old legacy databases. However, if you are not dealing with a legacy database, I would recommend that you delete @Column(unique = true) and the String value to replace them with:

@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long isbn;
  • @GeneratedValue will allow the system to automatically generate a value for isnb field.
  • strategy = GenerationType.IDENTITY tells the underlying database to handle the uniqueness and the auto incrementation in a way specific to the relational database.
  • Long instead of String because it can hold a larger number of bytes.

Service implementation

  • I have a few things to say about the BookServiceImpl class but first, good job on implementing an interface with BookService most people forget to program to an interface.

1) Single Responsibility

  • You are using both BookRepository and AuthorRepository which is of course fine(If it works it works). However, moving forward you should be weary not to add too many dependencies to one class. If you do so, you are breaking the Single Responsibility principle, which makes code harder to test and refactor.

2) Try and catch

  • The code inside your try catch blocks is a little confusing, especially since you have not shown the Author entity class. However, I am assuming you logic goes like this: if the author does not exist, save the author. Lastly save and return the book. Now you are correct in thinking that you handle the exceptions in the catch block. However, there is quite a lot to question here and only so little code to go on.

My overall recommendations

1) Break this method up : This method is trying to do three things at once. Create one method for saving the book, one for looking for the author and one for saving the author. This will allow for greater code reuse moving forward.

2) Read up on CascadeType : Specifically PERSIST, that might help you with your issues. Also, look into a many-to-many relationship as it is not uncommon for multiple books to have multiple authors.

Tristan Elliott
  • 594
  • 10
  • 8