web-dev-qa-db-fra.com

Comment joindre les résultats de plusieurs tables dans le référentiel Spring JPA

Je suis nouveau dans Spring et je n'arrive pas à comprendre comment joindre plusieurs tables pour renvoyer un résultat. J'ai essayé d'implémenter une petite application de bibliothèque comme indiqué ci-dessous.

Mes classes d'entités - Livre, client, réservations

Book.Java - livres disponibles dans la bibliothèque

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

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

    @NotNull(message = "Book name cannot be null")
    @Column(name = "book_name", columnDefinition = "VARCHAR(255)")
    private String bookName;

    @Column(name = "author", columnDefinition = "VARCHAR(255)")
    private String author;

    // getters and setters

    public Book() {}

    public Book(String bookName, String author) {
        this.bookName = bookName;
        this.author = author;
    }
}

Customer.Java - Clients enregistrés dans la bibliothèque

@Entity
@Table(name = "customer", uniqueConstraints = {@UniqueConstraint(columnNames = {"phone"})})
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", columnDefinition = "int")
    private int id;

    @NotNull(message = "Customer name cannot be null")
    @Column(name = "name", columnDefinition = "VARCHAR(255)")
    private String name;

    @Column(name = "phone", columnDefinition = "VARCHAR(15)")
    private String phone;

    @Column(name = "registered", columnDefinition = "DATETIME")
    private String registered;

    // getters and setters

    public Customer() {}

    public Customer(String name, String phone, String registered) {
        this.name = name;
        this.phone = phone;
        this.registered = registered;
    }
}

Booking.Java - Toutes les réservations effectuées par les clients

@Entity
@Table(name = "bookings")
public class Booking {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", columnDefinition = "int")
    private int id;

    @NotNull(message = "Book id cannot be null")
    @Column(name = "book_id", columnDefinition = "int")
    private int bookId;

    @NotNull(message = "Customer id cannot be null")
    @Column(name = "customer_id", columnDefinition = "int")
    private int customerId;

    @Column(name = "issue_date", columnDefinition = "DATETIME")
    private String issueDate;

    @Column(name = "return_date", columnDefinition = "DATETIME")
    private String returnDate;

    // getters and setters

    public Booking() {}

    public Booking(int bookId, int customerId, String issueDate) {
        this.bookId = bookId;
        this.customerId = customerId;
        this.issueDate = issueDate;
    }
}

Maintenant, les schémas de table pour les entités respectives sont les suivants:

 livres: 
 + ----------- + -------------- + ------ + --- - + --------- + ---------------- + 
 | Champ | Type | Null | Clé | Par défaut | Extra | 
 + ----------- + -------------- + ------ + ----- + - ------- + ---------------- + 
 | id | int (11) | NON | PRI | NULL | incrémentation automatique | 
 | Nom_livre | varchar (255) | NON | | NULL | | 
 | Auteur | varchar (255) | OUI | | NULL | | 
 + ----------- + -------------- + ------ + ----- + - -------- + ---------------- + 
 id - clé primaire 
 
 client: 
 + ------------ + -------------- + ------ + ----- + ------ ------------- + ------------------- + 
 | Champ | Type | Null | Clé | Par défaut | Extra | 
 + ------------ + -------------- + ------ + ----- + - ------------------ + ------------------- + 
 | id | int (11 ) | NO | PRI | NULL | auto_increment | 
 | Name | varchar (255) | NO | | NULL | | 
 | Registered | datetime | YES | | CURRENT_TIMESTAMP | DEFAULT_GENERATED | 
 | Phone | varchar (15) | YES | UNI | NULL | | 
 + ------------ + ------ -------- + ------ + ----- + ------------------- + ------------------- + 
 id - clé primaire 
 
 réservations: 
 + ------------- + ---------- + - ----- + ----- + ------------------- + ------------------ - + 
 | Champ | Type | Null | Clé | Par défaut | Extra | 
 + ------------- + ---------- + ------ + ----- + ---- --------------- + ------------------- + 
 | id | int (11) | NON | PRI | NULL | incrémentation automatique | 
 | Book_id | int (11) | NO | MUL | NULL | | 
 | Customer_id | int (11) | NO | MUL | NULL | | 
 | Issue_date | datetime | YES | | CURRENT_TIMESTAMP | DEFAULT_GENERATED | 
 | Return_date | datetime | YES | | NULL | | 
 + ------ ------- + ---------- + ------ + ----- + ------------------ - + ------------------- + 
 id - clé primaire 
 book_id - clé étrangère référence books.id 
 customer_id - la clé étrangère fait référence à customer.id 
 

Maintenant, ce que je veux faire, c'est donner des critiques de réservation comme le téléphone du client ou le nom de l'auteur, etc., je veux retourner toutes les réservations liées à cette commande. Je vais vous montrer un exemple d'API de réservation pour expliquer.

