I am facing a weird problem with Spring Boot(2.3.7) + PostgreSQL v12 (row level security) + Hibernate (5.x).
Here are the steps that I am executing
A procedure accepts an input variable and creates temporary table. The variable is then inserted in temporary table.
Spring Advice which executes for all #Service annotation and invokes a procedure with a variable (call it custom_id).
#Transactional attribute is specified on all #Service classes.
PostgreSQL row level security has been enabled on the tables being queried and updated.
Row level security applies filter based on the variable stored (custom_id value) in temporary table.
All update, select, insert operations are executed using custom implementation of JpaRepository (interface based)
This works fine as long as there are only select operation performed on the database. But starts to fail with code having a combination of select and updates. The code simply fails with a message as it is not able to locate the temporary table.
I enabled trace for Spring transaction and found that there are few statements like
No need to create transaction for XXX
While code that performs update operation has statements like
Getting transaction for XXX
After searching for a while, I realised that SimpleJpaRepository has #Transaction with readonly flag set to true. This results in SELECT operation getting executing in transaction less mode.
Procedure
create or replace procedure proc_context(dummy_id uuid) AS $context_var$
declare
begin
create temp table if not exists context_metadata
(
dummy_id uuid
)
on commit drop;
insert into context_metadata values(dummy_id);
end;
$context_var$ LANGUAGE plpgsql;
ERROR
Following error is logged in console
ERROR: relation "context_metadata" does not exist
What I tried
Tried implementing custom transaction manager and explicitly invoking the procedure to set the temporary variable value (Didn't work). Refer below
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
super.prepareSynchronization(status, definition);
if (status.isNewTransaction() || status.isReadOnly() || status.isNewSynchronization()) {
UUID someID = ....;
Query query = entityManager.createNativeQuery("CALL proc_context(?);");
query.setParameter(1, someID);
query.executeUpdate();
}
}
Tried setting #Transactional notation with readonly set to false on all repositories.
What I am looking for?
Unfortunately due to this behaviour, the row-level security implementation is not working in my code. Is there any way to disable read-only transactions using a global property OR provide me with any hint to overcome this problem?
Finally, I could figure out after 2 days of battle. The problem was multi-faceted.
I noticed hibernate.transaction.flush_before_completion property set to true in application.properties file. I had to remove that property.
Developer had written a very messy code to update the entity attributes (Was performing select, then creating new instance, populating attributes and then calling save method). All this ruckus to update one single attribute.
Tested the code and everything worked fine.
This question already has answers here:
Spring Data JPA Update #Query not updating?
(5 answers)
Closed 2 years ago.
The community reviewed whether to reopen this question 1 year ago and left it closed:
Original close reason(s) were not resolved
Let's suppose to have this situation:
We have Spring Data configured in the standard way, there is a Respository object, an Entity object and all works well.
Now for some complex motivations I have to use EntityManager (or JdbcTemplate, whatever is at a lower level than Spring Data) directly to update the table associated to my Entity, with a native SQL query. So, I'm not using Entity object, but simply doing a database update manually on the table I use as entity (it's more correct to say the table from which I get values, see next rows).
The reason is that I had to bind my spring-data Entity to a MySQL view that makes UNION of multiple tables, not directly to the table I need to update.
What happens is:
In a functional test, I call the "manual" update method (on table from which the MySQL view is created) as previously described (through entity-manager) and if I make a simple Respository.findOne(objectId), I get the old object (not updated one). I have to call Entitymanager.refresh(object) to get the updated object.
Why?
Is there a way to "synchronize" (out of the box) objects (or force some refresh) in spring-data? Or am I asking for a miracle?
I'm not ironical, but maybe I'm not so expert, maybe (or probably) is my ignorance. If so please explain me why and (if you want) share some advanced knowledge about this amazing framework.
If I make a simple Respository.findOne(objectId) I get old object (not
updated one). I've to call Entitymanager.refresh(object) to get
updated object.
Why?
The first-level cache is active for the duration of a session. Any object entity previously retrieved in the context of a session will be retrieved from the first-level cache unless there is reason to go back to the database.
Is there a reason to go back to the database after your SQL update? Well, as the book Pro JPA 2 notes (p199) regarding bulk update statements (either via JPQL or SQL):
The first issue for developers to consider when using these [bulk update] statements
is that the persistence context is not updated to reflect the results
of the operation. Bulk operations are issued as SQL against the
database, bypassing the in-memory structures of the persistence
context.
which is what you are seeing. That is why you need to call refresh to force the entity to be reloaded from the database as the persistence context is not aware of any potential modifications.
The book also notes the following about using Native SQL statements (rather than JPQL bulk update):
■ CAUTION Native SQL update and delete operations should not be
executed on tables mapped by an entity. The JP QL operations tell the
provider what cached entity state must be invalidated in order to
remain consistent with the database. Native SQL operations bypass such
checks and can quickly lead to situations where the inmemory cache is
out of date with respect to the database.
Essentially then, should you have a 2nd level cache configured then updating any entity currently in the cache via a native SQL statement is likely to result in stale data in the cache.
In Spring Boot JpaRepository:
If our modifying query changes entities contained in the persistence context, then this context becomes outdated.
In order to fetch the entities from the database with latest record.
Use #Modifying(clearAutomatically = true)
#Modifying annotation has clearAutomatically attribute which defines whether it should clear the underlying persistence context after executing the modifying query.
Example:
#Modifying(clearAutomatically = true)
#Query("UPDATE NetworkEntity n SET n.network_status = :network_status WHERE n.network_id = :network_id")
int expireNetwork(#Param("network_id") Integer network_id, #Param("network_status") String network_status);
Based on the way you described your usage, fetching from the repo should retrieve the updated object without the need to refresh the object as long as the method which used the entity manager to merge has #transactional
here's a sample test
#DirtiesContext(classMode = ClassMode.AFTER_CLASS)
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = ApplicationConfig.class)
#EnableJpaRepositories(basePackages = "com.foo")
public class SampleSegmentTest {
#Resource
SampleJpaRepository segmentJpaRepository;
#PersistenceContext
private EntityManager entityManager;
#Transactional
#Test
public void test() {
Segment segment = new Segment();
ReflectionTestUtils.setField(segment, "value", "foo");
ReflectionTestUtils.setField(segment, "description", "bar");
segmentJpaRepository.save(segment);
assertNotNull(segment.getId());
assertEquals("foo", segment.getValue());
assertEquals("bar",segment.getDescription());
ReflectionTestUtils.setField(segment, "value", "foo2");
entityManager.merge(segment);
Segment updatedSegment = segmentJpaRepository.findOne(segment.getId());
assertEquals("foo2", updatedSegment.getValue());
}
}
I have a Spring Boot 1.3.M1 web application using Spring Data JPA. For optimistic locking, I am doing the following:
Annotate the version column in the entity: #Version private long version;. I confirmed, by looking at the database table, that this field is incrementing properly.
When a user requests an entity for editing, sending the version field as well.
When the user presses submit after editing, receiving the version field as a hidden field or something.
Server side, fetching a fresh copy of the entity, and then updating the desired fields, along with the version field. Like this:
User user = userRepository.findOne(id);
user.setName(updatedUser.getName());
user.setVersion(updatedUser.getVersion());
userRepository.save(user);
I was expecting this to throw exception when the versions wouldn't match. But it doesn't. Googling, I found some posts saying that we can't set the #Vesion property of an attached entity, like I'm doing in the third statement above.
So, I am guessing that I'll have to manually check for the version mismatch and throw the exception myself. Would that be the correct way, or I am missing something?
Unfortunately, (at least for Hibernate) changing the #Version field manually is not going to make it another "version". i.e. Optimistic concurrency checking is done against the version value retrieved when entity is read, not the version field of entity when it is updated.
e.g.
This will work
Foo foo = fooRepo.findOne(id); // assume version is 2 here
foo.setSomeField(....);
// Assume at this point of time someone else change the record in DB,
// and incrementing version in DB to 3
fooRepo.flush(); // forcing an update, then Optimistic Concurrency exception will be thrown
However this will not work
Foo foo = fooRepo.findOne(id); // assume version is 2 here
foo.setSomeField(....);
foo.setVersion(1);
fooRepo.flush(); // forcing an update, no optimistic concurrency exception
// Coz Hibernate is "smart" enough to use the original 2 for comparison
There are some way to workaround this. The most straight-forward way is probably by implementing optimistic concurrency check by yourself. I used to have a util to do the "DTO to Model" data population and I have put that version checking logic there. Another way is to put the logic in setVersion() which, instead of really setting the version, it do the version checking:
class User {
private int version = 0;
//.....
public void setVersion(int version) {
if (this.version != version) {
throw new YourOwnOptimisticConcurrencyException();
}
}
//.....
}
You can also detach entity after reading it from db, this will lead to version check as well.
User user = userRepository.findOne(id);
userRepository.detach(user);
user.setName(updatedUser.getName());
user.setVersion(updatedUser.getVersion());
userRepository.save(user);
Spring repositories don't have detach method, you must implement it. An example:
public class BaseRepositoryImpl<T, PK extends Serializable> extends QuerydslJpaRepository<T, PK> {
private final EntityManager entityManager;
public BaseRepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
this.entityManager = entityManager;
}
public void detach(T entity) {
entityManager.detach(entity);
}
...
}
Part of the #AdrianShum answer is correct.
The version comparing behavior follows basically this steps:
Retrieve the versioned entity with its version number, lets called V1.
Suppose you modify some entity's property, then Hibernate increments the version number to V2 "in memory". It doesn't touch the database.
You commit the changes or they are automatically commited by the environment, then Hibernate will try to update the entity including its version number with V2 value. The update query generated by Hibernate will modify the registry of the entity only if it match the ID and previous version number (V1).
After the entity registry is successfully modified, the entity takes V2 as its actual version value.
Now suppose that between steps 1 and 3 the entity was modified by another transaction so its version number at step 3 isn't V1. Then as the version number are different the update query won't modify any registry, hibernate realize that and throw the exception.
You can simply test this behavior and check that the exception is thrown altering the version number directly on your database between steps 1 and 3.
Edit.
Don't know which JPA persistence provider are you using with Spring Data JPA but for more details about optimistic locking with JPA+Hibernate I suggest you to read chapter 10, section Controlling concurrent access, of the book Java Persistence with Hibernate (Hibernate in Action)
In addition to #Adrian Shum answer, I want to show how I solved this problem. If you want to manually change a version of Entity and perform an update to cause OptimisticConcurrencyException you can simply copy Entity with all its field, thus causing an entity to leave its context (same as EntityManager.detach()). In this way, it behaves in a proper way.
Entity entityCopy = new Entity();
entityCopy.setId(id);
... //copy fields
entityCopy.setVersion(0L); //set invalid version
repository.saveAndFlush(entityCopy); //boom! OptimisticConcurrencyException
EDIT:
the assembled version works, only if hibernate cache does not contain entity with the same id. This will not work:
Entity entityCopy = new Entity();
entityCopy.setId(repository.findOne(id).getId()); //instance loaded and cached
... //copy fields
entityCopy.setVersion(0L); //will be ignored due to cache
repository.saveAndFlush(entityCopy); //no exception thrown
I'm using JPA 2 with the Hibernate ver. 4.1.7.Final as JPA implementation. I'm also using Spring framework v. 3.1.2.RELEASE to be clear. And here is my problem.
I have written a method to add/update my User entity.
#Override
#Transactional
public void saveUser(UserForm userForm) {
User user;
if (userForm.getId() == null) { // new user
user = new User();
user.setCreationDate(new Date());
entityManager.persist(user); // !!!
} else {
user = entityManager.find(User.class, userForm.getId());
}
user.setFirstName(userForm.getFirstName());
user.setLastName(userForm.getLastName());
user.setMiddleName(userForm.getMiddleName());
user.setEmail(userForm.getEmail());
user.setRole(entityManager.find(Role.class, 1));//TODO
user.setLogin(userForm.getLogin());
user.setPassword(userForm.getPassword1());
entityManager.flush();
}
I'm testing addition of user (userForm.getId() == null). And the above code doesn't work, giving error:
javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: Column 'first_name' cannot be null
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1377)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1300)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1306)
at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:989)
...
But. If I move call to persist() to the end before flush() all works fine:
#Override
#Transactional
public void saveUser(UserForm userForm) {
User user;
if (userForm.getId() == null) { // new user
user = new User();
user.setCreationDate(new Date());
} else {
user = entityManager.find(User.class, userForm.getId());
}
user.setFirstName(userForm.getFirstName());
user.setLastName(userForm.getLastName());
user.setMiddleName(userForm.getMiddleName());
user.setEmail(userForm.getEmail());
user.setRole(entityManager.find(Role.class, 1));//TODO
user.setLogin(userForm.getLogin());
user.setPassword(userForm.getPassword1());
entityManager.persist(user);// !!!
entityManager.flush();
}
I think what happens is in first (problem) case it tries to store the User object data existed at time of persist call, although the actual save executed during flush() (upd. Wrong. It tries to issue sql insert exactly at persist() call). What make me think this way is when I tried to debug I have found that it still tries to insert correct creation_date somewhere inside, but other values are nulls.
But I swear that in my other project I've been working on some years ago this worked just fine, though then I used Oracle instead of MySQL (I don't think this is the reason) and older versions of frameworks.
What could be the problem here? Maybe there is some configuration option for Hibernate affecting this?
Or this is the correct behaviour and my misunderstanding of JPA API?
UPD. I'm using
#Id
#GeneratedValue
#Column(columnDefinition="INT")
private int id;
for id field in User entiry. I believe it means generation strategy = AUTO, which is appropriate for mysql auto_increment key.
You set your user first name to null (user.setFirstName(userForm.getFirstName());) because userForm.getFirstName() returns null, and your error displays that: Column 'first_name' cannot be null.
You should check why userForm.getFirstName() returns null or allow user's first name to be null because your current entity configuration does not allow that.
Maybe you could show us User entity?
Thats correct behaviour. At the time of your first call persist(), user's firstName is null. Enventhough you have set the first name after this point, but before the flush, JPA/Hibernate prepared commands would be something like,
At the time of persist, the state of user object lets say, user#version1
Insert into user values (id, creationdate, null, null..) from user#version1 object
Now a new user version exists user#version2
At the time of flush,
check for any pending inserts/updates.. bring all the objects to version1, which is at the time of last persist.
Note that, flush may or may not do physical insert operation in DB, but the objects are are brought to the correct state, meaning non persisted changes would be lost.
Dont worry if it was working fine before may be with hibernate 3, and started not working when you migrated to hibernate 4. I have several issues, where hibernate 3 is no problem, but doesnt work on hibernate 4 without additional fixes.
I have a java project that runs on a webserver. I always hit this exception.
I read some documentation and found that pessimistic locking (or optimistic, but I read that pessimistic is better) is the best way to prevent this exception.
But I couldn't find any clear example that explains how to use it.
My method is like:
#Transactional
public void test(Email email, String subject) {
getEmailById(String id);
email.setSubject(subject);
updateEmail(email);
}
while:
Email is a Hibernate class (it will be a table in the database)
getEmailById(String id) is a function that returns an email (this method is not annotated with #Transactional)
updateEmail(email): is a method that updates the email.
Note: I use Hibernate for save, update & so on (example: session.getcurrentSession.save(email))
The exception:
ERROR 2011-12-21 15:29:24,910 Could not synchronize database state with session [myScheduler-1]
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [email#21]
at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1792)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2435)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2335)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2635)
at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:115)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:279)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:263)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:168)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:365)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:137)
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:656)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at $Proxy130.generateEmail(Unknown Source)
at com.admtel.appserver.tasks.EmailSender.run(EmailNotificationSender.java:33)
at com.admtel.appserver.tasks.EmailSender$$FastClassByCGLIB$$ea0d4fc2.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:149)
at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:688)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:55)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:50)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:50)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:621)
at com.admtel.appserver.tasks.EmailNotificationSender$$EnhancerByCGLIB$$33eb7303.run(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.util.MethodInvoker.invoke(MethodInvoker.java:273)
at org.springframework.scheduling.support.MethodInvokingRunnable.run(MethodInvokingRunnable.java:65)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:51)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
at java.util.concurrent.FutureTask$Sync.innerRunAndReset(FutureTask.java:317)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:150)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$101(ScheduledThreadPoolExecutor.java:98)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.runPeriodic(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:204)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:680)
ERROR 2011-12-21 15:29:24,915 [ exception thrown < EmailNotificationSender.run() > exception message Object of class [Email] with identifier [211]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [Email#21] with params ] [myScheduler-1]
org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException: Object of class [Email] with identifier [21]: optimistic locking failed; nested exception is
Pessimistic locking is generally not recommended and it's very costly in terms of performance on database side. The problem that you have mentioned (the code part) a few things are not clear such as:
If your code is being accessed by multiple threads at the same time.
How are you creating session object (not sure if you are using Spring)?
Hibernate Session objects are NOT thread-safe. So if there are multiple threads accessing the same session and trying to update the same database entity, your code can potentially end up in an error situation like this.
So what happens here is that more than one threads try to update the same entity, one thread succeeds and when the next thread goes to commit the data, it sees that its already been modified and ends up throwing StaleObjectStateException.
EDIT:
There is a way to use Pessimistic Locking in Hibernate. Check out this link. But there seems to be some issue with this mechanism. I came across posting a bug in hibernate (HHH-5275), however. The scenario mentioned in the bug is as follows:
Two threads are reading the same database record; one of those threads
should use pessimistic locking thereby blocking the other thread. But
both threads can read the database record causing the test to fail.
This is very close to what you are facing. Please try this if this does not work, the only way I can think of is using Native SQL queries where you can achieve pessimistic locking in postgres database with SELECT FOR UPDATE query.
We have a queue manager that polls data and gives it to handlers for processing. To avoid picking up the same events again, the queue manager locks the record in the database with a LOCKED state.
void poll() {
record = dao.getLockedEntity();
queue(record);
}
this method wasn't transactional but dao.getLockedEntity() was transactional with REQUIRED.
All good and on the road, after few months in production, it failed with an optimistic locking exception.
After lots of debugging and checking in details we could find out that some one has changed the code like this:
#Transactional(propagation=Propagation.REQUIRED, readOnly=false)
void poll() {
record = dao.getLockedEntity();
queue(record);
}
So the record was queued even before the transaction in dao.getLockedEntity() gets committed (it uses the same transaction of poll method) and the object was changed underneath by the handlers (different threads) by the time the poll() method transaction gets committed.
We fixed the issue and it looks good now. I thought of sharing it because optimistic lock exceptions can be confusing and are difficult to debug.
It doesn't appear that you are actually using the email that you retrieve from the database, but an older copy that you get as a parameter. Whatever is being used for version control on the row has changed between when the previous version was retrieved and when you are doing the update.
You probably want your code to look more like:
#Transactional
public void test(String id, String subject) {
Email email = getEmailById(id);
email.setSubject(subject);
updateEmail(email);
}
I had the this problem on my project.
After I implemented optimistic locking, I got the same exception.
My mistake was that I did not remove the setter of the field that became the #Version. As the setter was being called in java space, the value of the field did not match the one generated by the DB anymore. So basically the version fields did not match anymore. At that point any modification on the entity resulted in:
org.hibernate.StaleObjectStateException: Row was updated or deleted by
another transaction (or unsaved-value mapping was incorrect)
I am using H2 in memory DB and Hibernate.
This exception is probably caused by optimistic locking (or by a bug in your code). You're probably using it without knowing. And your pseudo-code (which should be replaced by real code to be able to diagnose the problem) is wrong. Hibernate saves all the modifications done to attached entities automatically. You shouldn't ever call update, merge or saveOrUpdate on an attached entity. Just do
Email email = session.get(emailId);
email.setSubject(subject);
No need to call update. Hibernate will flush the changes automatically before committing the transaction.
I had problems with the same error on more than one Spring project.
For me a general solution was, to split my service Method, that each INSERT, UPDATE and DELETE action got an own Method with #Transactional.
I think this problem relates to the internal Spring managment, where database interactions are executed at the end of the method and, in my oppinion, this is the point, where the Exception is triggered.
Update and further solutions.
My problem was that I queried an #Entity Class object and changed a value without saving it because, strictly speaking, it was updated by another query (outside the scope), but since this object was internal to the sessions in a map now it had a different value, the next request was blocked with this message.
So I created a variable and saved the new values there and then sent them to the UpdateQuery, so Hibernate did not register any unsaved changes and the line could be updated.
Hibernate seems to send a lock statement to the database every time an object of the #Entity class is changed or at least to spear the line locally by primary key.
I had the same problem and in my case the problem was missing and/or incorrect equals implementation on some types of fields in the entity object. At commit time, Hibernate checks ALL entities loaded in the session to check if they are dirty. If any of the entities are dirty, hibernate tries to persist them - no matter of the fact that the actual object that is requested a save operation is not related to the other entities.
Entity dirtiness is done by comparing every property of given object (with their equals methods) or UserType.equals if property has an associated org.Hibernate.UserType.
Another thing that surprised me was, in my transaction (using Spring annotation #Transactional), I was dealing with a single entity. Hibernate was complaining about some random entity that's unrelated to that entity being saved. What I realized is there is an outermost transaction we create at REST controller level, so the scope of the session is too big and hence all objects ever loaded as part of request processing get checked for dirtiness.
Hope this helps someone, some day.
Thanks Rags
Just in case someone checked this thread and had the same issue as mine...
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)
I'm using NHibernate, I received same error, during creation of an object...
I was passing the key manually, and also specified a GUID generator in mapping...
And hibernate generate same exact error for me,
So once I removed the GUID, and left the field empty, everything went just fine.
This answer may not help you, but will help someone like me, who just viewed your thread because of same error
check if the object exists or not in DB, if it exists get the object and refresh it:
if (getEntityManager().contains(instance)) {
getEntityManager().refresh(instance);
return instance;
}
if it fails the above if condition... find the Object with Id in DB, do the operation which you need, in this case exactly changes will reflects.
if (....) {
} else if (null != identity) {
E dbInstance = (E) getEntityManager().find(instance.getClass(), identity);
return dbInstance;
}
I had the experienced the same issue in different context of my project and there are different scenarios like
- object is accessed from various source like (server side and client)
- without any interval accessing the same object from a different place
In the first case
When I issue a server cal, before save the that object their one call from js and trying to save and another place, I got like, js call is going two, three times(I thing that call binding thing cause the issue)
I solved by
e.preventDefault()
The second case,
object.lock()
I was also receiving such an exception, but the problem was in my Entity identifier. I am using UUID and there are some problems in the way Spring works with them. So I just added this line to my entity identifier and it began working:
#Column(columnDefinition = "BINARY(16)")
Here you can find a little bit more information.
This error occurred for me when I was trying to update the same row from 2 different sessions. I updated a field in one browser while a second was open and had already stored the original object in its session. When I attempted to update from this second "stale" session I get the stale object error. In order to correct this I refetch my object to be updated from the database before I set the value to be updated, then save it as normal.
I also ran into this error when attempting to update an existing row after creating a new one, and spent ages scratching my head, digging through transaction and version logic, until I realised that I had used the wrong type for one of my primary key columns.
I used LocalDate when I should have been using LocalDateTime – I think this was causing hibernate to not be able to distinguish entities, leading to this error.
After changing the key to be a LocalDateTime, the error went away. Also, updating individual rows began to work as well – previously it would fail to find a row for updating, and testing this separate issue was actually what led me to my conclusions regarding the primary key mapping.
Don't set an Id to the object you are saving as the Id will be autogenerated
I had the same issue and for me, the case was a bit different, I was using Spring Data JPA and the entity class was annotated with #Entity and #Table annotation, and on the ID field I had #Id annotation but I missed adding #GeneratedValue since the DB table had the auto-increment identity field.
But the issue happened when we were doing bulk insert for these entities and since there was no Generator specified on the ID field, all entities had the default value (0) as the id field. and Started giving this exception:
javax.persistence.OptimisticLockException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) :[dao.entity.OrderAssortmentReportEntity#0]
We added the #GeneratedValue(strategy = GenerationType.IDENTITY) along with #Id and it worked.
I had the same problem in my grails project. The Bug was, that i overwrite the getter method of a collection field. This returned always a new version of the collection in other thread.
class Entity {
List collection
List getCollection() {
return collection.unique()
}
}
The solution was to rename the getter method:
class Entity {
List collection
List getUniqueCollection() {
return collection.unique()
}
}
if you are using Hibernate with Dropwizard,
this could happen if you are using id as autogenerated.
Remove #GeneratedValue
enter image description here
1. Reason for error
There is another situation: Error data.
#Column(name = "ID", unique = true, nullable = false, length = 32)
private String id;
One of the data is blank or null. When the front-end value is saved,
{
"cause": {
"cause": null,
"message": "Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.xxx#]"
},
"message": "Object of class [com.xxx] with identifier []: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.xxx#]"
}
2 .solving
Delete error data.
This problem happens if you are trying to update an object which is the same instance but retrieved from different List/Hash/ and so on, right from a different sub-thread.
In order to prevent StaleObjectStateException, in your hbm file write below code:
<timestamp name="lstUpdTstamp" column="LST_UPD_TSTAMP" source="db"/>
First check your imports, when you use session, transaction it should be org.hibernate
and remove #Transactinal annotation. and most important in Entity class if you have used #GeneratedValue(strategy=GenerationType.AUTO) or any other then at the time of model object creation/entity object creation should not create id.
final conclusion is if you want pass id filed i.e PK then remove #GeneratedValue from entity class.
Hibernate uses versioning to know that modified object you had is older than one which is currently persisted.
so when you update an entity don't include version in json body if its unwanted. just annotate with #Version in version column.
I had this problem in one of my apps, now, I know this is an old thread but here is my solution; I figured out by looking at the data inside the debugger that JVM actually didn't load it properly when Hibernate was trying to update the database (that is actually done in a different thread), so I added the keyword "volatile" to every field of the entities. It has some performance issues to do that but rather that than Heavy objects beeing thrown around...