Win a copy of The Little Book of Impediments (e-book only) this week in the Agile and Other Processes forum!
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic

Updating child DTO object and @MapsId

 
Rj Ewing
Ranch Hand
Posts: 91
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I am getting an EntityExistException when I try and update the Parent and the Child is a DTO. How do I go about fixing this? I've tried calling merge on Parent, with no luck. I am using Spring data jpa and hibernate.



In my rest service I am doing the following:





I think the problem is that the Child object is already in the session from the "parentRepositoryService.getParent(id)" call. Then the Child object is created outside the session causing a mismatch between the old and new Child object. I'd prefer to not have to manually update the session based Child object from the request data, and would like to just execute an equivalent to  mysql "insert... on duplicate key update" for the Child entity.
 
Frits Walraven
Creator of Enthuware JWS+ V6
Saloon Keeper
Pie
Posts: 2534
113
Android Chrome Eclipse IDE
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I am not familiar with Spring data but, if I would use JPA, I would save the Child object as the Child is the owner of the @OneToOne relationship.

In JPA:
 
Rj Ewing
Ranch Hand
Posts: 91
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Frits Walraven wrote:I am not familiar with Spring data but, if I would use JPA, I would save the Child object as the Child is the owner of the @OneToOne relationship.

In JPA:


When I try this, I get the same exception:
javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session: Child

On the point about who owns the relationship...
Maybe I have it backwards. I will only access, persist, etc the Child through the Parent. Instead of having all the child properties directly in the Parent class, I would like to keep them in a separate object as all the Child properties go together.
Should I map the relationship differently?

 
Frits Walraven
Creator of Enthuware JWS+ V6
Saloon Keeper
Pie
Posts: 2534
113
Android Chrome Eclipse IDE
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
When I try this, I get the same exception:
javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session: Child

Was your Child object already in the database? What is you transaction demarcation?

Should I map the relationship differently?

No, your mapping is correct. This is exactly how JPA describes a derived identity (2.4.1 Primary Keys Corresponding to Derived Identities).

You have an alternative mapping though:
 
Rj Ewing
Ranch Hand
Posts: 91
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Frits Walraven wrote:Was your Child object already in the database? What is you transaction demarcation?


Yes. It works fine when the Parent doesn't already have a Child, but when there is an existing Child and I want to update/replace the Child is when it fails.

As far as the transaction, the process goes like this.

  • 1. call a service to validate the data.

  •   
    • 1a. in this service, I create the new Child object and save it to the session.
  • 2. client calls a service to upload the data.

  •    
    • 2a. fetch the new Child from the session.

    •  
    •   2b. fetch the Parent with old Child from the db.         //this is in the service/repository layer and is transactional
    •     2c. Parent.setChild(new Child);
    •     2d. save the Parent (and new Child) to the db.    //this is in the service/repository layer and is transactional


    The flow might seem a bit weird for this simple example, but it is part of a validation and upload service for excel spreadsheets, where I need to get user input if there are warnings before uploading (thus the 2 rest services).

     
    Frits Walraven
    Creator of Enthuware JWS+ V6
    Saloon Keeper
    Pie
    Posts: 2534
    113
    Android Chrome Eclipse IDE
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator

    2b. fetch the Parent with old Child from the db.         //this is in the service/repository layer and is transactional
    2c. Parent.setChild(new Child);
    2d. save the Parent (and new Child) to the db.    //this is in the service/repository layer and is transactional

    Ok, you can't do step 2c. as your new Child doesn't have the same id as the Child in the database. Can't you update the data like this:


     
    Rj Ewing
    Ranch Hand
    Posts: 91
    1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    ya I could do that, but I was hoping I could just force the update with jpa. I will never need to keep the any properties from OldChild and want to overwrite everything with NewChild.

    I also don't want to fetch the Old Child in step 1 above as I have no need for the old child data.

    Since Child contains about 10 properties in my case, I see 2 options.

    1. delete old Child before saving new Child.

    2. Write a mapper class that transfers the data from new Child to Old Child

    I am think option 2 would be best.
     
    Frits Walraven
    Creator of Enthuware JWS+ V6
    Saloon Keeper
    Pie
    Posts: 2534
    113
    Android Chrome Eclipse IDE
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Well there is still another alternative by using the orphanRemoval option.

    Now you can change the Child object completely:
     
    Frits Walraven
    Creator of Enthuware JWS+ V6
    Saloon Keeper
    Pie
    Posts: 2534
    113
    Android Chrome Eclipse IDE
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    From my notes:

    The difference between cascade=REMOVE

    and the orphanRemoval=true:

    is when you break the relationship between Parent and Child by either:
  • setting the relationship to null or
  • by changing the relationship from a Parent to another Child.
  • In those cases the Child is removed from the database (as it would be when the remove operation is executed on the Parent entity in case of  cascade=CascadeType.REMOVE).

    When the remove operation is executed on the Parent (with orphanRemoval=true) the remove operation will be cascaded just like when the cascade=CascadeType.REMOVE would have been specified.
     
    Rj Ewing
    Ranch Hand
    Posts: 91
    1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    hmm, I am still getting the exception:

    javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session
     
    Frits Walraven
    Creator of Enthuware JWS+ V6
    Saloon Keeper
    Pie
    Posts: 2534
    113
    Android Chrome Eclipse IDE
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    I guess you ran into this Hibernate bug: Orphan removal does not work for OneToOne relations
     
    Rj Ewing
    Ranch Hand
    Posts: 91
    1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    hmm, I'm not sure it is that bug.

    If I set Child to null, it old Child is deleted.

    I'm thinking it has to do with the @MapsId on the Child.parent
     
    Frits Walraven
    Creator of Enthuware JWS+ V6
    Saloon Keeper
    Pie
    Posts: 2534
    113
    Android Chrome Eclipse IDE
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Yeah, probably another bug.

    I am using EclipseLink and everything is working as expected. I guess going for option 2 in your case is the best.
     
    Rj Ewing
    Ranch Hand
    Posts: 91
    1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    ya, I filed a bug report and went with option 2. Thanks
     
    • Post Reply
    • Bookmark Topic Watch Topic
    • New Topic