Contrôleur de réservation:

@RestController
@RequestMapping("/bookings")
public class BookingController {
    @Autowired
    BookingService bookingService;

    // some booking apis which return Booking objects

    @GetMapping
    public List<Booking> getAllBookingsBy(@RequestParam("phone") String phone,
                                         @RequestParam("authors") List<String> authors) {
        return bookingService.getAllBy(phone, authors);
    }

    @PostMapping
    public Booking addBooking(@RequestBody Booking booking) {
        bookingService.saveBooking(booking);
        return booking;
    }
}

Classe de service de réservation:

@Service
public class BookingService {
    @Autowired
    private BookingRepository bookingRepository;

    // some booking service methods

    // get all bookings booked by a customer with matching phone number and books written by a given list of authors
    public List<Booking> getAllBy(String phone, List<String> authors) {
    return bookingRepository.queryBy(phone, authors);
    }

    public void saveBooking(Booking booking) {
        bookingRepository.save(booking);
    }
}

Classe de référentiel de réservation:

@Repository
public interface BookingRepository extends JpaRepository<Booking, Integer> {
    // some booking repository methods

    @Query(value = "SELECT * FROM bookings bs WHERE " +
            "EXISTS (SELECT 1 FROM customer c WHERE bs.customer_id = c.id AND c.phone = :phone) " +
            "AND EXISTS (SELECT 1 FROM books b WHERE b.id = bs.book_id AND b.author IN :authors)",
            nativeQuery = true)
    List<Booking> queryBy(@Param("phone") String phone,
                            @Param("authors") List<String> authors);
}

Maintenant, frapper le contrôleur de réservation affiché retournera un objet de réservation qui ressemble à ceci:

[
    {
        "id": 3,
        "book_id": 5,
        "customer_id": 2,
        "issue_date": "2019-02-04 01:45:21",
        "return_date": null
    }
]

Mais je ne le veux pas comme ça, je veux retourner avec eux le nom du client pour cette réservation et aussi le nom du livre. Je veux donc que les objets de réservation retournés par le contrôleur ressemblent à ceci:

[
    {
        "id": 3,
        "book_id": 5,
        "customer_id": 2,
        "issue_date": "2019-02-04 01:45:21",
        "return_date": null,
        "customer_name": "Cust 2",
        "book_name": "Book_2_2",
    }
]

Quelqu'un peut-il m'aider à le faire? Je suis bloqué car je ne peux pas continuer à partir d'ici.

EDIT: J'ai ajouté ces associations unidirectionnelles individuelles dans ma classe de réservation:

@OneToOne
@JoinColumn(name = "book_id", insertable = false, updatable = false)
private Book book;

@OneToOne
@JoinColumn(name = "customer_id", insertable = false, updatable = false)
private Customer customer;

Mais maintenant, lorsque j'appuie sur mon contrôleur, j'obtiens tous les objets Livre et Client dans mon objet Réservation. Alors, que puis-je faire pour renvoyer simplement le nom du livre et le nom du client dans l'objet de réservation? Voici à quoi ressemble mon objet Booking retourné:

[
    {
        "id": 3,
        "book_id": 5,
        "book": {
            "id": 5,
            "book_name": "Book_2_2",
            "author": "author_2"
        },
        "customer_id": 2,
        "customer": {
            "id": 2,
            "name": "Cust 2",
            "phone": "98765431",
            "registered": "2019-02-04 01:13:16"
        },
        "issue_date": "2019-02-04 01:45:21",
        "return_date": null
    }
]

De plus, maintenant mon API save () dans mon contrôleur de réservation ne fonctionne pas parce que lorsque j'y envoie un objet de type Booking, bookId et customerId deviennent en quelque sorte 0, ce qui ne s'est pas produit avant d'ajouter ces modifications .

7
user3248186

La requête que vous avez n'est pas la meilleure façon de joindre des tables. Une manière plus intuitive est comme ça

SELECT * FROM bookings
WHERE customer_id in (SELECT id FROM customer WHERE phone = :phone)
 AND book_id in (SELECT id FROM books WHERE author IN :authors)
0
Sharon Ben Asher

Vous pouvez l'implémenter selon les étapes ci-dessous.

  1. Créez une nouvelle interface avec les getters pour tous les champs dont vous avez besoin en réponse.
  2. Dans votre chaîne de requête à l'intérieur de @Query, vous devrez fournir des noms à vos colonnes dans select. Remarque: Ces noms doivent être synchronisés avec les getters que vous créez dans votre interface.
  3. Utilisez cette interface comme type de retour de votre méthode de référentiel.

Pour plus d'informations, vous pouvez consulter Projections dans le repos de données du printemps. https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections

0
Dheeraj Kadam