更新ManyToMany关系以包括新字段

时间:2019-03-10 19:30:08

标签: java spring hibernate spring-boot spring-data-jpa

因此,我在spring应用程序中建立了一个现有的ManyToMany关系。看来我使用@ManyToMany批注进行了设置,这是一个错误,因为现在我需要向联接表中添加一个字段,而这似乎并不容易。

我的结构是发货和产品。货件表存储有关货件发送给谁,货件发送日期等信息。产品表存储有关货品,制造者,描述,尺寸等信息。

在构建此产品时,我没有考虑的是,创建货件时我需要跟踪已发货的产品数量,这应该在联接表上完成。

我一直在处理以下示例:https://vladmihalcea.com/the-best-way-to-map-a-many-to-many-association-with-extra-columns-when-using-jpa-and-hibernate/

更新: 我一直在研究上面的示例,并遇到了产品和发货表之间无限递归调用的问题。我的结构如下:

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 vs LAZY和JsonIgnore和JsonIgnoreProperties的各种组合。关于如何解决这个问题有什么想法吗?我最好的猜测是与Lombok有一些互动,但我无法弄清楚。

2 个答案:

答案 0 :(得分:1)

您可以保留@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;

答案 1 :(得分:1)

看起来我终于想通了...我删除了以下方法:

getAllProducts()
getAllShipments()

,并将它们替换为:

allProducts()
allShipments()

将它们作为吸气剂总是将它们添加到我的返回对象中,而不会被@JsonIgnore或其他任何东西切断。

接下来,我更新了ShipmentProduct.java,并向货运和产品添加了 @JsonIgnore ,同时删除了 @JsonIgnore 和/或 @JsonIgnoreProperties

然后,为了在使用allProducts()或allShipments()时不会出现错误,我将其添加到了application.properties文件中: spring.jackson.serialization.fail-on-empty-beans = false

一旦全部完成,我也可以保留龙目岛。

希望这可以帮助处于类似情况的其他人。另外,如果有人还有其他建设性的批评,请让我知道!