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

JPA - Parent Children - Dto to Entity Persistence

 
Michelle Streeter
Ranch Hand
Posts: 120
Eclipse IDE Flex Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Okay, I am quite confused on how I am suppose to deal with persisting a new or changed child of a parent child relationship or one to many relationship.

In the past, I have just saved the child by converting the child to an entity and then do a find on the id of the child. If update then merge if child id is 0 then add child which the child had the id of the parent. However, I discovered a problem with this. If I close the app and reopen the app, then the parent does not have the current reference to either the changed child or the added child. So evidently, I am doing this wrong. However, I was doing it the way I was doing it to save bandwidth. Am I really suppose to copy the parent back from dto to entity with all the children being copied as well and persist the parent and all the children to update one child? That seems like a whole lot of overhead. What am I missing here? Could you imagine how crazy this would get with a Many to Many relationship?
 
James Sutherland
Ranch Hand
Posts: 553
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Yes, you need to maintain both sides of the relationship in JPA.
No, you don't need to send/merge the parent and all of its children.

If you are updating a child, then you just need to find() it, and update it, or use merge().

If you are adding a new child, then you need to persist() or merge() it, set its parent to the one from the current persistence context using find() (merge can do this, but if your parent is transient, or nulled out, then you need to do this yourself, and if the parent's children are transient or nulled out, then you need to be careful it is not merged or cascaded).
You also should add the child to the parent's children (otherwise it will not be there in the current persistence context, or possibly future ones if you are using a shared cache).

There are alternatives to adding the child though:
- If you are using EclipseLink, a List, LAZY, and weaving, then add() will not instantiate the children.
- If the children is not something you want to read, then consider not mapping it, just query it when needed.
- If the parent's children is not instantiated, you could avoid adding the child. (see PersistenceUnitUtil.isLoaded()).
- You can call refresh() on the parent after the commit to reset its children.
- You can avoid adding the child if you clear or throw away your persistence unit, and are not using a shared cache.
 
Michelle Streeter
Ranch Hand
Posts: 120
Eclipse IDE Flex Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I got the part about adding a child and I got the part of updating a child directly, but on updating a child for the parents children list, do I literally iterate through the set and when I find the child by id then update that child? If you get this wrong, wouldnt it overwrite the wrong one in the DB? Or is it just easier and not that big of a hit to just refresh the parent?
 
Michelle Streeter
Ranch Hand
Posts: 120
Eclipse IDE Flex Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Oh, and when you say refresh, is there a JPA command to refresh the parent?
do I first do a find on the parent id then call refresh? or do I do a getParent query and do a refresh? will this update the list of parents when I did my getparents list to begin with? Or will I have two instances of my parent?
Or do I have to iterate through the list of parents to find the parent and then call a get children query?
Should I treat JPA instances like any other POJO? And if so then should I change the set to a List so I can easier iterate through the list?

Sometimes, JPA seems magical and yet other times it seems to not be able to put the link together. I have read it is better to maintain the Dto or Dao objects but if we have to iterate through then sometimes I wonder why it is better. At least in the Dto or Dao objects, most of the time, you can goto the index directly, With the Entity list, you need to iterate through because you may have sorted the Dto or Dao list and changed the order. Though I just realized I am assuming when you sort a list it changes the index. Will have to double check that. Maybe its better to sort at the query to make this easier.
 
James Sutherland
Ranch Hand
Posts: 553
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
To update a child, or any object from a detached object you either,
- call em.find() with the object's id and update it, or call em.merge(), you don't have to iterate of anything or touch the parent's children in anyway

To refresh an object call em.refresh(), the object must be managed, so if it is detached, first call find() (you can also pass the refresh hint to find to avoid the possible duplicate query).

 
Michelle Streeter
Ranch Hand
Posts: 120
Eclipse IDE Flex Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
How do I know if its detatched?

I suspect its not. I am using a stateless session bean with 2nd level cache turned off.

In the past, I just persisted or merged the child but when I loaded the app again, the child was either not there or it displayed the old version of the child which leads me to believe the parent is still managed. Actually, now that I have stated this, how does your above statement make sense. Because I am doing a find on the child which I suspect its getting it from the db since the children of the parent are loaded implicitely when the parent is loaded. So the find is not finding that specific child and so probably loading it from the db which is why the disconnect happens. So, are you saying I do a find on the parent and then a fiind on the child and that will work? How does the second find know I am referring to the parents child instead of just doing what it is currently doing? This is why I figured I needed to iterate through the parents children to find the child I needed copy the updated info to and merge. I appreciate your patience.

I suspect the best scenario is to persist the child and then refresh the parent. Assuming the parent is still managed.
 
James Sutherland
Ranch Hand
Posts: 553
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
em.contains(object) will return if the object is managed or not.

Assuming you are using a JTA managed EntityManager in your SessionBean, then anything you read in the current transaction (sb method call) will be managed. Anything you read in a previous transaction (sb method call), will be detached.

If you are using a stateless session bean and no 2nd level cache, then I have no idea how you could every get stale data.

Within the same transaction there is only one copy of any object, the child you read through find will be identical to the child you get from the parent.
 
Michelle Streeter
Ranch Hand
Posts: 120
Eclipse IDE Flex Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Let me give you an example
I run get A's which the eao runs a query (select a from A a) explicitly loading all A's from the DB
Then the stateless session bean (SSB) converts the entities created to dto's
ADto.id = A.id
ADto.name = A.name
ADto.B's = new ArrayList<BDto>
for (B b : a.getBs())
{
BDto.id = b.id
BDto.name = b.name
A.Bs.add(BDto)
}

The for loop inplicitly loads the B entities but they are associated with A's

Now, if I run add B which converts Bdto to B and the persists. In this case a new B is added but has no idea its associated with the list of B's in an A even though in the conversion of the Bdto to B I did a find for the link to A. So unless I refresh A or now add the B to As list, the next time I load the app, it will already have a managed A and give me that A and so the new B will be missing.

Now, if I run update B which converts the Bdto to a B, when I do the find, it creats a managed B separate from the B which was implicitely loaded by A. So when I update that B there are actually two copies of the old B being managed and only the explicit B that was loaded from the find will be updated and the one that is associated with the A is not. And so the next time you load the app, it finds the managed A and gives you the list of Bs associated with that A and not the one you updated and so the list will display the old data. Again, unless you refresh the A or iterate through the B list for the A and find the one and explicitly update that B.

At least this is what I am seeing happen and that is with Second Level Cacheing turned off.
 
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic