Does calling EntityManager.remove(someEntity) -for other than transaction scoped entity manager- cause the entity to be detached? As I was reading the JSR-317 :
A detached entity results from transaction commit if a
transaction-scoped container-managed entity manager is used (see
section 3.3); from transaction rollback (see section 3.3.2); from
detaching the entity from the persistence context; from clearing the
persistence context; from closing an entity manager; or from
serializing an entity or otherwise passing an entity by value—e.g., to
a separate application tier, through a remote interface, etc.
It didn't mention that remove() causes the entity to be detached, though other web sites mentions it. please provide a reference to your answer.
According to the JPA 2.1 specification (Section 3.2.3 Removal, PDF page 82), we find an important piece of information, related to your question:
After an entity has been removed, its state (except for generated state) will be that of the entity at the point at which the remove operation was called.
So, the answer seems to be:
It depends.
Yet, on page 80, we find another important bit of information.
A removed entity instance is an instance with a persistent identity, associated with a persistence context, that will be removed from the database upon transaction commit.
Thus, we can infer that it is not in a detached state as it is still "associated" until finally removed by the TX commit.
Digging deeper, you can consult the JPA lifecycle image provided in the answer by Vlad Mihalcea to "understand the JPA state transitions better". As depicted in the first image (JPA), an entity does not transit to the detached state, but ends up in a removed state.
Further details on the semantics of the remove(..) operation are found on page 81/82 of the JPA Spec document.
Hope it helps.
Related
What is the meaning of detached entity. In my course following in Spring Framework Guru. We made a DTO, Does it mean when you fetched data from db then convert it in DTO is it called Detached Entity?
A detached entity is an entity which state must not be reflected by the JPA provider.
In other words, if you change its state (i.e. through setters methods) these changes will not be saved to the underlying database, as the JPA provider doesn't have to "observe" such entities.
If entity E1 is a managed entity you can make it detached invoking (very reasonable named) method EntityManager#detach(E1). You can also use EntityManager#clear() which will clear whole PersistenceContext and effectively making all managed entities detached.
Some time ago I wrote a blog article on this general subject that helps to answer this question, and I post it here as a separate answer only because it comes at the subject from a slightly different perspective.
I'm using JPA EclipseLink 2.0 with Glassfish 3.1.2.2
I want to know if after I call
em.flush()
em.clear()
The objects are immediatly commited to the database. My problem is I'm doing so many transactions that I'm getting OutOfMemory. I want to avoid this by flushing the transaction's objects.
After I flush and clear, I can't see any immediate entity commited to the database, I can only see them AFTER the whole process is done, which tells me this isn't actually commiting.
If flush and clear doesn't commit:
1) What does it actually do?
2) Why am I no longer getting OutOfMemory?
Please tell me if I'm right:
The objects that were allocated in my RAM are sent to the database, but changes are not yet commited. This only means I cleared my RAM, the objects are now in the DB server but the transaction is not yet commited.
Entities are synchronized to the connected database at transaction commit time. If you only have n = 1 ongoing transaction (here: JTA/container managed), changes on one or more entities get written to the DB the moment you call flush() on the EntityManager instance.
However, changes become "visible" only after the transaction has been properly executed by the container (here: Glassfish) which is responsible for transaction handling. For reference, see. section 7.6.1 (p. 294) of JPA Spec 2.0 which defines:
A new persistence context begins when the container-managed entity manager is invoked (Specifically, when one of the methods of the EntityManager interface is invoked) in the scope of an active JTA transaction, and there is no current persistence context already associated with the JTA transaction. The persistence context is created and then associated with the JTA transaction.
The persistence context ends when the associated JTA transaction commits or rolls back, and all entities that were managed by the EntityManager become detached.
In section 3.2.4 (Synchronization to the Database) of the JPA Spec 2.0 we find:
The state of persistent entities is synchronized to the database at transaction commit.
[..]
The persistence provider runtime is permitted to perform synchronization to the database at other times as well when a transaction is active. The flush method can be used by the application to force synchronization.
It applies to entities associated with the persistence context. The EntityManager and Query setFlushMode methods can be used to control synchronization semantics. The effect of FlushModeType.AUTO is defined in section 3.8.7. If FlushModeType.COMMIT is specified, flushing will occur at transaction commit; the persistence provider is permitted, but not required, to perform to flush at other times. If there is no transaction active, the persistence provider must not flush to the database.
Most likely in your scenario, the container (Glassfish) and/or your application is configured for FlushModeType.COMMIT(*1). In case FlushModeType.AUTO is in place, it is up to the Persistence Provider (EclipseLink) which "is responsible for ensuring that all updates to the state of all entities in the persistence context which could potentially affect the result of the query are visible to the processing of the query." (Section 3.8.7, p. 122)
By contrast, the clear() method does NOT commit anything by itself. It simply detaches all managed entities from the current persistence context, thus causing any changes on entities which have not been flushed (committed) to get lost. For reference, see p. 70 of the linked JPA Spec.
With respect to the OutOfMemoryError, it's hard to tell what's causing this under which circumstances, as you did not provide much detail either. However, I would:
read the aforementioned sections of the JPA specification
check how your environment is configured and
reevaluate how your application is written/implemented, potentially making false assumptions on the transaction handling of the container it is running in.
Related to 2., you might check your persistence.xml whether it configures
<property name="eclipselink.persistence-context.flush-mode" value="COMMIT" />
and change it to AUTO to see if there is any difference.
Hope it helps.
Footnotes
*1: But that's a good guess, as you did not provide that much detail on your setup/environment.
On JPA transaction commit, JPA is doing flush automatically. You should see object in DB right after first transaction end, not only after whole process end. Check if really do more transactions or just one.
I'm using EF (EF Core, actually, with ASP.NET Core on OSX, but I believe this is more of a general "newbie-style" EF question, so please read on...)
I built a little logging routine that uses EF to publish log entries to my database. Sort of like this, called from a repository class:
WebLog log = new WebLog(source, path, message);
Context.WebLogs.Add(log);
Context.SaveChanges();
Where WebLog is a simple model class, Context.WebLogs is a DbSet<WebLog> collection, and Context is obviously the DbContext. I believe this is quite straightforward.
But my question is this: if I continue to add new log entries to the Context.WebLogs collection and I never do anything like reboot my server, isn't the collection just going to grow without bounds? Is there some kind of "purge" or "flush" action I can take periodically to manage memory usage (without affecting the committed rows in the database, of course--I want those to persist). Or is DbSet some sort of a special collection that won't do this?
As mentioned by DevilSuichiro above, the recommended approach is to limit the lifetime of the instances of DbContext. E.g. in a Web application you typically use a DbContext instance per request, so an unbounded number of entities added doesn't become a problem.
The closest thing to a "flush" operation is SaveChanges() that method will not try to remove references to tracked entities, as DbContext is designed to be reused after SaveChanges().
In previous versions of EF we had a Detach() API that you could use to get rid of an individual tracked reference but we don't have that API in DbContext or anywhere in EF Core.
BTW, having an instance of DbContext that is shared between multiple requests is extremely problematic because DbContext is not thread safe.
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
I have a JPA entity that links to others -- something like this:
#Entity
class LinkRec implements Serializable {
...
#OneToOne
private OtherEntity otherTable;
...
}
So my logic eventually can delete this entity (calling the EntityManger.remove method), then I want to write to a log file what was done, including reference members of the otherTable object. Is this a permitted operation in JPA?
Is this a permitted operation in JPA?
Yes.
What JPA (underlying JPA provider) does when you invoke remove is just "mark" that the instance is expected to be deleted/removed. But even if the transaction is committed (and the instance deleted from the database) or not, the instance object remains the same. Any changes on its attributes depend on what you do.
Due to you mark the entity as removed you won't can refresh the instance's state from the database (call EntityManager.refersh method). You will get an IllegalArgumentException.
Be aware that, in other cases, you could screw up if you refresh the entity before loggin what you want.
I quote a text from the JPA specification (see Synchronization to the Database section) that could help you to understand the "JPA" behaivor
Synchronization to the database does not involve a refresh of any managed entities unless the refresh operation is explicitly invoked on those entities or cascaded to them as a result of the specification of the cascade=REFRESH or cascade=ALL annotation element value
The relevant line in the spec is:
After an entity has been removed, its state (except for generated state) will be that of the entity at the point at which the remove operation was called.
Since this is all I can find on the subject in the spec, I would say that it could vary from implementation to implementation. In my opinion, this makes what you are tying to do dangerous. It may work in one JPA implementation and not another, or work in one version and not in an upgrade.
If I had to guess on implementations, I would say that #OneToOne objects will probably work okay. Where I would worry is with things like #OneToMany. In the case of Hibernate for example: this collection may be hydrated and in memory, but it may also point to a proxy. If it is a proxy and you call the getter it will check with the database for the collection and fail to load it because the object is gone.