1

I'm creating renting a book method in my book rental. It looks like this:

@PostMapping("/book")
public void purchaseBook(@RequestParam("userID") int userID, @RequestParam("bookID") int bookID) {
    bookRentalService.rentBook(userID, bookID);
}

After inputting userID and bookID, book if is available, it is added to BookRentalDB.

Relations between tables I made like this: enter image description here

Entity of rentals:

package bookrental.model.book;

import bookrental.model.account.User;
import lombok.*;

import javax.persistence.*;

@Entity
@Getter
@Setter
@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor
public class BookRentals {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    @OneToOne
    private Book book;
    @OneToOne
    private User user;
}

I've got problem with creating object of this entity(BookRentals) in Service. It needs to contain object of Book and object of User. I've created constructors for this object with only bookID for Book and userID for User. In prepareBookToRent Im asked to put ID of BookRentals class, too. In that case I can not to create object of it. Should it not be generated automatically? What should I do to make it work.

package bookrental.service.book;

import bookrental.model.account.User;
import bookrental.model.book.Book;
import bookrental.model.book.BookRentals;
import bookrental.repository.book.BookRepository;
import bookrental.repository.book.BookRentalsRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class BookRentalService {

    private final BookRepository bookRepository;
    private final BookRentalsRepository bookRentalsRepository;

    @Autowired
    public BookRentalService(BookRepository bookRepository, BookRentalsRepository bookRentalsRepository) {
        this.bookRepository = bookRepository;
        this.bookRentalsRepository = bookRentalsRepository;
    }

    public void rentBook(int userID, int bookID) {
        if (bookRepository.doesBookExistsWithGivenID(bookID)) {
            Book bookToRent = bookRepository.findOne(bookID);
            if (bookToRent.isAvailable()) {
                updateBookAvailabilityAndSaveToDb(bookToRent);
                BookRentals preparedBookToRent = prepareBookToRent(userID, bookID);
                bookRentalsRepository.save(preparedBookToRent);
            } else {
                throw new IllegalArgumentException("Book is no available");
            }
        }
        throw new IllegalArgumentException("Book does not exist!");
    }


    private BookRentals prepareBookToRent(int userID, int bookID) {
        return new BookRentals(new Book(bookID),new User(userID)); // here im asked to input ID, too
    }

    private void updateBookAvailabilityAndSaveToDb(Book bookToRent) {
        bookToRent.setAvailable(false);
        bookRepository.save(bookToRent);
    }
}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.book.rental.piotrek</groupId>
    <artifactId>BookRental</artifactId>
    <version>1.0-SNAPSHOT</version>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
    </parent>



    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.22</version>
        </dependency>
    </dependencies>

</project>

Staktrace

C:\Users\Admin\IdeaProjects\BookRental\src\main\java\bookrental\service\book\BookRentalService.java
Error:(39, 16) java: no suitable constructor found for BookRentals(bookrental.model.book.Book,bookrental.model.account.User)
    constructor bookrental.model.book.BookRentals.BookRentals(int,bookrental.model.book.Book,bookrental.model.account.User) is not applicable
      (actual and formal argument lists differ in length)
    constructor bookrental.model.book.BookRentals.BookRentals() is not applicable
      (actual and formal argument lists differ in length)

As I said above, this is it:

enter image description here

pipilam
  • 587
  • 3
  • 9
  • 22
  • Please post the error stacktrace. Also add info about the database type and version, Spring boot version, Java version, JPA provider. –  Nov 20 '18 at 09:29
  • @EugenCovaci updated pom.xml and stratrace. – pipilam Nov 20 '18 at 09:36
  • Your issue has nothing to do with JPA auto generated id, but with improper use of Lombok. –  Nov 20 '18 at 09:38
  • @EugenCovaci should I exclude then ID from costructor as Sarief in asnswer said? – pipilam Nov 20 '18 at 09:39
  • @EugenCovaci see updated answer then. Didn't notice OneToOne annotation on the mapping class. It didn't make sense, so my brain ignored it – Andrii Plotnikov Nov 20 '18 at 09:53

1 Answers1

1

You're wrong, you created all args contstructor. You have 3 attributes. This means, you have all args constructor with 3 arguments not 2.

Just create custom constructor.

public BookRentals (Book book, User user) {
// logic
}

ps: why not possible to exclude some parameter from all args: Java Lombok: Omitting one field in @AllArgsConstructor?

