@ManyToOne unidirectional mapping

13,430

Solution 1

Start by removing the cascades, since you precisely don't want to create/modify the PaymentMethod when you create/modify a Payment.

Then, when you create a Payment, assign it the existing PaymentMethod you want it to be associated with:

PaymentMethod existingPaymentMethod = em.find(PaymentMethod.class, idOfThePaymentMethod);
Payment payment = new Payment();
payment.setPaymentMethod(existingPaymentMethod);
em.persist(payment);

Solution 2

remove cascade={CascadeType.REFRESH,CascadeType.MERGE,CascadeType.PERSIST} from your Payment entity.

Since PaymentMethod is a master table like you said, you do not want to have any cascading operation happens from Payment down to PaymentMethod.

Share:
13,430
Zuned Ahmed
Author by

Zuned Ahmed

Updated on June 30, 2022

Comments

  • Zuned Ahmed
    Zuned Ahmed almost 2 years

    I have an Entity class Payment which has Entity class PaymentMethod with Many to one relationship.

    Now since My PaymentMethod is master table.

    So i want to save payment without effecting the master table PaymentMethod.

    But every time i save the Payment it insert new row in PaymentMethod to. I unable to figure out why.

    I m using SpringData save.

    Payment.java

    @Entity
    @Table(name="payment")
    public class Payment implements Serializable {
       @Id
       @GeneratedValue(strategy = GenerationType.AUTO)
       private Long id;
    
       @ManyToOne(cascade={CascadeType.REFRESH,CascadeType.MERGE,CascadeType.PERSIST})
       @JoinColumn(name="payment_method_id")
       private PaymentMethod paymentMethod;
    
      //getter and setters
    }
    

    PaymentMethod.java

    @Table(name="paymentmethod")
     public class PaymentMethod implements Serializable {
     @Id
     @GeneratedValue(strategy = GenerationType.AUTO)
     private Long id;
    
     @Column(name="name",unique=true ,nullable=false)
     private String name;
    
     //GETTER AND SETTERS
    }
    

    PAYMENT_METHOD DATA

    ID | NAME 
    1  | AA
    2  | BB
    3  | CC
    

    After payment get save PAYMENT_METHOD entry with duplicate name ?

    I am unable to figure out why?

    For saving i have created simple CRUDRepository for Payment type

    calling save entity of repository.

    Please suggest where i am falling out.

    whenever code executes this line :

    paymentMethodRepository.findByName(payment.getPaymentMethod().getName());
    

    It gives error as

    Caused by: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing: com.enrollment.domain.Payment.paymentMethod -> com.enrollment.domain.PaymentMethod
        at org.hibernate.engine.spi.CascadingAction$8.noCascade(CascadingAction.java:380)
        at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:176)
        at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:423)
        at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:264)
        at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:193)
        at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:136)
        at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:78)
        at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:208)
        at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:151)
        at org.hibernate.internal.SessionImpl.firePersistOnFlush(SessionImpl.java:870)
        at org.hibernate.internal.SessionImpl.persistOnFlush(SessionImpl.java:863)
        at org.hibernate.engine.spi.CascadingAction$8.cascade(CascadingAction.java:346)
        at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:380)
        at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:323)
        at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:208)
        at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:409)
        at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:350)
        at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:326)
        at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:208)
        at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:165)
        at org.hibernate.event.internal.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:448)
        at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:293)
        at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:193)
        at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:136)
        at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:78)
        at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:208)
        at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:151)
        at org.hibernate.internal.SessionImpl.firePersistOnFlush(SessionImpl.java:870)
        at org.hibernate.internal.SessionImpl.persistOnFlush(SessionImpl.java:863)
        at org.hibernate.engine.spi.CascadingAction$8.cascade(CascadingAction.java:346)
        at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:380)
        at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:323)
        at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:208)
        at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:165)
        at org.hibernate.event.internal.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:448)
        at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:293)
        at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:193)
        at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:136)
        at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:78)
        at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:208)
        at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:151)
        at org.hibernate.internal.SessionImpl.firePersistOnFlush(SessionImpl.java:870)
        at org.hibernate.internal.SessionImpl.persistOnFlush(SessionImpl.java:863)
        at org.hibernate.engine.spi.CascadingAction$8.cascade(CascadingAction.java:346)
        at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:380)
        at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:323)
        at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:208)
        at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:165)
        at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:423)
        at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:264)
        at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:193)
        at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:136)
        at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:78)
        at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:208)
        at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:151)
        at org.hibernate.internal.SessionImpl.firePersistOnFlush(SessionImpl.java:870)
        at org.hibernate.internal.SessionImpl.persistOnFlush(SessionImpl.java:863)
        at org.hibernate.engine.spi.CascadingAction$8.cascade(CascadingAction.java:346)
        at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:380)
        at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:323)
        at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:208)
        at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:165)
        at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:423)
        at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:264)
        at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:193)
        at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:136)
        at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:78)
        at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:208)
        at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:151)
        at org.hibernate.internal.SessionImpl.firePersistOnFlush(SessionImpl.java:870)
        at org.hibernate.internal.SessionImpl.persistOnFlush(SessionImpl.java:863)
        at org.hibernate.engine.spi.CascadingAction$8.cascade(CascadingAction.java:346)
        at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:380)
        at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:323)
        at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:208)
        at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:409)
        at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:350)
        at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:326)
        at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:208)
        at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:165)
        at org.hibernate.event.internal.AbstractFlushingEventListener.cascadeOnFlush(AbstractFlushingEventListener.java:160)
        at org.hibernate.event.internal.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:151)
        at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:88)
        at org.hibernate.event.internal.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:58)
        at org.hibernate.internal.SessionImpl.autoFlushIfRequired(SessionImpl.java:1186)
        at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1241)
        at org.hibernate.internal.QueryImpl.list(QueryImpl.java:101)
        at org.hibernate.ejb.QueryImpl.getSingleResult(QueryImpl.java:285)
    

    I am unable to figure out how the code if trying to flush it when i call finder method from repository.

    Is i am missing something?

    Issue solved thanks @JB and @spiritwalker , But unable to find the exact root cause , so just discussing the behavior may be the discussion will add on too knowledege.

    Behaviour ..
    
    Open Transaction
       1. validate
       2. save child
       3. validate and update 
       4. save parent
    Close Transaction
    
    it was giving above error.
    
    Now, after code change it worked
    
    Open Transaction
       1. validate
       2. validate and update 
       3. save child
       4. save parent
    Close Transaction