JPA #Entity not managed after creation? - jpa

I have a simple controller method, where I create a new object Car and then set its name to Audi:
#GetMapping(value = "/resource")
public ResponseEntity visit() {
Car car = carRepo.save(new Car("VolksWagen")); // Car should be managed now?
car.setName("Audi"); // <-- has no effect on database state
return ResponseEntity.ok().build();
}
In the database, it never becomes an Audi, but stays a VolksWagen.
Why does this happen? Shouldn't the newly created Car be in managed state for the persistence context?
Note: It works if I add the #Transactional annotation. I thought it would be enough if OSIV is enabled. What am I misunderstanding about OSIV and #Transactional?

Open Session In View (OSIV) leaves the session open, in order to be able to lazy-load associations when rendering the view. But it doesn't leave the transaction open.
The changes have already been committed, and later changes won't be persisted since later changes are ever flushed nor committed (and since changes are not supposed to happen in the first place)
OSIV is a dirty hack anyway, since the data loaded after the transaction is committed is possibly inconsistent with the data loaded inside the transaction. I would avoid it. See https://vladmihalcea.com/the-open-session-in-view-anti-pattern for more reasons.

carRepo.save do a persist or a merge? if you are using merge pick up the result of the merge!
"Persist takes an entity instance, adds it to the context and makes that instance managed (ie future updates to the entity will be tracked).
Merge creates a new instance of your entity, copies the state from the supplied entity, and makes the new copy managed. The instance you pass in will not be managed (any changes you make will not be part of the transaction - unless you call merge again)."
as described in this answer

Related

How are state changes to JPA entities actually tracked

When I java.persistence.EntityManger.find() an #Entity the EntityManager checks the Transaction for an existing instance of its associated persistence context. If one exists, then
if the entity being searched for is present in the context, then that is what is returned to the caller of EntityManager.find
if the entity being searched for is not present in the context, then EntityManager gets it from the datasource and puts it there, and then that is what is returned to the caller of EntityManager.Find
And if the transaction does not contain an existing instance of the manager's associated persistence context, then the manage creates one, associates it with the transaction, finds the entity in the datasource, and adds it to that context for management, and then returns that entity to the caller of find.
--> the result is the same in that the caller now has a an managed entity that exists in the persistence context. (important: the persistent context is attached to the transaction, so if the transaction has ended at the point at which the client gets hold of the 'managed' entity, well then the persistence context is no more and the entity is 'detached' an no longer managed).
Now, when I make state changes using setters or other other internal state changing methods on my #entity instance, those changes are tracked because my entity is part of persistence context that will get flushed to the datasource when the transaction finally commits. My question is HOW are the state changes tracked and by what? If I were making the changes via some intermediary object, then that intermediary object could update the persistence context accordingly, but I'm not (or am I?). I'm making the changes directly using my #entity annotated object. So how are these changes being tracked.
Perhaps there are events that are being listened for? Listened for by what? I'm reading books and articles on the subject, but I can't pin this one point down.
State changes are tracked by jpa vendor's internal implementation during entity's lifecycle.
Dirty checking strategy is vendor specific. Can be done by fields comparing or bytecode enhancements like posted in JPA dirty checking.
Although it's vendor specific, the PersistentContext will be aware of the state changes during state synchronization, on flush or on commit.
It's important to remember all the points where flushes can be done :
Manually
Before querying
Before commit

What is the point of the Update function in the Repository EF pattern?

