SongRequestPubSplit is a child of SongRequest. Based on a PubSplitId which is a String passed in as input we find the SongRequestPubSplit object (PubSplitId is the PK of SongRequestPubSplit). Then we get the parent SongRequest. Now we modify both objects and then we create a transaction. Then we try to persist the SongRequest within the transaction. This code is throwing the following exception.....
However if we move the transaction to the beginning (meaning that we start the transaction before getting and updating these objects) then it works fine. Why is this happening? Does Hibernate not like it when objects are retrieved and modified outside of a TX and then tried to be persisted within?
The reason I want to only do the transaction at the end is for performance. I want to reduce the amount of deadlocks so I want to hold the lock for as short a time as possible.
Transaction to Session is one to one. When a Session is closed and you still have a reference to an object, then that object is considered to be in the Detached State. When you start another Session you will need to reattach the object to the Session. It is all about equality/identity mismatch. I recommend reading the first chapter int he Hibernate docs at hibernate.org. It will discuss the 3 mismatches between databases and ORM tools. Then read about object states later in the docs.
Hibernate's Session when loading data has an object it just loaded in a Map inside the PersistenceContext object inside the Session object. If you ever try to get the same record back it will make sure that the object it returns is the exact same object that is in the Map. So that there is ever only one instance of an object that represents one row in the database. If you close the Session with you holding a reference to that object, you have that one instance. Now if you create a new Session and load the same data, Hibernate doesn't know about your object you are holding, so it loads from the database and creates a new instance of that object and holds on to it. It is not the same object you are holding, they are in two different memory addresses. So when you try to save the object you are holding it is different than what is in the PersistenceContext.
So you are saying that there was already a session active when those objects were retrieved, and then when I created a transaction I created a 2nd session.
How then can I get around this problem? How can I retrieve the objects, work with them, and then only persist them within a transaction?
No, what I am saying is that a session is scoped to a transaction. Just creating a transaction does not create a session. But in order to have a session and run queries a transaction has to be in place, and removed when the transaction is done. You cannot use a single session, so to speak across multiple transactions. However, with Open Session in View you can accomplish it.