upd: comments are stating something crazy. So, if you got one to one, use annotation on entity directly. If you got many to many, consider OneToMany, as having multiple users renting same book is not realistic.

refer to this for table design: How to implement one-to-one, one-to-many and many-to-many relationships while designing tables?

Andrii Plotnikov
  • 3,022
  • 4
  • 17
  • 37
  • And ID will be genereated automatically? – pipilam Nov 20 '18 at 09:35
  • Id, if annotated properly, will be generated once you save the entity – Andrii Plotnikov Nov 20 '18 at 09:37
  • 1
    I've got it properly annotated, right? :D Why should I consider @OneToMany - one book can be only once rented by only one user. – pipilam Nov 20 '18 at 09:40
  • @EugenCovaci yeah, my bad, I was thinkin this is many to many, but having many users rent same book did not make sense – Andrii Plotnikov Nov 20 '18 at 09:49
  • @pipilam no you're not. You're supposed to use the annotation directly on the User entity. – Andrii Plotnikov Nov 20 '18 at 09:50
  • @EugenCovaci now oyu better explain what you mean. Why did you say that it was one to one if it's many to many? As a bonus, OneToMany makes more sense, since the book cannot be rented by multiple users at the same time – Andrii Plotnikov Nov 20 '18 at 10:36
  • I got minus voted on correct answer. Great community – Andrii Plotnikov Nov 20 '18 at 10:38
  • @Sarief why should I make `OneToOne` in `User` class instead of BookRentals? It doesn't make any sense for me, because I need bookID and userID in BookRentals. – pipilam Nov 20 '18 at 11:54
  • @pipilam Your design is wierd. if you need one-to-one you can have it as attribute in table of user or book. Even if not, you should still have it in one entity and supply mapped by, because that's how entities are designed in code. You will have the ids in db, but in code you should not care (or sometimes even know) about Ids. Why do you need a separate table for mapping of one-to-one? – Andrii Plotnikov Nov 20 '18 at 11:59
  • https://stackoverflow.com/questions/7296846/how-to-implement-one-to-one-one-to-many-and-many-to-many-relationships-while-de – Andrii Plotnikov Nov 20 '18 at 12:03
  • I made another entity as to join these 2 tables (user and book) into one. I thought it is more cleaner. Do not understand your https://stackoverflow.com/questions/53389763/why-do-i-have-to-input-id-pk-problem-with-auto-generating-id/53390008?noredirect=1#comment93660304_53390008 comment. I will try to read about it in link you put. – pipilam Nov 20 '18 at 12:05
  • @pipilam it's not cleaner from java perspective: it's harder to read and maintain. Direct links are easier to read. Table is different thing and can be as you design, with proper mapping from the OneToOne anotations mappedby attribute – Andrii Plotnikov Nov 20 '18 at 12:09
  • 1
    @pipilam as for why... this is convention. This conventions are used not only by you but by frameworks you use. Doing desing in accepted conventions helps you to easily integrate with those frameworks. QueryDsl and Spring Data can generate meta entities based on those designs, other developers immidiately know how things work, etc. – Andrii Plotnikov Nov 20 '18 at 12:14
  • I understand. To sum up. You want me to delete BookRentals and make annotation `OneToMany` that refers to Book? – pipilam Nov 20 '18 at 12:19
  • @pipilam I want you to show you how to design entities in java, regardless of how DB is done: https://www.logicbig.com/tutorials/java-ee-tutorial/jpa/one-to-one-join-table.html but if you only need one-to-one making attribute in one table is much better. Just learn those and decide what's best. I.e. Consider. Knowing all options – Andrii Plotnikov Nov 20 '18 at 12:21
  • I didnt say that I need one-to-one relation. :D I needed it to make BookRentals Entity. – pipilam Nov 20 '18 at 12:36
  • @pipilam ok, if you have something else which is not described in the question - it's your call. I hope to answer also those who don't have same context you have – Andrii Plotnikov Nov 20 '18 at 12:38
  • 1
    well, I tried to understand concept of what is in the link. I think that you didn't understand how I'm trying to make it. I illustrated it on the picture. Im creating 3 entities - one for books, one for users and one for rentals. I need 3 tables to it. I need OneToOne relation. Unfortunately I do not understand what you mean. I think I will leave like I have written. – pipilam Nov 20 '18 at 14:34
  • @pipilam ok, it's your choice. Revisit this in a year or two, maybe you will understand what I mean and why, then you can answer NO or YES with clear resolution :) – Andrii Plotnikov Nov 20 '18 at 14:36