I am using the repository pattern within EF using an Update function I found online
public class Repository<T> : IRepository<T> where T : class
{
public virtual void Update(T entity)
{
var entry = this.context.Entry(entity);
this.dbset.Attach(entity);
entry.State = System.Data.Entity.EntityState.Modified;
}
}
I then use it within a DeviceService like so:
public void UpdateDevice(Device device)
{
this.serviceCollection.Update(device);
this.uow.Save();
}
I have realise that what this actually does it update ALL of the device's information rather than just update the property that changed. This means in a multi threaded environment changes can be lost.
After testing I realised I could just change the Device then call uow.Save() which both saved the data and didnt overwrite any existing changes.
So my question really is - What is the point in the Update() function? It appears in almost every Repository pattern I find online yet it seems destructive.
I wouldn't call this generic Update method generally "destructive" but I agree that it has limited use cases that are rarely discussed in those repository implementations. If the method is useful or not depends on the scenario where you want to apply it.
In an "attached scenario" (Windows Forms application for instance) where you load entities from the database, change some properties while they are still attached to the EF context and then save the changes the method is useless because the context will track all changes anyway and know at the end which columns have to be updated or not. You don't need an Update method at all in this scenario (hint: DbSet<T> (which is a generic repository) does not have an Update method for this reason). And in a concurrency situation it is destructive, yes.
However, it is not clear that a "change tracked update" isn't sometimes destructive either. If two users change the same property to different values the change tracked update for both users would save the new column value and the last one wins. If this is OK or not depends on the application and how secure it wants changes to be done. If the application disallows to ever edit an object that is not the last version in the database before the change is saved it cannot allow that the last save wins. It would have to stop, force the user to reload the latest version and take a look at the last values before he enters his changes. To handle this situation concurrency tokens are necessary that would detect that someone else changed the record in the meantime. But those concurrency checks work the same way with change tracked updates or when setting the entity state to Modified. The destructive potential of both methods is stopped by concurrency exceptions. However, setting the state to Modified still produces unnecessary overhead in that it writes unchanged column values to the database.
In a "detached scenario" (Web application for example) the change tracked update is not available. If you don't want to set the whole entity to Modified you have to load the latest version from the database (in a new context), copy the properties that came from the UI and save the changes again. However, this doesn't prevent that changes another user has done in the meantime get overwritten, even if they are changes on different properties. Imagine two users load the same customer entity into a web form at the same time. User 1 edits the customer name and saves. User 2 edits the customer's bank account number and saves a few seconds later. If the entity gets loaded into the new context to perform the update for User 2 EF would just see that the customer name in the database (that already includes the change of User 1) is different from the customer name that User 2 sent back (which is still the old customer name). If you copy the customer name value the property will be marked as Modified and the old name will be written to the database and overwrite the change of User 1. This update would be just as destructive as setting the whole entity state to Modified. In order to avoid this problem you would have to either implement some custom change tracking on client side that recognizes if User 2 changed the customer name and if not it just doesn't copy the value to the loaded entity. Or you would have to work with concurrency tokens again.
You didn't mention the biggest limitation of this Update method in your question - namely that it doesn't update any related entities. For example, if your Device entity had a related Parts collection and you would edit this collection in a detached UI (add/remove/modify items) setting the state of the parent Device to Modified won't save any of those changes to the database. It will only affect the scalar (and complex) properties of the parent Device itself. At the time when I used repos of this kind I named the update method FlatUpdate to indicate that limitation better in the method name. I've never seen a generic "DeepUpdate". Dealing with complex object graphs is always a non-generic thing that has to be written individually per entity type and depending on the situation. (Fortunately a library like GraphDiff can limit the amount of code that has to be written for such graph updates.)
To cut a long story short:
For attached scenarios the Update method is redundant as EFs automatic change tracking does all the necessary work to write correct UPDATE statements to the database - including changes in related object graphs.
For detached scenarios it is a comfortable way to perform updates of simple entities without relationships.
Updating object graphs with parent and child entities in a detached scenario can't be done with such a simplified Update method and requires significantly more (non-generic) work.
Safe concurrency control needs more sophisticated tools, like enabling the optimistic concurrency checks that EF provides and handling the resulting concurrency exceptions in a user-friendly way.
After Slauma's very profound and practical answer I'd like to zoom in on some basic principles.
In this MSDN article there is one important sentence
A repository separates the business logic from the interactions with the underlying data source or Web service.
Simple question. What has the business logic to do with Update?
Fowler defines a repository pattern as
Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.
So as far as the business logic is concerned a repository is just a collection. Collection semantics are about adding and removing objects, or checking whether an object exists. The main operations are Add, Remove, and Contains. Check out the ICollection<T> interface: no Update method there.
It's not the business logic's concern whether objects should be marked as 'modified'. It just modifies objects and relies on other layers to detect and persist changes. Exposing an Update method
makes the business layer responsible for tracking and reporting its changes. Soon all kinds of if constructs will creep in to check whether values have changes or not.
breaks persistence ignorance, because the mere fact that storing updates is something else than storing new objects is a data layer detail.
prevents the data access layer from doing its job properly. Indeed, the implementation you show is destructive. While the Data Access Layer may be perfectly capable of perceiving and persisting granular changes, this method marks a whole object as modified and forces a swiping UPDATE.

