• Post Reply Bookmark Topic Watch Topic
  • New Topic

JPA cannot persist cascading related entities  RSS feed

 
Vasilis Souvatzis
Ranch Hand
Posts: 94
1
Chrome Java Netbeans IDE
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
*SIGH* I should bloody know this! Every time I start a new project and need to set up entities, I always change something in the RDBMS and maybe that's what's messing it up. Or I rely too much in the IDE auto-generating my entities and instead I should write them myself.

The scenario: class Person has OneToOne with FoodHabit and is uni-directional from Person to FoodHabit. Cascading operations is enabled, therefore when I persist the Person entity, it should auto-persist the FoodHabit i've set to the Person right? Well it doesn't, it only persists the Person and it's driving me insane.

Entities:

So in my JSF backing bean, I call the save method which takes the values from input texts bla bla bla

All this seems fine to me, and from what I've seen in the books I have is correct. Either I'm missing something trivial or it's screwed by having the IDs auto-generated by Postgres.

Man, I keep getting stuck at trivial things.....
 
Vasilis Souvatzis
Ranch Hand
Posts: 94
1
Chrome Java Netbeans IDE
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Found it! In the backing bean where I construct the objects to be persisted, after I set the FoodHabit to the Person, I must also set the Person to the FoodHabit. Seems like I did a bi-directional relationship after all?

Obviously it works now, it's apparent though I need to work more on ORM, I touch it only when I begin a project and then forget it because "it works". So when I start another project, it's all over again.
 
Frits Walraven
Creator of Enthuware JWS+ V6
Saloon Keeper
Posts: 2966
210
Android Chrome Eclipse IDE
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
In the backing bean where I construct the objects to be persisted, after I set the FoodHabit to the Person, I must also set the Person to the FoodHabit. Seems like I did a bi-directional relationship after all? 

Yes, it is a bidirectional relationship because you have @OneToOne annotations on both sides of the relationship.

The cascade did not work because you have the cascade option (cascade = CascadeType.ALL) on the non-owner side of the relationship (that is where the mappedBy attribute is).

In other words there are two solutions to your problem:
  • set both sides of the relationship before you do the persist (that is what you did), or
  • move the mappedBy attribute to the FoodHabit Entity, and @JoinColumn to the Person Entity.


  • From the JPA specs:
    3.2.4 Synchronization to the Database
    Bidirectional relationships between managed entities will be persisted based on references held by the owning side of the relationship. It is the developer’s responsibility to keep the in-memory references held on the owning side and those held on the inverse side consistent with each other when they change. In the case of unidirectional one-to-one and one-to-many relationships, it is the developer’s responsibility
     
    Vasilis Souvatzis
    Ranch Hand
    Posts: 94
    1
    Chrome Java Netbeans IDE
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Frits Walraven wrote:Yes, it is a bidirectional relationship because you have @OneToOne annotations on both sides of the relationship.

    Seriously, this exact wording of yours is invaluable. On all the resources I've looked, no one has said simply that "annotations on both entities for bi-directional, only on parent for uni-directional"...


    The cascade did not work because you have the cascade option (cascade = CascadeType.ALL) on the non-owner side of the relationship (that is where the mappedBy attribute is).

    I see. If I had created my SQL tables with the FK on the Person entity, it would have worked. So essentially, with the FK on the FoodHabit table, the FoodHabit is the parent and Person the child entity.

    Now, this means that the Person entity must have a FK for the Measurement entity, although in a Join table because it's a OneToMany relationship.


    In other words there are two solutions to your problem:
  • set both sides of the relationship before you do the persist (that is what you did), or
  • move the mappedBy attribute to the FoodHabit Entity, and @JoinColumn to the Person Entity.

  • For the time being I'll stick to the bi-directional relationships in order to finish my project and not re-creating my DB tables, but changing to uni-directional is mandatory. To me it makes sense to go from Person to FoodHabit and not the other way around.

    From the JPA specs:
    3.2.4 Synchronization to the Database
    Bidirectional relationships between managed entities will be persisted based on references held by the owning side of the relationship. It is the developer’s responsibility to keep the in-memory references held on the owning side and those held on the inverse side consistent with each other when they change. In the case of unidirectional one-to-one and one-to-many relationships, it is the developer’s responsibility
    Quite frankly I won't be making bi-directional relationships any more, now that you explained what's going on I'll need to be extra careful with the Entities though, especially if I let the IDE auto-generate them. It doesn't mean the program is always right...
     
    Frits Walraven
    Creator of Enthuware JWS+ V6
    Saloon Keeper
    Posts: 2966
    210
    Android Chrome Eclipse IDE
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Seriously, this exact wording of yours is invaluable. On all the resources I've looked, no one has said simply that "annotations on both entities for bi-directional, only on parent for uni-directional"...

    The key factor here is that you have also used the mappedBy attribute on one side of the relationship. Had you not done that, you would have ended up having two uni-directional relationships.

    For the time being I'll stick to the bi-directional relationships in order to finish my project and not re-creating my DB tables, but changing to uni-directional is mandatory. To me it makes sense to go from Person to FoodHabit and not the other way around. 

    OK, when you change to unidirectional you will have to change the foreign key (@JoinColumn) to the Person table and remove the @OneToOne mapping on the FoodHabit entity.

    I'll need to be extra careful with the Entities though, especially if I let the IDE auto-generate them. It doesn't mean the program is always right...

     
    Himai Minh
    Ranch Hand
    Posts: 1563
    10
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator

    public class Person implements Serializable {
        
        @OneToOne(cascade = CascadeType.ALL, mappedBy = "personId")
        private FoodHabit foodHabit;
        
        @OneToMany(cascade = CascadeType.ALL, mappedBy = "personId")
        private List<Measurement> measurementList;
    }

    public class Measurement implements Serializable {
        
        @JoinColumn(name = "person_id", referencedColumnName = "person_id")
        @ManyToOne(optional = false)
        private Person personId;
    }

    public class FoodHabit implements Serializable {

        @JoinColumn(name = "person_id", referencedColumnName = "person_id")
        @OneToOne(optional = false)
        private Person personId;
    }



    This is my other suggestion:


    Based on the entities, FoodHabit is the owner and Person is the being owned entity with cascade type = ALL.
    So, if we persist a FoodHabit entity, it will persist the person it is related to automatically.
     
    Himai Minh
    Ranch Hand
    Posts: 1563
    10
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    With regard to my previous post, after I double checked with the JPA Pro, the CASCADE.ALL should be on the owner side , which is the FoodHabit.
     
    Vasilis Souvatzis
    Ranch Hand
    Posts: 94
    1
    Chrome Java Netbeans IDE
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Himai, I should have responded earlier so you didn't waste time answering for my crappy design. Your code makes perfect sense although it's the opposite of what I wanted to do, because I had it backwards in my head :P Owner entity is Person and the other two are the children. I could keep them bi-directional but it didn't make much sense to navigate the other way. When I fixed it, everything worked correctly hahaha

    Actually, last week I went back to the "Beginning Java EE 7" book and re-read the ORM chapter. I noticed the things I missed the first time I read it, and why I thought I understood it (when clearly I didn't). There's no way I'm having problems again with this level of relationships (to this level of simplicity of course hahah)

    Final code:
     
    • Post Reply Bookmark Topic Watch Topic
    • New Topic
    Boost this thread!