Lok
Lok

Reputation: 131

Relationship table data overwritten in many-to-many hibernate

I have two entity classes Order and Product with many-to-many relationship (several orders may have the same product as far as stock is not empty and an order may contain multiple products)as follows:

@Entity
@Table(name = "Order_")
public class Order implements Serializable {

    .......
@ManyToMany(mappedBy="orders")
private List<Product> products;  
    .......
}

@Entity
@Table(name = "Product")
public class Product implements Serializable {

.......
@ManyToMany
@JoinTable(name = "product_order", 
        joinColumns = { @JoinColumn(referencedColumnName="id") }, 
        inverseJoinColumns = { @JoinColumn(referencedColumnName="id") })
private List <Order> orders;
.......
}

Following is the Dao that is used to insert order and product. The products are predefined and I only need to update their quantity. The order is to be inserted as new order. The product_order relationship table has to be generated and data inserted as primary keys from order and product.

public class OrderDao {

 public static void insertProductOrder(Order order, List <Product> cart, List<Integer> quantity){

    ...............
    session.beginTransaction();

    Query query;

    Product pr;

    List list;

    List <Order> orders = new ArrayList<>();
    orders.add(order);

    for(int i=0;i<cart.size();i++) {

    query = session.createQuery("from Product where id = :pid");
    query.setParameter("pid", cart.get(i).getId());
    list = query.list();
    pr = (Product) list.get(0);

    pr.setQuantity(cart.get(i).getQuantity() - quantity.get(i));

    cart.get(i).setOrders(orders);
    }

    order.setProducts(cart);

    session.save(order);

    session.getTransaction().commit();
 }
}'

With this scenario, data is properly inserted into all three tables. But when I try to insert a new order, the data in the relationship table i.e. product_order is overwritten. The record which is overwritten is the one being updated for quantity. This is due to the fact that Hibernate is removing the record from product_order and then inserting it again. What I want is no updation of product_order table as it is not needed. I just want to insert a record into order table, update quantity in the product table and insert order_id and product_id into the product_order table. Thank you in advance.

Upvotes: 2

Views: 810

Answers (2)

abhinav kumar
abhinav kumar

Reputation: 1803

Hi Guys i struggled similar kind of scenario where even after maintaining CascadeType.PERSIST my mappings were getting lost/overridden from the mapping table I learned the hard way to fix this issue

I am providing the similar kind of scenario hope it may be helpful to some people.

i am taking Product and Order to explain the issue and using uni-directional manyToMany mapping

@Entity
@Table(name="Product")
public class Product {

    @Id
    @Column(name = "id")
    private Long id;

    
    @Column(name = "product")
    private String product;

    @ManyToMany(cascade = CascadeType.PERSIST,fetch= FetchType.EAGER)
    @JoinTable(name = "product_order_mapping", joinColumns = { @JoinColumn(name = "product_id") }, inverseJoinColumns = {
            @JoinColumn(name = "order_id") })
    @JsonProperty("orders")
    private Set<Order> orders =new HashSet<Order>();

    //setters and getters


}

@Table(name="Order")
public class Order {

    @Id
    @Column(name = "id")
    private Long id;
        
    @Column(name = "order")
    private String order;

    //setters and getters
    //equals and hashcode

}

Implementation Logic

productList and OrderFunctions are some list with list of products and orders details

for (Product pEntity : productList) {

OrderFunctions = this.getAllLineFunctions(product.getId(), cmEntity.getId())
                        .getBody();
Set<Order> oList = new HashSet<>();

for (OrderFunction orderFn : OrderFunctions) {
Order orderEntity = new Order();
orderEntity.setId(lfs.getOrderFunction_id());
orderEntity.setStageGroup(lfs.getOrderFunctionOrder());
oList.add(sgEntity);
}

orderRepository.saveAll(oList);
pEntity.setOrders(oList);
productRepository.save(pEntity);
}
}

To understand duplicacy mapping let us take an example

Suppose Product is under a Company Now a Company has multiple Product And Product has Multiple Order

Now Let us take a practical example for storing data in tables

set1--> company with id c1 has Product with id p1,p2,p3 product p1 has order o1,o2,o3 product p2 has order o1,o2 product p3 has order o2,o3,o4

set2--> company with id c2 has Product with id p1,p3 product p1 has order o1,o2 product p3 has order o2

Now when save set1 table product_order_mapping looks like

product_id  order_id
---------------------
   p1         o1
   p1         o2
   p1         o3
   p2         o1
   p2         o2
   p3         o2
   p3         o3
   p3         o4

But when save set2 after set1 table product_order_mapping will look like

product_id  order_id
---------------------
   p1         o1
   p1         o2
   p2         o1
   p2         o2
   p3         o2

We can see the difference before saving set2 and after saving set2 mappings p1->o3 , p3->o3 and p3->o4 are lost

Reason for losing mapping

when we tried to store set2 we tried to override the duplicate entry like p1->o1,p1->o2 which are allready available in the mapping table So what happened is before adding these duplicate entry all mappings related to p1 are removed and then p1->o1,p1->o2 got added

And so we lost some previous entry

Solution

Avoid adding duplicate entry from set2 like p1->o1,p1->o2 as they are already present in mapping table

for (Company cEntity : companyList) {
for (Product pEntity : productList) {

OrderFunctions = this.getAllOrderFunctions(cEntity.getId(), pEntity.getId());
                        
Set<Order> oList = new HashSet<>();

for (OrderFunction orderFn : OrderFunctions) {
Order orderEntity = new Order();
orderEntity.setId(lfs.getOrderFunction_id());
orderEntity.setStageGroup(lfs.getOrderFunctionOrder());
oList.add(sgEntity);
}

Set<Order> collectSG = pEntity.getOrders().stream().filter(o->oList.contains(o)).collect(Collectors.toSet());
oList.removeAll(collectSG);
if(oList.size()>0) {
orderRepository.saveAll(oList);
                
pEntity.setOrders(oList);
productRepository.save(pEntity);
}
}
}
}

Upvotes: 1

Lok
Lok

Reputation: 131

Got it working by properly identifying the owner entity and making changes as follows:

@Entity
@Table(name = "Order_")
public class Order implements Serializable {
....... 
@ManyToMany
@Cascade({CascadeType.PERSIST})
@JoinTable(name = "product_order", 
        joinColumns = { @JoinColumn(referencedColumnName="id") }, 
        inverseJoinColumns = { @JoinColumn(referencedColumnName="id") })
private List<Product> products;
.......
}


@Entity
@Table(name = "Product")
public class Product implements Serializable {
.......
@Cascade({CascadeType.PERSIST})
@ManyToMany(mappedBy="products")
private List <Order> orders;
.......
}

Upvotes: 2

Related Questions