EF Code First - How does it know which objects to update?

As in the title, I have a method:
void method(MyDb db, Thread thread, Post post)
{
thread.Title = "changed";
db.SaveChanges();
}
(of course thread item is within MyDb object)
How does it recognize items that need to be updated? I didn't specify anywhere anything like db.Update(thread) or anything like that, it knew what to update without my help. What mechanisms are under it?
When you load entity Thread from database it becomes by default "attached". It means EF internally keep reference to your entity and it also keeps original values of the entity when you loaded it from the database.
When you updated a title there may be two scenarios:
You are using change tracking proxies and EF was notified about your change so it now knows that your instance was modified and it applies those changes to database when you call SaveChanges
You are not using change tracking proxies and when you call SaveChanges EF goes through its internally maintained list of entity references and check if any entity has any property different from original values - all such entities and their modified properties are updated to database during SaveChanges
You can read more about that process here.

Entity Framework - Why explicitly set entity state to modified?

The official documentation says to modify an entity I retrieve a DbEntityEntry object and either work with the property functions or I set its state to modified. It uses the following example
Department dpt = context.Departments.FirstOrDefault();
DbEntityEntry entry = context.Entry(dpt);
entry.State = EntityState.Modified;
I don't understand the purpose of the 2nd and 3rd statement. If I ask the framework for an entity like the 1st statement does and then modify the POCO as in
dpt.Name = "Blah"
If I then ask EF to SaveChanges(), the entity has a status of MODIFIED (I'm guessing via snapshot tracking, this isn't a proxy) and the changes are persisted without the need to manually set the state. Am I missing something here?
In your scenario you indeed don't have to set the state. It is purpose of change tracking to find that you have changed a value on attached entity and put it to modified state. Setting state manually is important in case of detached entities (entities loaded without change tracking or created outside of the current context).
As said, in a scenario with disconnected entities it can be useful to set an entity's state to Modified. It saves a roundtrip to the database if you just attach the disconnected entity, as opposed to fetching the entity from the database and modifying and saving it.
But there can be very good reasons not to set the state to Modified (and I'm sure Ladislav was aware of this, but still I'd like to point them out here).
All fields in the record will be updated, not only the changes. There are many systems in which updates are audited. Updating all fields will either cause large amounts of clutter or require the auditing mechanism to filter out false changes.
Optimistic concurrency. Since all fields are updated, this may cause more conflicts than necessary. If two users update the same records concurrently but not the same fields, there need not be a conflict. But if they always update all fields, the last user will always try to write stale data. This will at best cause an optimistic concurrency exception or in the worst case data loss.
Useless updates. The entity is marked as modified, no matter what. Unchanged entities will also fire an update. This may easily occur if edit windows can be opened to see details and closed by OK.
So it's a fine balance. Reduce roundtrips or reduce redundancy.
Anyway, an alternative to setting the state to Modified is (using DbContext API):
void UpdateDepartment(Department department)
{
var dpt = context.Departments.Find(department.Id);
context.Entry(dpt).CurrentValues.SetValues(department);
context.SaveChanges();
}
CurrentValues.SetValues marks individual properties as Modified.
Or attach a disconnected entity and mark individual properties as Modified manually:
context.Entry(dpt).State = System.Data.Entity.EntityState.Unchanged;
context.Entry(dpt).Property(d => d.Name).IsModified = true;

JPA EntityManager: Why use persist() over merge()?

