Using JPA i have a question relating to the CascadeTypes.
for Example:
#ManyToMany(fetch=FetchType.LAZY, cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
is different to this:
#ManyToMany(fetch=FetchType.LAZY, cascade={CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
Why?
I need the cascadetype persist to automatically insert referenced objects in my
entityclass. and i need merge because i dont want to have double entries in my
tables. but when i define persist first, merging doesnt work, when i define
merge first, persist doesnt work.
why?
The JPA specification is actually a very readable document and can be downloaded here:
https://jcp.org/aboutJava/communityprocess/final/jsr317/index.html
Inside it on page 384 it covers the cascade attribute of the ManyToMany annotation:
The cascade element specifies the set of cascadable operations that
are propagated to the associated entity. The operations that are
cascadable are defined by the CascadeType enum: public enum
CascadeType { ALL, PERSIST, MERGE, REMOVE, REFRESH, DETACH}; The value
cascade=ALL is equivalent to cascade={PERSIST, MERGE, REMOVE, REFRESH,
DETACH}.
As you can see it says nothing about the order. What is probably happening is your application is sometimes using a new object that needs to be persisted and sometimes loading one from the database that then needs to be merged. In order words, its an application issue.
Personally I use a DIY approach to merging entities in my persistence context. A good article to read on the subject is here:
http://blog.xebia.com/2009/03/23/jpa-implementation-patterns-saving-detached-entities/
Related
For a while now I have been wondering why when using JPA, do I have to write my delete methods like this:
#Transactional
public void delete(Account account)
{
if (entityManager.contains(account))
{
entityManager.remove(account);
}
else
{
entityManager.remove(entityManager.merge(account));
}
}
Perhaps the contains isn't needed since the transaction begins and ends with this method, but I still wonder why the remove couldn't just take an unmanaged object. Is it because it needs to be managed in order to know what the id is for that object? Any other insights would be great to hear. I just want to understand the hows and whys of the JPA remove.
The remove operation can be cascaded to associations of an entity.
To be able to know which associated entities to remove, the entity manager can't rely on a detached entity, since, by definition, this detached entity doesn't reflect the latest state of the entity, and doesn't necessarily have all its cascaded associations recursively loaded.
So, if it accepted a detached entity, remove() would have to decide for you: either merge the detached entity and execute the remove operation based on what the detached entity contains, or simply load the entity having the same ID as the detached entity, and execute the operation based on the loaded entity.
Instead of deciding for you, it simply requires an attached entity. That way, you decide what you want.
I am using EclipseLink 2.3.3. with a data model with about 100 entities. I have a Java class mapped to each database table using annotations.
I have two use cases to implement. One is that a new record enters the system that hits about 60-75 of the tables. For this case, I want merge and persist to cascade, so that I can just merge the top level object and have that cascade to all related entities.
Another use case is that I need to insert a collection of individual objects, often one from each of a bunch of different tables. In this case I don't want the cascading merge, because I need to have control over the insertions. If I have cascade enabled, merging the first object might or might not merge the other objects, depending on if or how they are related, so I'd rather explicitly merge each of them.
So essentially, I want cascading merge and persist in one situation, but not another. So if I include the cascade annotations in the mapped classes, I need to selectively disable the cascading for certain operations; or, if I turn off cascading in the mapped classes, I would like to enable cascading for certain operations.
So far I am not finding any way to selectively turn on or off cascading for a particular operation. There is a CascadePolicy class but that seems to only be used with queries. There are dynamic entities, and I was thinking perhaps I could use that to do something like create a dynamic entity from an existing entity and turn off the cascading behavior on that entity's relationships and somehow use that for the merge, but I have not been able to find the right API for that.
So I am wondering if there is a better answer somewhere that I'm overlooking? Thanks for any information.
I'm not certain about what level of control you are after, especially in the case that you mention you want to insert individual objects. From the sounds of it, cascade merge is exactly what you want for your Entity object tree in the first case for use with the EntityManager.merge. Merge called on an entity will check if it is new or not, and update or insert as appropriate. Marking relationships as cascade merge will allow finding new objects and having them inserted.
The second case though where you want to handle individual insertions, why not exclude the cascade persist option on mappings and just call EntityManager.persist on the objects you want to insert? Persist then will not cascade, so only the entity you call em.persist on will get inserted. Relationships will be used just to set the foreignkey values - though you might want to leave them nulled out and set them later as part of larger merge calls. Both sides of bidirectional relationships need to be maintained, and if the other side is exists and doesn't get merged, its relationship changes are not stored.
If that isn't what you want, EclipseLink has native API on the UnitOfWork (the EntityManager essentially wraps a UnitOfWork for transactional work) that allows you to specify the merge policy. See mergeClone, deepMergeClone and shallowMergeClone on UnitOfWork, which essentially use CASCADE_ALL_PARTS, CASCADE_PRIVATE_PARTS and NO_CASCADE respectively as the merge policies, while the JPA merges use CASCADE_BY_MAPPING.
I'm working on code that allows an object graph to be migrated from one database to another. The object graph represents configuration, we're essentially moving configuration from a staging environment to a production environment.
The graph is fetched from the source DB, detached, serialised, and then merged in to the target DB.
This has been working really well so far, but I have one pickle.
I have a graph that looks like this:
#Entity
class UoObject
{
#Id
private int uoObject;
#OneToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
private Set<UoAttribute> uoAttribute;
// Other irrelevant fields
}
#Entity
class UoAttribute
{
#Id
private int uoAttribute;
#OneToOne(optional=true, fetch=FetchType.EAGER, cascade=CascadeType.ALL)
private UoAttributeObject uoAttributeObject;
}
#Entity
class UoAttributeObject
{
#Id
private UoAttribute uoAttribute;
#ManyToOne
private UoObject uoObject;
}
So here UoObject has a collection of UoAttributes, and these UoAttributes may have a UoAttributeObject which refers to another UoObject.
The UoObjects referred to by UoAttributeObjects are expected to be in the target database already. If they're not, I want to raise an error. For clarity, I never want the target UoObject to have its state updated... essentially I want to only set up the relationship.
When I implemented this, I expected that without a cascade=MERGE on the relationship, JPA would raise an error saying that the object doesn't exist. Or, even, the database layer would end up complaining about foreign keys. Instead I'm finding that JPA attempts to insert a mostly empty UoObject instance (only the key is set). I found this in the JPA 2.0 specification:
If X is an entity merged to X', with a reference to another entity Y, where cascade=MERGE or cascade=ALL is not specified, then navigation of the same association from X' yields a reference to a managed object Y' with the same persistent identity as Y.
It seems that the source DB finds that the target UoObject isn't there, schedules an instance for insertion (bad), but does not merge the state of the source UoObject in (good!).
In the source DB end, is there a way that I can detect that the entity wasn't in the DB already and raise an error? I suspect that lifecycle callbacks #PrePersist and #PreUpdate might help, but I can't see how.
What happens if you add an #Version to the UoObject?
Basically you are merging a corrupt object graph, so the result can be difficult to determine. Usage of locking can help in these situations. Basically you can look at it as you are merging changes from one transaction (your old database), and have inconsistent state (because the object was deleted on the new database in another transaction), so the only way to resolve such concurrency issues is through locking.
It does seem like a bug, in that if the object does not exist, and not cascade merge or cascade persist, it seems invalid and an error should be thrown from the merge. But knowing whether something is a new object, or an existing object that was deleted is very difficult without the usage of locking.
You could always just check the relationship yourself as well. If the UoObject must exist, then do a find for it before the merge, and throw an error if it does not exist, or insert it, or whatever.
I'm using POCO with EF4 and have problem deleting a child object from the parent. Consider the following scenario:
public class Order
{
public List<OrderItem> Items { get; private set; }
}
I need to delete an OrderItem from an Order object like:
myOrder.Items.Remove(0);
I expect EF to keep track the changes and delete the associated OrderItem when I call ObjectContext.SaveChanges().
However, I have realized this is not possible without calling Context.DeleteObject(). I believe this is not a true POCO way as my domain model shouldn't be aware of my persistence infrastructure.
Does anyone have a work around this issue?
Cheers,
Mosh
However, I have realized this is not
possible without calling
Context.DeleteObject(). I believe this
is not a true POCO way as my domain
model shouldn't be aware of my
persistence infrastructure.
I would not follow your interpretation. If you have in-memory collections of orders and order items (ignoring the persistence infrastructure), it's a difference if you only remove an item from the order's item collection or if you also delete the item from the in-memory repository. It might be possible that you want to remove the item only from one order, keep it existing and assign it to another order. (It wouldn't probably make much sense in case of orders and order items, but that's business logic and your POCOs and their relationships cannot know that you want to delete the item from the repository everytime you remove it from an order.)
So, you have to tell explicitely that the item should be deleted from the repository - for an in-memory repository and for a persistence related repository as well. You have to call DeleteObject for the item, there is no workaround to avoid this.
What referential integrity do you have setup in the DB? You have only asked for the orderitem to be removed from the item - not for it to be deleted, and that is what is happening.
I've got a JPA entity "Request", that owns a List of Answers (also JPA entities). Here's how it's defined in Request.java:
#OneToMany(cascade= CascadeType.ALL, mappedBy="request")
private List<Answer> answerList;
And in Answer.java:
#JoinColumn(name = "request", referencedColumnName="id")
#ManyToOne(optional = false)
private Request request;
In the course of program execution, the Request's List of Answers may have Answers added or removed from it, or the actual List object may be replaced. My problem is thus: when I merge a Request to the database, the Answer objects that used to be in the List are kept in the database -- that is, Answer objects that the Request no longer holds a reference to (indirectly, via a List) are not deleted.
This is not the behaviour I desire, as if I merge a Request to the database, and then fetch it again, its Answers List may not be the same. Am I making some programming mistake? Is there an annotation or setting that will ensure that the Answers in the database are exactly the Answers in the Request's List?
A solution is to keep references to the original Answers List and then use the EntityManager to remove each old Answer before merging the Request, but it seems like there should be a cleaner way.
In JPA 1.0, what you want to do can not be done by JPA. Simply, all relationship management must be done by the application, including removing any orphans from collections (which is what you're trying to do).
In JPA 2.0, they do support (though I do not know the details), but depending on your implementation, an upgrade may not be easy or possible.
You can also use something like Hibernate directly, but still use many JPA 1.0 annotations, etc. I can't provide details on that either.
In my case I wrote generic code to handle merging of collections automatically via reflection.
Using EclipseLink and putting #PrivateOwned annotation to answerList would help.
It's illegal to specify additional properties in mapped relation.
You can use #DependentElement annotation in Request instead.
If you use Hibernate as JPA provider, you could add a
#Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
on the answerList. This would work in addition to the JPA Annotations you use there already. JPA 2 (most/all JPA providers do not have a production ready implementation at this time) can do this on its own.