Mapeando classe de associação, pt1

Bom, ela terá uma chave múltipla primária. Ou seja, dentro da nossa tabela de OrderItem no banco de dados, vai ter uma chave estrangeira para o ID do produto e para o ID da order. Como as duas chaves estrangeiras são primárias, temos um caso de CHAVE COMPOSTA (EMBEDDED KEY).

Chave Composta (Embedded Key)

Quando temos uma chave primária composta no banco relacional para mapearmos ela para o java (JPA), teremos que criar uma classe a parte para representar essa chave primária, veja:

//anotação para dizer que essa classe será incorporada por outras
@Embeddable
public class OrderItemPK {

    @ManyToOne
    @JoinColumn(name = "order_id")
    private Order order;

    @ManyToOne
    @JoinColumn(name = "product_id")
    private Product product;
}

Agora, na nossa classe OrderItem (que terá preço e quantidade), instanciaremos a classe auxiliar criada aima uma ID, veja:

public class OrderItem {
    
    //usando a classe como referência, instanciando-a
    @EmbeddedId
    private OrderItemPK id = new OrderItemPK();
}

❗Importante. Não esquecer de ao instanciar o construtor na classe OrderItem, trocar os parâmetros. Não faz sentido expormos no construtor a OrderItemPK.

Se outra classe precisa instanciar a OrderItem, ela não precisa (e nem deve), conhecer artícios internos da JPA. Portanto, o construtor terá: order e product, veja:

    public OrderItem(Order order, Product product, Integer quantity, Double price) {
        id.setOrder(order);
        id.setProduct(product);
        this.quantity = quantity;
        this.price = price;
    }

Dentro do construtor, nós chamados o id (que está na classe OrderItem, mas inicialmente é da OrderItemPK), settando seu Order e Product.

❗Importante. Dentro da classe Order e da classe Product podemos ter referência para os itens. Por exemplo: Dentro do Product, usar um "getItems" e acessar os OrderItems associados a ele? Sim! Veja:

Classe Product: Importamos uma coleção de OrdemItem. E devemos lembrar o seguinte: Na classe do OrdemItem, temos o id que é uma OrderItemPK. Dentro da OrderItemPK, temos a anotação ManyToOne. Dito isso, passaremos a anotação OneToMany na coleção de OrderItem dentro de Product.

    //O mapeamento é feito diferente. O Product que estamos nos referindo,
    // está dentro do OrderItemPK, que este, por sua vez, está dentro de OrderItem
    // como "id". Usamod o id e o "." para entrar na classe, selecionando o product
    @OneToMany(mappedBy = "id.product")
    private Set<OrderItem> items = new HashSet<>();

Classe Order: Faremos a mesma coisa acima.

    @OneToMany(mappedBy = "id.order")
    private Set<OrderItem> items = new HashSet<>();

Ao rodar o código:

Tabela auxiliar OrderItem agora possui referência para o category_id e product_id.

Atualizado