Обновление отношения ManyToMany для включения нового поля - PullRequest
0 голосов
/ 10 марта 2019

Итак, в моем весеннем приложении настроены существующие отношения ManyToMany. Похоже, что я допустил ошибку, настроив это с помощью аннотации @ManyToMany, потому что теперь мне нужно добавить поле в таблицу соединений, и это не кажется легким делом.

Моя структура - поставки и продукты. В таблице отгрузки хранится информация о том, кому была отправлена ​​партия, в какую дату она была отправлена ​​и т. Д. В таблице продуктов хранится информация о продукте, кто его производит, описание, размер и т. Д.

То, что я не учел при создании, было то, что мне нужно будет отслеживать количество отгруженного продукта при создании отгрузки, что должно быть сделано на соединительном столе.

Я работал с этим примером: https://vladmihalcea.com/the-best-way-to-map-a-many-to-many-association-with-extra-columns-when-using-jpa-and-hibernate/

UPDATE: Я работал над примером выше и столкнулся с проблемой бесконечных рекурсивных вызовов между таблицами продуктов и отгрузки. Моя структура выглядит следующим образом:

ShipmentProductID.java:

// Package and Imports here

@Embeddable
public class ShipmentProductId
    implements Serializable {

@Column(name = "product_id")
private Long productId;

@Column(name = "shipment_id")
private Long shipmentId;

private ShipmentProductId() {}

public ShipmentProductId(
        Long productId,
        Long shipmentId) {
    this.productId = productId;
    this.shipmentId = shipmentId;
}

// Getters and Setters here

@Override
public boolean equals(Object o) {
    if (this == o) return true;

    if (o == null || getClass() != o.getClass())
        return false;

    ShipmentProductId that = (ShipmentProductId) o;
    return Objects.equals(productId, that.productId) &&
            Objects.equals(shipmentId, that.shipmentId);
}

@Override
public int hashCode() {
    return Objects.hash(productId, shipmentId);
}
}

ShipmentProduct.java:

// Package and Imports here

@Entity(name = "ShipmentProduct")
@Table(name = "shipment_product")
public class ShipmentProduct {

@EmbeddedId
private ShipmentProductId id;

@ManyToOne(fetch = FetchType.LAZY)
@MapsId("productId")
private Product product;

@ManyToOne(fetch = FetchType.LAZY)
@MapsId("shipmentId")
private Shipment shipment;

@Column(name = "created_on")
private Date createdOn = new Date();

private ShipmentProduct() {}

public ShipmentProduct(Product product, Shipment shipment) {
    this.product = product;
    this.shipment = shipment;
    this.id = new ShipmentProductId(product.getId(), 
    shipment.getId());
}

// Getters and Setters here

@Override
public boolean equals(Object o) {
    if (this == o) return true;

    if (o == null || getClass() != o.getClass())
        return false;

    ShipmentProduct that = (ShipmentProduct) o;
    return Objects.equals(product, that.product) &&
            Objects.equals(shipment, that.shipment);
}

@Override
public int hashCode() {
    return Objects.hash(product, shipment);
}
}

Product.java:

// Package and Imports here

@Entity
@Data
@Cache( usage = CacheConcurrencyStrategy.READ_WRITE )
public class Product extends AbstractEntity {

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

@JsonIgnoreProperties("products")
// Have tried @JsonIgnore as well
@OneToMany(
        mappedBy = "product",
        orphanRemoval = true
)
private List<ShipmentProduct> shipments = new ArrayList<>();

@NotNull
private Integer quantity;

public boolean isAssociated(Client client){
    if( this.client == null || this.client.getId() == null ||
            client == null || client.getId() == null ) return 
false;
    return this.client.getId() == client.getId();
}

public boolean isAssociated(Expression expression){
    if( this.expression == null || this.expression.getId() == null 
||
            expression == null || expression.getId() == null ) 
return false;
    return this.expression.getId() == expression.getId();
}

public void addShipment(Shipment shipment) {
    ShipmentProduct shipmentProduct = new ShipmentProduct(this, 
shipment);
    shipments.add(shipmentProduct);
    shipment.getProducts().add(shipmentProduct);
}

public Set<Shipment> getAllShipments(){
    Set<Shipment> shipmentList = new HashSet<>();
    for (ShipmentProduct shipmentProduct : shipments) {
        shipmentList.add(shipmentProduct.getShipment());
    }
    return shipmentList;
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Product product = (Product) o;
    return Objects.equals(id, product.id);
}

@Override
public String toString(){
    return "";
}

@Override
public int hashCode() {
    return Objects.hash(id);
}
}

