2

I'm trying to build a REST API with Spring boot application for web and mobile app. In my application I have multiple foreign keys, and I want to pass only the id of the object instead of the entire object. See example below.

Model Book

// imports

@Entity
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String title;
    private String isbn;

    @ManyToOne
    @JoinColumn(name = "category_id", updatable = false)
    private BookCategory bookCategory;

    public Book() {
    }

    public Book(Integer id, String title, String isbn, BookCategory bookCategory) {
        this.id = id;
        this.title = title;
        this.isbn = isbn;
        this.bookCategory = bookCategory;
    }

    // getters and setters
}

Model Category

// imports

@Entity
public class BookCategory {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String name;

    @OneToMany(mappedBy = "bookCategory", cascade = CascadeType.ALL)
    private List<Book> books;

    public BookCategory() {
    }

    public BookCategory(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    // getters and setters
}

Book Controller

// imports

@RestController
@RequestMapping(path = "/api/v1")
public class BookController {

    @Autowired
    private BookRepository bookRepository;

    // other methods for index, show, put and delete

    @PostMapping(path = "/books")
    public book createBook(@RequestBody Book book) {
        return bookRepository.save(book);
    }

}

Book Repository

// imports

public interface BookRepository extends PagingAndSortingRepository<Book, Integer> {
}

Book categories are pre-loaded on database, and I don't want to allow the client to change them or to add new ones.

Here's the JSON POST I tried and the object that was stored on database.

Request

{
    "title": "Tirant lo blanc",
    "isbn": "9788475841199",
    "category_id": 4
}

Response

{
    "title": "Tirant lo blanc",
    "isbn": "9788475841199",
    "bookCategory": null
}

I tried to send it as object too, but the result is null.

{
    "title": "Tirant lo blanc",
    "isbn": "9788475841199",
    "category_id": {
        "id": 4
    }
}

How can I send the category id to correctly store the book on database? Is better to send the entire category object instead? I tried too, but the response and the stored book on database is always on category null.

Sergi
  • 35
  • 1
  • 6
  • Book doesn't have a property `category_id`. It does however have a property `bookCategory` – Alan Hay Mar 02 '19 at 11:27
  • That's true, `bookCategory` is the object, but on database Spring stores a column named `category_id` with the id of the category belonging some book. – Sergi Mar 03 '19 at 12:13
  • Yes, I can obviously see that. **JSON Binding is hoever on Java property name.** – Alan Hay Mar 03 '19 at 14:52

2 Answers2

1

If you only want to pass the id of the object set the getters as you want it to return.

For example:

// imports

@Entity
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String title;
    private String isbn;

    @ManyToOne
    @JoinColumn(name = "category_id", updatable = false)
    private BookCategory bookCategory;

    public Book() {
    }

    public Book(Integer id, String title, String isbn, BookCategory bookCategory) {
        this.id = id;
        this.title = title;
        this.isbn = isbn;
        this.bookCategory = bookCategory;
    }

   // getters
   public Integer getBookCategory() {
        return bookCategory.getId();
   }
}
Karenloh
  • 31
  • 5
0

There is a very simple solution to your problem. In a good REST API design, you don't let users of API manipulate with database entities. Instead, you introduce DTOs that will be returned in responses and/or received as a body of the message. In this case, you have all the control - you don't expose too much of your data structure. In order to make this happen, you should at first create DTOs and then write the mapping between DTO and database entity - this can help.

Ilia Ilin
  • 152
  • 1
  • 5
  • 1
    Sorry this is not an answer but a matter of opinion. https://stackoverflow.com/questions/1440952/why-are-data-transfer-objects-dtos-an-anti-pattern/1440994#1440994 – Alan Hay Mar 03 '19 at 14:53
  • As far as I know it is considered the best practice, https://stackoverflow.com/questions/36174516/rest-api-dtos-or-not – Ilia Ilin Mar 03 '19 at 15:30
  • @AlanHay It works for my project so for me is an accepted answer. Furthermore, as Ilia Ilin says, is considered the best practice. – Sergi Mar 03 '19 at 17:53
  • So you are going to do all this this rather than just name the property correctly as I detailed above. Good luck! – Alan Hay Mar 03 '19 at 18:16