Object from a different session - saveOrUpdate in another session? - spring

In the application that i'm working on, i have a senario,
UI updates some data which is already retrived from the database and sends an object back to the server(say using EntityTag.java), Server retrives the object again from the DB(say Entity.java which is mapping file in hibernate) and copies all the values from the EntityTag into Entity.java objct.
Now, using some service, it tries to save the updated Entity.java object. This is does in spring declarative transactions. So, i'm assuming that the new transaction is started on this service.
What I was hoping to see in this service method is a session. merge() because, we updated an object which was detached, but here they use saveOrUpdate on the entity.java object. I see that the object is updated into the table without any issues. This is so wierd, until now i've been thinking that the merge will merge the object into the session and later, i can commit the changes, this seems soo wierd to me.
In what cases does saveandupdate work without issues?

See this answer, saveOrUpdate works well if there is never the risk that there is not already an object with the same database identifier associated to the session where saveOrUpdate is being called.
If an object already exists with the same database Id, saveOrUpdate throws an error, while merge will just replace the object in the session with the new object and return a reference to the new attached object.
Hibernate had a method called saveOrUpdateCopy that provided the same as current merge,
which got standardized as merge in JPA.
Although saveOrUpdateis still available and used in many tutorials, you would be better off always using merge when dealing with detached objects, as it's hard / error prone to try to guess if an object is already in a session.

Related

Can a commit be reverted in Javers?

I have a scenario where in case a subsequent operation fails, a commit or a shallow delete might need to be reverted. This would be particularly useful in scenarios involving Mongo where there is no atomicity available across collections. Is this possible with Javers?
There is no 'rollback' option for now. It can be implemented in the future but there could be some limitations.
You could annotate your method with #Transactional annotation and if an exception occurs the database updates that occurred within that method would rollback which should include the Javers tables.
https://www.logicbig.com/tutorials/spring-framework/spring-data-access-with-jdbc/transactional-roll-back.html
Alternatively, you could use Spring AOP to perform a custom rollback and then delete the committed records manually.
How to call a custom rollback method in Spring Transaction Management?
Hope one of these options helps you.
If you need to travel back in time, instead of using Javers, you should redesign your database using functional programming idea called "persistent data structures". At the core of it is that you should never modify any existing data, you should always create new versions of existing entities.
You can read about persistent data structures for example here:
https://medium.com/#arpitbhayani/copy-on-write-semantics-9538bbeb9f86
https://medium.com/#mmdGhanbari/persisting-a-persistent-data-structure-3f4cfd46036

Spring Transaction propagation: can't get the #OneToMany related entities when using the same transaction for creation and consultation operation

