Kotlin immutable entities changing unexpectedly when using it with JPA - jpa

In our project we are using kotlin with JPA. All of our entities are immutable so, it is not possible to set fields of our entities directly. You have to create a new instance by using the copy method. If you want these changes to be reflected to database, you must persist this newly created entity with an explicit function call.
In the beginning, this approach looks perfect to us. However, nowadays we are having some problems like some of our instances are changing unexpectedly in the memory.
val instance1 = repository.findById(entityId)
repository.save(instance1.copy(deletedAt = Instant.now()))
..
..
assertNull(instance1.deletedAt())
In the code snipped above, instance1 is retrieved from database and its deletedAt field is set with copy method and the new instance which is created with this copy method is passed to save method of the repository. We don't set any field of instance1, we create a new instance to do these changes. However, the result on assert line is unexpectedly not-null.
It seems, There is a confliction on JPA persistence context (first level cache) and kotlin's immutable and copy method logic.
Is anyone facing this problem or any suggestion or best practices when using JPA and immutable Kotlin entities?

I suspect the problem is that you're ignoring the return value from save().  Its docs say:
Saves a given entity. Use the returned instance for further operations as the save operation might have changed the entity instance completely.
But you're not doing that; you're instead continuing to use the original instance which (as that says) may have changed.
Instead, store the return value from save(), and use that thereafter.  (Either by making instance1 a var, or creating a new val and not referring to instance1 afterward.)
(This isn't a Kotlin-specific problem, and is exactly the same in Java.  JPA , Spring, &c work their magic by futzing with the bytecode, so can do things your code can't — such as changing immutable values.  Most of the time you can ignore it, but this case makes it obvious.)

Immutable types are not compatible on how JPA works.
JPA works around the concept of UnitOfWork, which mean objects retrieved from the database lives in a PersistedContext (1st level cache) and they get discarded once the EntityManager is closed (on a web application at the end of the HTTP request).
When using the copy method in an entity you just retrieved from the database, the copied object is considered detached from the current session meaning that changes on it cannot be tracked by JPA and the underlying implememtation (Hibernate / EclipseLink) have hard time figuring out which SQL statement needs to be fired (Insert/Update/Delete ????)
Things got way more complex when you have complex object graph with OneToMany associations and cascading options.
So my recommendation is unfortunately is to avoid Immutable types when using JPA.

Related

How to properly use EFCore with SignalR Core (avoid caching entities)

I just found some really strange behaviour which turns out it is not so strange at all.
My select statement (query from database) worked only the first time. The second time, query from database was cached.
Inside Hub method I read something from database every 10 seconds and return result to all connected clients. But if some API change this data, Hub context does not read actual data.
In this thread I found this:
When you use EF it by default loads each entity only once per context. The first query creates entity instance and stores it internally. Any subsequent query which requires entity with the same key returns this stored instance. If values in the data store changed you still receive the entity with values from the initial query. This is called Identity map pattern. You can force the object context to reload the entity but it will reload a single shared instance.
So my question is how to properly use EFCore inside SignalR Core hub method?
I could use AsNoTracking, but I would like to use some global setting. Developer can easily forget to add AsNoTracking and this could mean serving outdated data to user.
I would like to write some code in my BaseHub class which will tell context do not track data. If I change entity properties, SaveChanges should update data. Can this be achieved? It is hard to think all the time to add AsNoTracking when querying from hub method.
I would like to write some code in my BaseHub class which will tell context do not track data.
The default query tracking behavior is controlled by the ChangeTracker.QueryTrackingBehavior property with default value of TrackAll (i.e. tracking).
You can change it to NoTracking and then use AsTracking() for queries that need tracking. It's a matter of which are more commonly needed.
If I change entity properties, SaveChanges should update data.
This is not possible if the entity is not tracked.
If you actually want tracking queries with "database wins" strategy, I'm afraid it's not possible currently in EF Core. I think EF6 object context services had an option for specifying the "client wins" vs "database wins" strategy, but EF Core currently does not provide such control and always implements "client wins" strategy.

How to get a detached object from JPA

In my application I need most objects fetched in detached mode (fetched with the find API).
I'm wondering if there is a way to ask a detached object from the JPA provider and save the extra call to detach() API.
In additional I would expect the object created in such mode to be less expensive since the JPA provider doesn't need to add it to the entity manager context.
Is there a way to achieve this with JPA APIs?
Is there a way to achieve such functionality with query results?
Specifically I'm using Eclipse Link so if there is a specific way to do it with this implementation it will be helpful as well.
You can fetch a detached entity without an extra call to detach() if you fetch it outside a transaction. If you are not using container-managed transactions, it's trivial, simply do not start a transaction.
If you are using CMT, you have to make sure the requesting object is not a transaction-enabled EJB:
if in an EJB, suspend the transaction by annotating the appropriate method with:#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED),
or
call the EntityManager from a POJO. You dont have to call it directly, it only impotrant that the query result will end in a non-EJB object.
AFAIK, there is no performance gain to be expected, since the query result will always be put in the current persistence context, however shortlived it may be.
EDIT: There is another possibility to get detached objects which does not depend on transaction demarcations: JPA constructor expressions:
List<DTO> dtos = em.createQuery("SELECT NEW com.example.DTO( o.title, o.version) FROM Entity o").getResultList();
The constructed type must have a constructor with all the relevant attributes. The objects in the list, entities or not, will always be created detached. However there is a small overhead of instantiating a new object.

