Our java application is using Toplink JPA to connect the data access layer to our SQL Server 2008 database.
We can query the database and get our results without any issue. The problem is that if we try and change the returned entity, it persists to the database as soon as the setter is called.
Query rQuery = em.createNamedQuery("Region.findAll");
Region r= rQuery.getResultList();
r.setActive(active);
From what we've been reading on JPA, it seems like it shouldn't send the changes to the database until persist/merge/flush is called. This is the behavior we're looking to have. We want to be able to make all of our changes and then send them all at once. If we send them one at a time and we get an error, we could end up with partially updated records.
I've tried setting the entity manager flush mode to commit in order to force it to wait for a commit call before persisting and it didn't make any difference.
em.setFlushMode(FlushModeType.COMMIT);
I also tried detaching the returned entity before calling the setters, but it throws an exception.
java.lang.AbstractMethodError: oracle.toplink.essentials.internal.ejb.cmp3.EntityManagerImpl.detach(Ljava/lang/Object;)
This is our first time using Toplink JPA and we don't know what else to try.
If anyone has any other suggestions on how to fix this issue I'd really appreciate it.
If your updating code to the entity is done within a method marked with a transaction then by the end of that method changes will be committed since the transaction must commit any changes before it closes.
Merge is used if the entity is detached ,you made some changes to that entity and you need to re-attach it again and merge the update in the database.
The solution is to make the query for data in a different method with transaction attribute 'Require_new' and pass the result set to the method that do the update with transaction attribute 'required' or 'mandatory'.
Related
We have spring boot project with JPA hibernate. The setup is very common - RestController - Service - Repository.
There is one particular endpoint with Return type of Entity. The service fetches the entity from db, does some calculation and returns it to the client. The problem is, as soon as the service call returns, an update query is fired on this entity in the back end. None of the entity property is changed. Repository.save() also is not called still update query is being fired. I want to understand this behavior and possibly stop it.
If fetch is all i am doing, I don't want the JPA/Hibernate to fire an update query if save() or saveAndFlush() is not called. its resulting unnecessary load on db.
An entity returned from the database is "managed" in the world of hibernate. So when the transaction block / boundaries close hibernate may issue a save back to the db because it thinks that the entity is dirty and needs to be updated with the in memory / hibernate cache info it has.
I do not think that you can skip this as is part of how hibernate works. Even if you see an update being issued I suggest you do not worry.
I am using Spring data for my project, I am using a Standard Repository that extends CRUD Repository.
My code is working as expected, however when I call repository.save() the database is not altered?
Do I also need to then call a commit after this in order to alter the Database? Or should the repository.save() method be altering the database automatically?
When your application runs, the Entity Manager associated with the thread keeps control of modified or added objects, the save() method just do this, it's a mark that says: "this should be saved in the database".
The database DML (insert,update,delete) is not sent to the database while you save things, it's done at the end just before commit, it's delayed to the last moment.
It's possible the send the DML to the database anytime you like using the Entity Manager's flush() method, in fact you can debug the database log and see your queries going through, but this changes to the database will only be visible within your DB connection until the commit is issued; commit() is a method of the transaction associated to the Entity Manager.
In some frameworks like play 1.4.x the commits is issued after the response view is correctly parsed and rendered.
in JPA, if we call EntityTransaction.commit(), does it automatically call EntityManager.flush()? or should we call them both? what is the difference? because i have problem with JPA, when i insert an entity to database, i call persist(). in the database, the data has been inserted (can be fetched), but that data doesn't show up in my app (i fetch it using findAll()). and on another entity, it showed up. is there something i don't know? i'm using standard Spring CRUD, JPA resource_local, and postgresql. sorry for my english, thanks in advance
if we call EntityTransaction.commit(), does it automatically call
EntityManager.flush()?
Yes
what is the difference?
In flush() the changes to the data are reflected in database after encountering flush, but it is still in transaction.flush() MUST be enclosed in a transaction context and you don't have to do it explicitly unless needed (in rare cases), when EntityTransaction.commit() does that for you.
Source
em.flush() - It saves the entity immediately to the database with in a transaction to be used further and it can be rolled back.
em.getTransaction().commit - It marks the end of transaction and saves all the changes with in the transaction into the database and it can't be rolled back.
Refer https://prismoskills.appspot.com/lessons/Hibernate/Chapter_14_-_Flush_vs_Commit.jsp
If you have a #Version annotated column in your entity and call entityManager.flush(), then you will either (immediately!) get an OptimisticLockException, or the database will lock this row (or table). In the later case you can still call setRollbackOnly(), and the lock will later be released without a DB change.
Or from a different perspective, with flush() you can create a (pessimistic) lock on that database row. The others will still see the old entry, but if they try to update they will be blocked, until the lock is released.
All this is also true for CMT (container managed transactions). Instead of waiting for the moment, where the service method is finished and the CMT commit is performed, you can call flush() (even several times) in your service method and handle the OptimisticLockException(s) immediately.
I deleted a record from the database, and the entity manager still references that deleted record.
I have the following query:
List results = em.createNamedQuery("Customers.findNew")
.setParameter("status", "n")
.getResultList();
I am getting back results which include the deleted record. I've read the entity manager caches the database for better performance. This is fine if only the application using the entity manager is accessing the database. But what happens when multiple systems will access the same database?
I have tried:
1. em.flush()
2. em.refresh()
3. em.clear()
right before I use entity manager to query the database, to try and force a re synchronization but none of them do. I am still getting the same record that isn't in the database anymore.
UPDATE
The program I used to delete record, Oracle SQL Developer, didn't commit changes. So JPA was working fine, it was the program I was using to make changes to the database hadn't committed the changes. If you are experiencing similar problem make sure the db admin program committed changes.
EntityManagers cache Objects themselves, for their own use. This is generally save.
In addition, you can enable the 2nd level cache, where inconsistencies can arise between systems. If you enabled this, try to disable it.
Did you delete the entity using the same EntityManager? If no, make sure the transaction to delete the entity is commited and make sure the transaction of your reading EntityManager starts after the deletion is commited.
I have web application using JPA. This entity manager keeps bunch of entites and suddenly I update the database from other side. I use MySQL and I use PhpMyAdmin and change some row.
How to tell entity manager to re-synchronize, e.g. to forgot all the entites in cache?
I know there is refresh(Object) method, but is there any possibility how to do refreshAll() or something what results in this?
It is sure this is expensive operation but if it has to be done.
entityManager.getEntityManagerFactory().getCache().evictAll()
Refresh is something different since it modifies your object. This line will just empty the cache, so if you fetch objects changed outside the entity manager, it will do an actual database query instead of using the outdated cached value.
I had a similar issue and the evictAll() line above worked for me.
Alternatively, the #Cache annotation on the entity class worked too, with the benefit of being able to control caching parameters:
#Cache(coordinationType=CacheCoordinationType.INVALIDATE_CHANGED_OBJECTS)
See: http://wiki.eclipse.org/EclipseLink/Examples/JPA/Caching
If you are using EclipseLink instead of Hibernate the hint is:
em.createNamedQuery("SomeEntity.SomeNamedQuery")
.setHint(QueryHints.REFRESH, true)
.getResultList();
Well, for some people (like me) that tried to add factory.getCache().evictAll(); and doesn't work, and are used JPA + Hibernate, to refresh a query add the hint org.hibernate.cacheMode to IGNORE. Example:
em.createNamedQuery("SomeEntity.SomeNamedQuery")
.setHint("org.hibernate.cacheMode", "IGNORE")
.getResultList();
cache.evictAll is not working for me. So to retrieve data pushed from another app, I peform :
em.getTransaction().begin();
em.getTransaction().commit();
After that, my find query retrieves refreshed data. I don't know if it's very safe solution but it works properly.
When you read an object into an EntityManager, it becomes part of the persistence context, and the same object will remain in the EntityManager until you either clear() it and get a new EntityManager.
So if you update the database, the EntityManager will not see the change unless you call refresh() on the object, or clear() the EntityManager. This has nothing to do with the shared cache (L2) or the persistence context (L1). If you also also using a shared cache, and updating the database directly, then your shared cache will be out of date. You need to refresh() the object, or mark it as invalid to be refreshed the next time it is queried.
Code must follow the way like.
DETACH
REFRESH
MERGE
FLUSH