Shipment.java:

// Package and Imports here

@Entity
@Data
@ToString(exclude = {"products", "contacts"})
@EqualsAndHashCode(exclude = {"products", "contacts"})
public class Shipment extends AbstractEntity {

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

@JsonIgnoreProperties("shipments")
@OneToMany(
        mappedBy = "shipment",
        orphanRemoval = true
)
private List<ShipmentProduct> products = new ArrayList<>();

public Set<Product> getAllProducts(){
    Set<Product> productList = new HashSet<>();
    for (ShipmentProduct shipmentProduct : products) {
        productList.add(shipmentProduct.getProduct());
    }
    return productList;
}

public void addProduct(Product product) {
    ShipmentProduct shipmentProduct = new ShipmentProduct(product, 
this);
    products.add(shipmentProduct);
    product.getShipments().add(shipmentProduct);
}

public void removeProduct(Product product) {
    for (Iterator<ShipmentProduct> iterator = products.iterator();
         iterator.hasNext(); ) {
        ShipmentProduct shipmentProduct = iterator.next();

        if (shipmentProduct.getShipment().equals(this) &&
                shipmentProduct.getProduct().equals(product)) {
            iterator.remove();

shipmentProduct.getProduct().getShipments().remove(shipmentProduct);
            shipmentProduct.setShipment(null);
            shipmentProduct.setProduct(null);
        }
    }
}

public Optional<Product> getProductById(Long productId){
    Optional<ShipmentProduct> shipmentProduct = 
products.stream().filter(product -> 
product.getId().equals(productId)).findFirst();
    return productId == null ? Optional.empty() :
            Optional.of(shipmentProduct.get().getProduct());
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Shipment shipment = (Shipment) o;
    return Objects.equals(id, shipment.id);
}

@Override
public String toString(){
    return "";
}

@Override
public int hashCode() {
    return Objects.hash(id);
}
}

Кажется, что я подхожу близко, так как это похоже на создание бесконечно большого объекта JSON. Я пробовал все виды комбинаций EAGER против LAZY и JsonIgnore и JsonIgnoreProperties. Есть мысли о том, как решить эту проблему? Мое лучшее предположение - какое-то взаимодействие с Ломбоком, но я не смог понять это.

Ответы [ 2 ]

1 голос
/ 14 марта 2019

Похоже, я наконец понял это ... Я удалил следующие методы:

getAllProducts()
getAllShipments()

и заменил их на:

allProducts()
allShipments()

Наличие их в качестве получателей всегда добавляло их в мой возвращаемый объект, и они не были обрезаны ни @JsonIgnore, ни чем-либо еще.

Затем я обновил ShipmentProduct.java и добавил @ JsonIgnore к отгрузке и продукту, удаляя при этом @ JsonIgnore и / или @ JsonIgnoreProperties из Shipment.java и Product.java.

Затем, чтобы не получать ошибок при использовании allProducts () или allShipments (), я добавил это в свой файл application.properties: spring.jackson.serialization.fail-on-empty-beans = ложь

Как только все это было завершено, я также смог сохранить ломбок.

Надеюсь, это поможет кому-то еще в подобной ситуации. Также, если у кого-то есть дополнительная конструктивная критика, пожалуйста, дайте мне знать!

1 голос
/ 10 марта 2019

Вы можете оставить аннотацию @ManyToMany, просто добавьте соединительную таблицу в свою базу данных и отобразите ее на карту:

@JoinTable(
        name = "joining_table",
        joinColumns = @JoinColumn(
                name = "this_id_in_jt",
                referencedColumnName = "this_id"
        ),
        inverseJoinColumns = @JoinColumn(
                name = "other_id_in_jt",
                referencedColumnName = "other_id"
        )
)
@ManyToMany
private List<Other> others;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...