EntityManager.merge() can insert new objects and update existing ones.
Why would one want to use persist() (which can only create new objects)?
Either way will add an entity to a PersistenceContext, the difference is in what you do with the entity afterwards.
Persist takes an entity instance, adds it to the context and makes that instance managed (i.e. future updates to the entity will be tracked).
Merge returns the managed instance that the state was merged with. It does return something that exists in PersistenceContext or creates a new instance of your entity. In any case, it will copy the state from the supplied entity, and return a managed copy. The instance you pass in will not be managed (any changes you make will not be part of the transaction - unless you call merge again). Though you can use the returned instance (managed one).
Maybe a code example will help.
MyEntity e = new MyEntity();
// scenario 1
// tran starts
em.persist(e);
e.setSomeField(someValue);
// tran ends, and the row for someField is updated in the database
// scenario 2
// tran starts
e = new MyEntity();
em.merge(e);
e.setSomeField(anotherValue);
// tran ends but the row for someField is not updated in the database
// (you made the changes *after* merging)
// scenario 3
// tran starts
e = new MyEntity();
MyEntity e2 = em.merge(e);
e2.setSomeField(anotherValue);
// tran ends and the row for someField is updated
// (the changes were made to e2, not e)
Scenario 1 and 3 are roughly equivalent, but there are some situations where you'd want to use Scenario 2.
Persist and merge are for two different purposes (they aren't alternatives at all).
(edited to expand differences information)
persist:
Insert a new register to the database
Attach the object to the entity manager.
merge:
Find an attached object with the same id and update it.
If exists update and return the already attached object.
If doesn't exist insert the new register to the database.
persist() efficiency:
It could be more efficient for inserting a new register to a database than merge().
It doesn't duplicates the original object.
persist() semantics:
It makes sure that you are inserting and not updating by mistake.
Example:
{
AnyEntity newEntity;
AnyEntity nonAttachedEntity;
AnyEntity attachedEntity;
// Create a new entity and persist it
newEntity = new AnyEntity();
em.persist(newEntity);
// Save 1 to the database at next flush
newEntity.setValue(1);
// Create a new entity with the same Id than the persisted one.
AnyEntity nonAttachedEntity = new AnyEntity();
nonAttachedEntity.setId(newEntity.getId());
// Save 2 to the database at next flush instead of 1!!!
nonAttachedEntity.setValue(2);
attachedEntity = em.merge(nonAttachedEntity);
// This condition returns true
// merge has found the already attached object (newEntity) and returns it.
if(attachedEntity==newEntity) {
System.out.print("They are the same object!");
}
// Set 3 to value
attachedEntity.setValue(3);
// Really, now both are the same object. Prints 3
System.out.println(newEntity.getValue());
// Modify the un attached object has no effect to the entity manager
// nor to the other objects
nonAttachedEntity.setValue(42);
}
This way only exists 1 attached object for any register in the entity manager.
merge() for an entity with an id is something like:
AnyEntity myMerge(AnyEntity entityToSave) {
AnyEntity attached = em.find(AnyEntity.class, entityToSave.getId());
if(attached==null) {
attached = new AnyEntity();
em.persist(attached);
}
BeanUtils.copyProperties(attached, entityToSave);
return attached;
}
Although if connected to MySQL merge() could be as efficient as persist() using a call to INSERT with ON DUPLICATE KEY UPDATE option, JPA is a very high level programming and you can't assume this is going to be the case everywhere.
If you're using the assigned generator, using merge instead of persist can cause a redundant SQL statement, therefore affecting performance.
Also, calling merge for managed entities is also a mistake since managed entities are automatically managed by Hibernate, and their state is synchronized with the database record by the dirty checking mechanism upon flushing the Persistence Context.
To understand how all this works, you should first know that Hibernate shifts the developer mindset from SQL statements to entity state transitions.
Once an entity is actively managed by Hibernate, all changes are going to be automatically propagated to the database.
Hibernate monitors currently attached entities. But for an entity to become managed, it must be in the right entity state.
To understand the JPA state transitions better, you can visualize the following diagram:
Or if you use the Hibernate specific API:
As illustrated by the above diagrams, an entity can be in one of the following four states:
New (Transient)
A newly created object that hasn’t ever been associated with a Hibernate Session (a.k.a Persistence Context) and is not mapped to any database table row is considered to be in the New (Transient) state.
To become persisted we need to either explicitly call the EntityManager#persist method or make use of the transitive persistence mechanism.
Persistent (Managed)
A persistent entity has been associated with a database table row and it’s being managed by the currently running Persistence Context. Any change made to such an entity is going to be detected and propagated to the database (during the Session flush-time).
With Hibernate, we no longer have to execute INSERT/UPDATE/DELETE statements. Hibernate employs a transactional write-behind working style and changes are synchronized at the very last responsible moment, during the current Session flush-time.
Detached
Once the currently running Persistence Context is closed all the previously managed entities become detached. Successive changes will no longer be tracked and no automatic database synchronization is going to happen.
To associate a detached entity to an active Hibernate Session, you can choose one of the following options:
Reattaching
Hibernate (but not JPA 2.1) supports reattaching through the Session#update method.
A Hibernate Session can only associate one Entity object for a given database row. This is because the Persistence Context acts as an in-memory cache (first level cache) and only one value (entity) is associated with a given key (entity type and database identifier).
An entity can be reattached only if there is no other JVM object (matching the same database row) already associated with the current Hibernate Session.
Merging
The merge is going to copy the detached entity state (source) to a managed entity instance (destination). If the merging entity has no equivalent in the current Session, one will be fetched from the database.
The detached object instance will continue to remain detached even after the merge operation.
Remove
Although JPA demands that managed entities only are allowed to be removed, Hibernate can also delete detached entities (but only through a Session#delete method call).
A removed entity is only scheduled for deletion and the actual database DELETE statement will be executed during Session flush-time.
I noticed that when I used em.merge, I got a SELECT statement for every INSERT, even when there was no field that JPA was generating for me--the primary key field was a UUID that I set myself. I switched to em.persist(myEntityObject) and got just INSERT statements then.
The JPA specification says the following about persist().
If X is a detached object, the EntityExistsException may be thrown when the persist
operation is invoked, or the EntityExistsException or another PersistenceException may be thrown at flush or commit time.
So using persist() would be suitable when the object ought not to be a detached object. You might prefer to have the code throw the PersistenceException so it fails fast.
Although the specification is unclear, persist() might set the #GeneratedValue #Id for an object. merge() however must have an object with the #Id already generated.
Some more details about merge which will help you to use merge over persist:
Returning a managed instance other than the original entity is a critical part of the merge
process. If an entity instance with the same identifier already exists in the persistence context, the
provider will overwrite its state with the state of the entity that is being merged, but the managed
version that existed already must be returned to the client so that it can be used. If the provider did not
update the Employee instance in the persistence context, any references to that instance will become
inconsistent with the new state being merged in.
When merge() is invoked on a new entity, it behaves similarly to the persist() operation. It adds
the entity to the persistence context, but instead of adding the original entity instance, it creates a new
copy and manages that instance instead. The copy that is created by the merge() operation is persisted
as if the persist() method were invoked on it.
In the presence of relationships, the merge() operation will attempt to update the managed entity
to point to managed versions of the entities referenced by the detached entity. If the entity has a
relationship to an object that has no persistent identity, the outcome of the merge operation is
undefined. Some providers might allow the managed copy to point to the non-persistent object,
whereas others might throw an exception immediately. The merge() operation can be optionally
cascaded in these cases to prevent an exception from occurring. We will cover cascading of the merge()
operation later in this section. If an entity being merged points to a removed entity, an
IllegalArgumentException exception will be thrown.
Lazy-loading relationships are a special case in the merge operation. If a lazy-loading
relationship was not triggered on an entity before it became detached, that relationship will be
ignored when the entity is merged. If the relationship was triggered while managed and then set to null while the entity was detached, the managed version of the entity will likewise have the relationship cleared during the merge."
All of the above information was taken from "Pro JPA 2 Mastering the Java™ Persistence API" by Mike Keith and Merrick Schnicariol. Chapter 6. Section detachment and merging. This book is actually a second book devoted to JPA by authors. This new book has many new information then former one. I really recommed to read this book for ones who will be seriously involved with JPA. I am sorry for anonimously posting my first answer.
There are some more differences between merge and persist (I will enumerate again those already posted here):
D1. merge does not make the passed entity managed, but rather returns another instance that is managed. persist on the other side will make the passed entity managed:
//MERGE: passedEntity remains unmanaged, but newEntity will be managed
Entity newEntity = em.merge(passedEntity);
//PERSIST: passedEntity will be managed after this
em.persist(passedEntity);
D2. If you remove an entity and then decide to persist the entity back, you may do that only with persist(), because merge will throw an IllegalArgumentException.
D3. If you decided to take care manually of your IDs (e.g by using UUIDs), then a merge
operation will trigger subsequent SELECT queries in order to look for existent entities with that ID, while persist may not need those queries.
D4. There are cases when you simply do not trust the code that calls your code, and in order to make sure that no data is updated, but rather is inserted, you must use persist.
JPA is indisputably a great simplification in the domain of enterprise
applications built on the Java platform. As a developer who had to
cope up with the intricacies of the old entity beans in J2EE I see the
inclusion of JPA among the Java EE specifications as a big leap
forward. However, while delving deeper into the JPA details I find
things that are not so easy. In this article I deal with comparison of
the EntityManager’s merge and persist methods whose overlapping
behavior may cause confusion not only to a newbie. Furthermore I
propose a generalization that sees both methods as special cases of a
more general method combine.
Persisting entities
In contrast to the merge method the persist method is pretty straightforward and intuitive. The most common scenario of the persist method's usage can be summed up as follows:
"A newly created instance of the entity class is passed to the persist method. After this method returns, the entity is managed and planned for insertion into the database. It may happen at or before the transaction commits or when the flush method is called. If the entity references another entity through a relationship marked with the PERSIST cascade strategy this procedure is applied to it also."
The specification goes more into details, however, remembering them is not crucial as these details cover more or less exotic situations only.
Merging entities
In comparison to persist, the description of the merge's behavior is not so simple. There is no main scenario, as it is in the case of persist, and a programmer must remember all scenarios in order to write a correct code. It seems to me that the JPA designers wanted to have some method whose primary concern would be handling detached entities (as the opposite to the persist method that deals with newly created entities primarily.) The merge method's major task is to transfer the state from an unmanaged entity (passed as the argument) to its managed counterpart within the persistence context. This task, however, divides further into several scenarios which worsen the intelligibility of the overall method's behavior.
Instead of repeating paragraphs from the JPA specification I have prepared a flow diagram that schematically depicts the behaviour of the merge method:
So, when should I use persist and when merge?
persist
You want the method always creates a new entity and never updates an entity. Otherwise, the method throws an exception as a consequence of primary key uniqueness violation.
Batch processes, handling entities in a stateful manner (see Gateway pattern).
Performance optimization
merge
You want the method either inserts or updates an entity in the database.
You want to handle entities in a stateless manner (data transfer objects in services)
You want to insert a new entity that may have a reference to another entity that may but may not be created yet (relationship must be marked MERGE). For example, inserting a new photo with a reference to either a new or a preexisting album.
I was getting lazyLoading exceptions on my entity because I was trying to access a lazy loaded collection that was in session.
What I would do was in a separate request, retrieve the entity from session and then try to access a collection in my jsp page which was problematic.
To alleviate this, I updated the same entity in my controller and passed it to my jsp, although I imagine when I re-saved in session that it will also be accessible though SessionScope and not throw a LazyLoadingException, a modification of example 2:
The following has worked for me:
// scenario 2 MY WAY
// tran starts
e = new MyEntity();
e = em.merge(e); // re-assign to the same entity "e"
//access e from jsp and it will work dandy!!
I found this explanation from the Hibernate docs enlightening, because they contain a use case:
The usage and semantics of merge() seems to be confusing for new users. Firstly, as long as you are not trying to use object state loaded in one entity manager in another new entity manager, you should not need to use merge() at all. Some whole applications will never use this method.
Usually merge() is used in the following scenario:
The application loads an object in the first entity manager
the object is passed up to the presentation layer
some modifications are made to the object
the object is passed back down to the business logic layer
the application persists these modifications by calling merge() in a second entity manager
Here is the exact semantic of merge():
if there is a managed instance with the same identifier currently associated with the persistence context, copy the state of the given object onto the managed instance
if there is no managed instance currently associated with the persistence context, try to load it from the database, or create a new managed instance
the managed instance is returned
the given instance does not become associated with the persistence context, it remains detached and is usually discarded
From: http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/objectstate.html
Going through the answers there are some details missing regarding `Cascade' and id generation. See question
Also, it is worth mentioning that you can have separate Cascade annotations for merging and persisting: Cascade.MERGE and Cascade.PERSIST which will be treated according to the used method.
The spec is your friend ;)
Scenario X:
Table:Spitter (One) ,Table: Spittles (Many) (Spittles is Owner of the relationship with a FK:spitter_id)
This scenario results in saving : The Spitter and both Spittles as if owned by Same Spitter.
Spitter spitter=new Spitter();
Spittle spittle3=new Spittle();
spitter.setUsername("George");
spitter.setPassword("test1234");
spittle3.setSpittle("I love java 2");
spittle3.setSpitter(spitter);
dao.addSpittle(spittle3); // <--persist
Spittle spittle=new Spittle();
spittle.setSpittle("I love java");
spittle.setSpitter(spitter);
dao.saveSpittle(spittle); //<-- merge!!
Scenario Y:
This will save the Spitter, will save the 2 Spittles But they will not reference the same Spitter!
Spitter spitter=new Spitter();
Spittle spittle3=new Spittle();
spitter.setUsername("George");
spitter.setPassword("test1234");
spittle3.setSpittle("I love java 2");
spittle3.setSpitter(spitter);
dao.save(spittle3); // <--merge!!
Spittle spittle=new Spittle();
spittle.setSpittle("I love java");
spittle.setSpitter(spitter);
dao.saveSpittle(spittle); //<-- merge!!
Another observation:
merge() will only care about an auto-generated id(tested on IDENTITY and SEQUENCE) when a record with such an id already exists in your table. In that case merge() will try to update the record.
If, however, an id is absent or is not matching any existing records, merge() will completely ignore it and ask a db to allocate a new one. This is sometimes a source of a lot of bugs. Do not use merge() to force an id for a new record.
persist() on the other hand will never let you even pass an id to it. It will fail immediately. In my case, it's:
Caused by: org.hibernate.PersistentObjectException: detached entity
passed to persist
hibernate-jpa javadoc has a hint:
Throws: javax.persistence.EntityExistsException - if the entity
already exists. (If the entity already exists, the
EntityExistsException may be thrown when the persist operation is
invoked, or the EntityExistsException or another PersistenceException
may be thrown at flush or commit time.)
You may have come here for advice on when to use persist and when to use merge. I think that it depends the situation: how likely is it that you need to create a new record and how hard is it to retrieve persisted data.
Let's presume you can use a natural key/identifier.
Data needs to be persisted, but once in a while a record exists and an update is called for. In this case you could try a persist and if it throws an EntityExistsException, you look it up and combine the data:
try { entityManager.persist(entity) }
catch(EntityExistsException exception) { /* retrieve and merge */ }
Persisted data needs to be updated, but once in a while there is no record for the data yet. In this case you look it up, and do a persist if the entity is missing:
entity = entityManager.find(key);
if (entity == null) { entityManager.persist(entity); }
else { /* merge */ }
If you don't have natural key/identifier, you'll have a harder time to figure out whether the entity exist or not, or how to look it up.
The merges can be dealt with in two ways, too:
If the changes are usually small, apply them to the managed entity.
If changes are common, copy the ID from the persisted entity, as well as unaltered data. Then call EntityManager::merge() to replace the old content.
persist(entity) should be used with totally new entities, to add them to DB (if entity already exists in DB there will be EntityExistsException throw).
merge(entity) should be used, to put entity back to persistence context if the entity was detached and was changed.
Probably persist is generating INSERT sql statement and merge UPDATE sql statement (but i'm not sure).
Merge won't update a passed entity, unless this entity is managed. Even if entity ID is set to an existing DB record, a new record will be created in a database.