I have the following problem: I am working on a spring-boot application which offers REST services and use a relational (SQL) database using spring-data-jpa.
I have two REST services:
- a entity-creation service, which create the child-entity, the parent-entity and associate them in a same transaction. When this service ends, the data are committed into the database.
- an entity consultation service, which get back the parent-entity with its children
These two services are annotated with the #Transactional annotation. It production case, it works well: I can create an parent-entity with its children in one transaction (which is commited/ended), and get it in another transaction latter.
The problem is when I want to create integration-tests. My idea was to annotate each test with the #Transactional annotation, and do a rollback after each test. This way I keep my database clean between each test, and I don't have a generate the schema again or clean all the records in the database.
The integration test consists in creating a parent and its children and then reading it, everything in one transaction (as the test is annotated with #Transaction). When reading the entity previously created in the same transaction, I can get the parent entity, but the children are not fetched (null value). I am not sure to understand very well the transaction mechanism: I was thinking that using the #Transactional on the test method, the services (annotated with "#Transactional") invoked by this test should detect and use the same transaction opened by the test method (the propagation is configured to "REQUIRED"). Hence as the transaction uses the same EntityManager, this one should be able to return the relation between the parent entity and its children created previously in the same transaction, even if the data has not been committed to the database. The strange thing is that it retrieve the parent entity (which has not been yet committed into the database), but not its children. Is my understanding of the transaction concept correct? If not, could someone explains me what am I missing?
Also, if someone did something similar, could he explain me how he did it please?
My code is quite complex. I first want to know if I understand well how are transaction managed and if someone already did something similar. If really it is required, I can send more information about my implementation (how the transaction-manager and the entity-manager are initialized, the JPA entities, the services etc...)
Binding the Entity-manager in my test and calling its flush method from my test,between the creation and the reading, the reading operation works well: I get the parent entity with its children. But the data are written into the database during the creation to read it latter during the read operation. And I don't want the transaction to be committed as I need my test to work on an empty database. My misunderstanding is not so much about the Transaction mechanism, but more about the entity-manager: it does not keep as a cache the entities created and theirs relations...
This post help me.
Issue with #Transactional annotations in Spring JPA
As a final word, I am thinking about calling an SQL script before each test to empty my database.

Why does #Transactional save automatically to database

I have a method annotated with #Transactional. I retrieve an object from my DB, change a field, and then return from the method. Without saving my object, the database gets updated anyway which is strange.
Could you please tell me how to avoid this beahvior?
This behaviour is one of the main purposes of transactionality.
Before the transactional method is about to return, the transaction commits, meaning all changes to the managed entities are flushed to the database.
If an error occurs, the transaction will be rolled back, meaning that no changes will be committed to the database.
You are probably getting the LazyInitializationException when trying to access a lazily loaded property, probably a collection from an entity. Lazily loded properties do not get instantiated when you fetch an entitiy from DB.
If you access a lazily loaded property in a transaction, the persistence provider will create a query, instantiate the result and attach it to the 'parent' entity.
EDIT: If you want to have the lazy properties loaded AND be able to change your entity without the changes being persisted to the DB, you can fetch the entity with fetch joins for the lazy properties.
em.createQuery("SELECT e FROM MyEntity e JOIN FETCH e.lazyProp");
Then proceed with one of the methods described by #orid.
If you are not using fetch joins, you will need to access the lazily loaded properties while still inside the transaction:
myEntity.getLazyProp().size();
Note the call to size(). Calling the getter is not enough as you will get a proxy. You need to perform an operation that needs the actual data from the property.
This a normal JPA behavior.
Once you retrieve an object via find() or so, that object is regarded as attached, or belongs to a persistence context. Once you exit the method the #Transactional triggers a Spring transaction management aspect which flushes every "dirty" object to database and commits the transaction. Since your object is already changed within the context of the persistence context and the transaction, the changes are saved to the database even without the need to explicitly call a save method.
If you want to change your object without affecting the database, you have two options:
Update the field after returning from the method annotated with #Transactional
If withing the method, call detach on the entity manager

data getting saved in db without save(Hibernate)

i'm new to hibernate, while i add an element and cancel it, I see that the data gets saved in db. Nowhere in my code i called save method to save it.
If you're modifying an object already associated with an Hibernate session all your modifications will be saved. Check the manual.
For example if you do something like:
Load an object from a DB
Modify the object by adding or removing values
The modifications will be saved even if you don't use the save() method.
seems you have AutoFlash and/or AutoCommit parameters On in your hibernate configuration. Try disable them.
Once you load the data from the db, it becomes persistent, and any changes made to it will be updated, if it is updated before the session is closed. If you do not want the data in the db to be updated with the changes you are making after loading it, make the changes only after closing the session. Then after that, if you want to persist the data again, open one more session and call save() or persist().
EDIT:
1) Make sure cache is disabled in order to ensure there is no caching between different sessions.
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
2) Follow the steps:
i) begin session --> begin transaction --> get data from both tables --> close transaction --> close session.
ii) create object of 3rd table--> do whatever u want with it, like adding data from the first two tables.
iii) begin new session --> begin new transaction --> save the object of 3rd table using session.save() --> close transaction --> close session.
After step (i) is done, the objects from table1 and table2 are no more 'persistent', and are 'detached'. If you don't do session.save() in step (iii), the object of table3 won't get saved, because it is no longer dealing with persistent objects.
This is from my understanding of persistent and detached objects. If it doesn't work, do reply. I will code it down and find a solution.
And one more advice, do consider using session.persist() instead of session.save().
If you want to understand their difference, this is the link: What's the advantage of persist() vs save() in Hibernate?
Good Luck!
You may have used the #Transactional annotation.
Try just removing the annotation.

Getting Exception when trying to update record second time in hibernate

We keep user object in session. When we update it first time ( using entityManager.merge( ent )) it works fine; but second time I get following exception:
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.kids.domain.Child#22]
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:492)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:256)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:86)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:781)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:766)
I am using spring with hibernate & jpa. Spring is injecting EntityManager in DAO. I do understand that if I would do a fresh find() everytime then make all the changes on that object and then merge().. it will work fine.. but in my case I cannot do that.
I also checked that the hashcode of EntityManager used is same both time; i.e same EntityManager instance is getting used both time then why it gives this exception when trying to update a record second time
Any help would be highly appreciated.
I tried lots of things but nothing worked. Even on internet there was no solution. Finally, when I removed version from my entities ( i.e. remove OptimisticLockException support provided by hibernate ).. everything started working fine. Probably , I will try to handle OLE myself.
When you call merge method in the current transaction, hibernate will copy the state of the given object onto the persistent object with the same identifier and returns new manageable entity. You need to work on the manageable entity which is returned by the merge in subsequent operations because this entity has new version than in DB.

Resources