I'll simplify the example to get the idea across. My model looks like the following :
Projects have a ManyToMany relationship with Jobs. In order to create the join table, I created a ProjectJob entity that has an added column, "job_order". Project references an OneToMany link to a list of ProjectJob, with CascadeType.ALL.
I have a UniqueConstraint on ProjectJob that looks like this :
#UniqueConstraint(columnNames = {"project_id", "job_order"}
Everything works like a charm unless I want to reorder the jobs in the project. Basically, I flip the orders around in my service and made sure that every ProjectJob was a unique job_order in the list, and then save the Project (since the ProjectJobs will be cascaded). Problems arise when JPA/Hibernate tries to flush the transaction since it tries to update "row by row", and obviously the UniqueConstraint is violated after the first update (even though the whole batch of updates would give a coherent state).
Weirdly enough, the service call happens inside a transaction, so I'm not sure why I get this error.
Is there any way to update the child members of a collection in a unique statement so that the UniqueConstraint gets checked after all the children have been updated, instead of getting triggered after the first one? If not, what solution is available for my use case?
You could try to use deferred constraints. Or you use #ManyToMany #OrderColumn List<Job> jobs in Project instead which handles all of this automatically for you.
Related
Have an entity with a OneToMany relationship in Spring Data that I would need to filter by a specific column. I've been unable to find a satisfying solution to get it with a single query (both parent entity and the filtered OneToMany in one go) so I'm now thinking in having it lazy fetched and after getting the parent entity, have a separate query to retrieve the linked entities and just using a setter to link them together (ie: parent.setChildren(childrenRepository.findAllBy...)).
Question is: would this effectively disable any future trigger to the lazy fetch so my manually set children would remain? Use case here is to add these children manually in certain cases, and using the default lazy fetch when the conditions don't apply.
I tested this and seems to be working as intended, but could not find information on when the lazy fetch should be triggered as to make sure my logic would work in any scenario (is it disabled as soon as the setter is invoked? is this documented anywhere?)
Thanks in advance.
I didn't know if it was appropriate to delete the original question but have added a more concise explanation at the bottom hopefully eliminating the need to review all of the details.
I am receiving inconsistent results and can't figure out what I am doing wrong. It relates to an EE7 web app with container managed Entity Manager, Java 8, EclipseLink 2.5.2, netbeans 8.0.2 IDE.
There is a parent child relationship mapped as a bi-directional relationship:
Event Table is the parent which maps to Games
#OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "event")
#OrderBy("gameType, round")
private List<Game> gameList;
Games maps the Event as
#JoinColumn(name = "EVENT", referencedColumnName = "ID")
#ManyToOne(fetch = FetchType.EAGER, optional = false)
private Event event;
Step 1:
Using a web interface I create an event and one game. The game is added to the event list and event to the game. em.persist(event) is called to save all of it.
-> works great
Step 2:
Edit the event
create a new game - Add the event to the game, em.persist(game) with em.flush() to get the id and add it to the game list in events
After all editing is done em.update(event)
-> database updates exactly as expected
-> BTW I understand that it is a better user experience to not persist game until the event is ready to be saved to allow option of 'quit'. It is something I plan to do but don't yet have a good handle on best practices for doing so and my experience with cascade is mixed.
Finally the issue:
- 100% of the time 'step two' works correctly with the database but when I retrieve the event for further edits the event has an additional entry in the game list with value of null.
- if I shut down and restart the app over and over sometimes it happens and sometimes it doesn't. The database always remain in tact and exactly as expected
- simultaneously using a different browser from the same machine or others has the same result
- obviously the persistence cache CAN get in an inconsistent state but I have not been able to determine if I am doing something wrong or experiencing an EclipseLink bug
Earlier I reported a problem that turned out to be an EclipseLink bug related to the IndirectList class and Java 8 so I am not sure if I am doing something wrong or if this is similar.
Thanks in advance!
After some reading through the JPA JSR I am wondering if my understanding is correct. Using the EE container managed transaction scoped persistent context in a web application essentially means a transaction begins with a method call to the EJB and end with the return. Therefore getting info to put something on a screen is one transaction and updating after the users changes is another. Or more clearly, I am almost always working with detached entities.
My approach has been to get the entity tree, let the user/administrator update whatever is necessary and then persist, merge or delete as appropriate. Since it is detached orphan removal doesn't work so I do that directly but maintain the relationships. I also persist the new children directly and presume I can em.merge the parent as much as I want because it becomes attached, applies the changes and my entity is in tact with the DB.
Is that a fair approach or is it it flawed somehow?
I am still having this issue and have possibly tracked it a little further. I have a number of related records with bi-directional links via foreign keys #ManyToOne and #OneToMany.
It is essentially a simple chain with backward links:
Entity A contains a List of B, and B a foreign key to A
Entity B contains a List of C, and C a foreign key to B
Steps to reproduce:
new C(B, and other constructor parameters)
B.add(C)
Set other properties in C
em.persist(C) to ensure an ID
em.update(B) to register updated relationship
After every step including 4, everything looks correct. I thought perhaps step 2 should follow 4 but am not sure it would make a difference.
After step 5,
B has the same memory location and pointer to C.
C has been updated with NEW memory locations for the references to B and A.
i.e. the pointer back to B and A in C is a different memory location than the source
Subsequently when I refresh the entity with a em.find(), entity B has NULL entries in List C although the MySQL database reflects what I expect.
I have obviously done something wrong which has corrupted the EclipseLink persistence cache. The only thing I have found to fix this is restarting the app to delete the cache.
I have not been able to resolve this for months and would appreciate any advice.
The problem was ultimately tracked to a coding error. Specifically the reference pointer returned from em.merge() was not updated in the related entities resulting in multiple copies and corrupting persistence.
Something in the startup sequence affected whether this corruption became evident with the symptom of null's in a list or everything working fine. It appeared sporadic but in fact was the result of the earlier corruption.
It's been a while since I've used Hibernate, and just now working with it again. I'm using Hibernate JPA.
Edited Question
How can I delete and add an object in the same transaction? I have a request mapped in the controller to a service layer that reaches to the dao layer. I would like to delete an object, make a copy of that object, and re-add the object, but I think the persistence context is getting flushed, so I can complete the delete, but then I get an detached entity exception when trying to re-add the object at the controller layer. This is probably a newbie mistake...help?!
User <- retain
association <- remove
association <- remove
association <- remove
...
association <- remove
Original Question
I'm trying to create a function that will remove all records that have a foreign key with MainRecord. Currently, all tables that have foreign keys with MainRecord are set to cascade on delete, so if I remove MainRecord, it will automatically delete everything.
The state I want is to remove all records, except for the main record, so that it's like the MainRecord just got inserted into the system, and any associations made with it will be saved after this. There's A LOT of tables that use MainRecord though, and I don't want to write a custom query to remove the record from each of those tables, so I was thinking of deleting the MainRecord, creating a copy, and re-adding it to the database.
Though I feel like there should be a way to do this without deleting the MainRecord.
Is there a better way to do this?
It might have been partly how I was delivering the object to the back end; it might have been that the connection was flushing before I completed both transactions; I'm still not sure I understand that exact cause, but I figured out a way to get it working the way I need it to.
I ended up relying on the client to make a call for delete, then make a call for add after the call for delete finished, and this works. I doubt it's ideal...so I'm still open to alternative solutions, but it works...
In a #OneToMany relationship if I want to remove a child, do I need to explicitly delete that child from parent's collection as well or just deleting the child will suffice?
For instance, Person and Phone. Each person has many phone numbers. If I want to delete one phone number from a person is this enough:
EntityManager.remove(phone);
Or I need to to this beforehand:
Person.getPhone().remove(phone);
Not to mention, the CascadeType is set to MERGE.
You need to remove the Phone explicitly from the phones collection, it's not enough to remove it with the EntityManager.
From the other side, it might be sufficient to use orphanRemoval, so if you remove an entity from a collection, it gets automatically deleted. Something like:
#OneToMany(mappedBy="person", orphanRemoval="true")
private List<Phone> phones;
See also: http://docs.oracle.com/cd/E19798-01/821-1841/giqxy/index.html
Cascade.REMOVE only removes the child entity if the parent entity is removed as well. Cascase.MERGE has nothing to do with this problem.
Not sure if MERGE is enough to get entities deleted cascaded, you will probably have to also define DELETE cascading and depending on how the data is mapped (with or without a secondary table in between) it might even be necessary to apply orphan removal too.
If you don't apply cascading for deletion but rather use a JPA query or an entityManager.remove(), then it is certainly a good idea to manually evict it from the oneToMany collection as well. The reason is simple: you may manually remove it from the database, but that doesn't mean it automagically gets removed from the collection too so for the lifetime of the parent entity, it will still be referencing an entity which is not supposed to exist anymore. Things get weird when you then also accidentally change the state of said entity.
I've been using JPA 2.0 for a while but, sad to admit, I haven't had enough time to learn it properly. It seems like I lack the basics of how to work with Entity Manager.
Moving one step at a time, I'd like to first ask you about maintaining relationships between mapped entities. Of course I know how to create mappings between entities, different types of available associations (OneToOne, etc.) and how databases work in general. I'm purely focused on maintaining it via Entity Manager, so please do not send me to any kind of general knowledge tutorial :-).
The questions are:
Am I right that as a programmer I'm responsible for maintaining (creating/updating/removing) relationships between instances of entities?
Do I have to always update (set to null, remove from collection, etc.) instances by hand?
Plain SQL can set entities to NULL on deleting, but it seems like JPA can't do such a simple thing. It also seems like a burden to do it manually. Is there a way to achieve that with JPA?
If I have OneToMany relationship and set to NULL the entity on the Many side of the relationship. Then I persist the changes in a Set by saving the entity on the One side. Do I then have to update the entities in the Many side and set association to NULL in each instance? Seems pure silliness for one-directional bindings!
Thanks in advance!
The main thing you need to investigate is the different options you have when mapping on entity. For example in the next piece of code the cascade all option will instruct jpa to delete the child list when the parent is deleted.
#OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.ALL }, mappedBy = "parent")
private Set<Child> events = new HashSet<Child>();
Yes. You maintain the object tree and modify it to look like what
you want.
Yes and no. If you want the entity to reference null, then yes.
For instance, if you are removing one Entity, then you should clean
up any references to it held by other entities that you are not
removing. A practical example: its good practice to let an Employee
know his/her Manager has been let go. If the Employee is going to
stay, it should either have its manager reference nulled out or set
to a different manager, before the current manager can be removed.
If the employee is going to be removed as well, then cascade remove
can cascade to all the Manager's subordinates, in which case you do
not need to clean up their references to the manager - as they are
going away too.
I don't quite understand what SQL is setting to null. Deleting
removes the row in the database, so there isn't anything to set to
null. Cleaning up a reference shouldn't be that difficult in the
object model, as JPA has a number of events to help such as
preremove preupdate etc. In the end though, the problem is with
your java objects. They are just java objects, so if you want
something done, your application will need to do it for the most
part. JPA handles building them and pushing them to the database,
not changing the state for you.
Yes and no. If you set up a bidirectional relationship, you must
maintain both sides as mentioned above. If you set the child's
parent reference to null, you should let the parent know it no
longer has a child, wouldn't you? Your parent will continue to
reference its child for as long as that Parent instance exists. So
even though the database is updated/controlled through the side that
owns a relationship, the object model will be out of synch with the
database until it is refreshed or somehow reloaded. JPA allows for
multiple levels of caching, so it all depends on your provider setup
how long that Parent instance will exist referencing a Child that no
longer exists in the database.