copy records from between two databases using EF

I need to copy data from one database to another with EF. E.g. I have the following table relations: Forms->FormVersions->FormLayouts... We have different forms in both databases and we want to collect them to one DB. Basically I want to load Form object recursively from one DB and save it to another DB with all his references. Also I need to change IDs of the object and related objects if there are exists objects with the same ID in the second database.
Until now I have following code:
Form form = null;
using (var context = new FormEntities())
{
form = (from f in context.Forms
join fv in context.FormVersions on f.ID equals fv.FormID
where f.ID == 56
select f).First();
}
var context1 = new FormEntities("name=FormEntities1");
context1.AddObject("Forms", form);
context1.SaveChanges();
I'm receiving the error: "The EntityKey property can only be set when the current value of the property is null."
Can you help with implementation?
The simplest solution would be create copy of your Form (new object) and add that new object. Otherwise you can try:
Call context.Detach(form)
Set form's EntityKey to null
Call context1.AddObject(form)
I would first second E.J.'s answer. Assuming though that you are going to use Entity Framework, one of the main problem areas that you will face is relationship management. Your code should use the Include method to ensure that related objects are included in the results of a select operation. The join that you have will not have this effect.
http://msdn.microsoft.com/en-us/library/bb738708.aspx
Further, detaching an object will not automatically detach the related objects. You can detach them in the same way however the problem here is that as each object is detached, the relationships that it held to other objects within the context are broken.
Manually restoring the relationships may be an option for you however it may be worthwhile looking at EntityGraph. This framework allows you to define object graphs and then perform operations such as detach upon them. The entire graph is detached in a single operation with its relationships intact.
My experience with this framework has been in relation to RIA Services and Silverlight however I believe that these operations are also supported in .Net.
http://riaservicescontrib.codeplex.com/wikipage?title=EntityGraphs
Edit1: I just checked the EntityGraph docs and see that DetachEntityGraph is in the RIA specific layer which unfortunately rules it out as an option for you.
Edit2: Alex Jame's answer to the following question is a solution to your problem. Don't load the objects into the context to begin with - use the notracking option. That way you don't need to detach them which is what causes the problem.
Entity Framework - Detach and keep related object graph
If you are only doing a few records, Ladislav's suggestion will probably work, but if you are moving lots of data, you should/could consider doing this move in a stored procedure. The entire operation can be done at the server, with no need to move objects from the db server, to your front end and then back again. A single SP call would do it all.
The performance will be a lot better which may or may not not matter in your case.

How can I create a detached clone of an EF codefirst class for testing purposes?

I want to create an integration test which grabs an EF entity from the DB, clones it to a detached object, modifies it and then saves it back and compares it again to the original.
However, I was using AutoMapper to create the clone of the class, but it turns out this is also tracked or an alias to the original object. I need it to be completely detached from EF, and am able to do this outside of my repository class (i.e. not using any EF detach methods).
The reason for doing this is my EF class contains nested collections of other classes and EF doesn't handle persisting the whole object tree. Hence, my Update() method in my repository class handles this and I want my NUnit test to test this code. I want the test is to be able to quickly create a copy of my original class without EF tracking it.
Creating a cloned object containing current, original, or database
values The DbPropertyValues object returned from CurrentValues,
OriginalValues, or GetDatabaseValues can be used to create a clone of
the entity. This clone will contain the property values from the
DbPropertyValues object used to create it. For example:
using (var context = new UnicornsContext())
{
var unicorn = context.Unicorns.Find(1);
var clonedUnicorn = context.Entry(unicorn).GetDatabaseValues().ToObject();
}
Note that the object returned is not the entity and is not being
tracked by the context. The returned object also does not have any
relationships set to other objects.
The cloned object can be useful for resolving issues related to
concurrent updates to the database, especially where a UI that
involves data binding to objects of a certain type is being used. (See
Part 9 for more details on dealing with optimistic concurrency.)
*From http://blogs.msdn.com/b/adonet/archive/2011/01/30/using-dbcontext-in-ef-feature-ctp5-part-5-working-with-property-values.aspx
Hope it can helps others
All troubles are gone once you're using EF 5+ where they introduced AsNoTracking() method.
The line below returns an unlinked instance, so all the context won't be aware about any changes in that instance:
context.Clients.AsNoTracking().FirstOrDefault(item => item.Id == id);
If Clients has a reference to Address and you want an unlinked instance of it too, just use an Include:
context.Clients
.Include("Address").AsNoTracking()
.FirstOrDefault(item => item.Id == id);
If it is a test you can do anything and you don't have to be binded to any architectural approach like repository. Your repository probably receive context as injection so you can have access to it. Another point is that I don't believe that AutoMapper will create tracked entity.
The one way to make a copy of the class is using serialization which by default saves only public fields (Xml serialization or DataContract serialization). Serialize the object and deserialize it back to a new instance. Serialization will save the whole object graph and deserialized object graph will be detached. Just be aware that that those serializations don't likes cyclic references in object graph (navigation property from A to B and from B to A from cycles). Serialization is also too much aggresive so it can traverse the graph more deeply then you want - this can be especially dangerous in many to many relations.
The best approach is using either ICloneable interface and implement Clone or define support methods which will do different clones with required depth.
Here is another approach for clonning EntityObject based entities. It is tough code, especially part with Reflection.Emit. But this will not help you because code-first is using POCOs.

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.