0

i have two entities Price<-1----1->PriceDetail mapped as OneToOne.

how can i handle different scenarios for this relation. so i have cases where i always want a new price and a new pricedetail, but i also would be able to create a new price only and update the pricedetail (with data from a previous price-entity). my current solution is to remove the pricedetail-entity, how can it be done with updating the pricedetail-entity?

@Entity
class Price {

  @OneToOne(cascade=CascadeType.ALL,mappedBy = "price")
  private PriceDetail priceDetail;
}

@Entity
class PriceDetail {

  @OneToOne
  private Price price;
}

save-method:

EntityManage em = getEntityManager();

for (Price price : getAllPrices()){ 

  Price oldPrice =  Price.getById(price.getId());               

  if (!oldPrice.equals(price)){ //if we have price-changes

     if (PriceCatalog.entryExists(oldPrice)){ //if the current-price is in a catalog

      //current solution: remove entry from PriceDetail, but i want to update PriceDetail-Entity, pointing 
      //to the newly created price
      em.remove(oldPrice.getPriceDetail());
      em.commitTransaction();

      oldPrice.setActive(false);  //referenced price in PriceCatalog is now inactive                

      //sets id null, so that a new price-entity is created
      price.setId(null);            
      price.setActive(true);                        
      em.persist(price);   //also inserts a new price-detail

     }else {
      em.merge(price);
     }
   }                        
 }
 em.commitTransaction();

because of CascadeType.ALL-Annotation in Price-Entity, JPA tries to insert a new PriceDetail-Entity.

approach 1:

price.getPriceDetail().setId(oldPrice.getPriceDetail().getId());

-> Error: insert into pricedetail violates unique-constraint: Key already exists

approach 2:

  //ommit cascade
  @OneToOne(mappedBy = "price")
  protected PriceDetail priceDetail;

then approach 1 works, but creating a complete new price results in: During synchronization a new object was found through a relationship that was not marked cascade PERSIST

Steve
  • 384
  • 1
  • 7
  • 17

1 Answers1

1

approach 2 is not an option in you case, this is the correct mapping to do a bidirectional one-to-one association:

//you must do this to handle the bidirectional association
  @OneToOne(mappedBy = "price")
  protected PriceDetail priceDetail;

Now the problem is :price is a new entity then the entityManager will call persit operation on price.getpriceDetail() because cascade persist is triggered automatically (not cascade-merge) to avoid this strange behaviour you can do the following.

EntityManage em = getEntityManager();

for (Price price : getAllPrices()){ 

  Price oldPrice =  Price.getById(price.getId());               

  if (!oldPrice.equals(price)){ //if we have price-changes

     if (PriceCatalog.entryExists(oldPrice)){ //if the current-price is in a catalog

      //current solution: remove entry from PriceDetail, but i want to update PriceDetail-Entity, pointing 
      //to the newly created price
      //em.remove(oldPrice.getPriceDetail());
      //em.commitTransaction();

      oldPrice.setActive(false);  //referenced price in PriceCatalog is now inactive                

      PriceDetail priceDetailold = price.getPriceDetail();
      price.setPriceDetail(null);
      priceDetailold.setPrice(null);
      //sets id null, so that a new price-entity is created
      price.setId(null);            
      price.setActive(true);  

      em.persist(price);   //inserts a new price
      price.setPriceDetail(priceDetailold);
      em.merge(price);// attach the pricedetail to the price
  }else {
      em.merge(price);
  }
 }                      
}
em.commitTransaction();
SEY_91
  • 1,615
  • 15
  • 26
  • this way it's just working as excepted. good to know, will keep an eye on your suggestion in future. many thanks! – Steve Aug 11 '16 at 13:10