I came across this example from a book while learning about the Hibernate framework.
public class BasicMovieManager()
{
private void persistMovie(Movie movie)
{
Session session=sessionFactory.getCurrentSession();
session.beginTransaction();
session.save(movie);
session.getTransaction().commit();
}
}
I can understand that the Movie object has to be mapped and written to the database. I also understand that the commit step will write to the database. But what is the purpose of save() here? A few sources I referred say that save() persists the data. Doesn't persist mean writing to a permanent storage? If not,what exactly does it mean?
I Believe the comparison is misplaced,you should compare
Commit vs Flush
and
Save vs Persist
Edited:
You should know this:
transient: never persistent, not associated with any Session.
persistent: associated with a unique Session.
detached: previously persistent, not associated with any Session.
Commit will save the data to DB, so you cannot rollback anymore, in opposed to Flush.
Save will generate and return identifier prior to writing the object, later upon Flush or Commit it writes the data to the database.
Where Persist will not return a value,as you only mark the object as dirty in the cache, so upon flush or commit it will be saved, this is useful when persisting multiple objects in a transaction.
Quick answer: save() stores the data in the database. commit() makes it visible to others (cf. isolation levels).
Slightly longer answer: Database operations should obey the ACID principle, (A)tomicity being the operative element here. If you are doing more than one change/insert, you can wrap it in a transaction and commit/reject the entire set of operations as a whole.
In your example it doesn't make much sense to start a transaction, but in real-life situations it very much makes sense.
Cheers,
Basically transactions are used when you are trying to persist related set of objects. If you are trying to insert only one object then transaction is not necessary. But when you are trying to persist a set of related objects which are dependent on each other then you should go for transaction where it comes handy
As for example:
//1.Load session
//2. persist an object
In above scenario nothing will happen if your persisting of a single object fails or success but when you will do like this:
//1. Load session
//2. Persist one object
//3. Persist other object whose data affects previous
In above scenarion suppose second was performed successfully but third failed that can adversely affect your data or business. This can be resolved as:
//1. Load session
//2. Begin transaction
//3. perform set of related operation
//4. commit
If any thing will go wrong in above scenario the whole transaction will be rollbacked nothing will be persisted. And if you want to do something after your transaction fails you can handle it by using try catch.
So, basically save() is used to save data in tables but commit() is used in transaction management
Related
In a container managed transaction i get a detached object and merge it so that the detached object is brought to managed state.My initial question is by caching the Pojo java objects and merging is a better idea to get the object into session or performing the get of the data from the DB to get in to session context a better idea in terms of cost of operation/time involved in getting the data from the DB?If i am performing an merge at start to get the object into the session context and doing the modification on this merged object will the hibernate take care of generating all the required sql statements and at the end will it be taken care ?
Please comment back which is better approach to get the entity to session , using a merge of the cached detached object or fetching the data from the DB is lesser time consumption?
when you call detach and then merge, merge returns you the attached entity in the context. it's a common mistake that users would use the passed entity after merge operation hoping that would be managed but this is not the case. you have to use the returned entity from merge which will be managed by hibernate and subsequent changes will be flushed at transaction end automatically.
it doesnt matter much when u load your entity coz hibernate will anyways fire a select if it is already not loaded in the context. also even if you keep on doing changes to your managed entity, hibernate will fire update only when you exit your transaction or call flush() explicitly.
Copy the state of the given object onto the persistent object with the same identifier. If there is no persistent instance currently associated with the session, it will be loaded. Return the persistent instance. If the given instance is unsaved, save a copy of and return it as a newly persistent instance. The given instance does not become associated with the session. This operation cascades to associated instances if the association is mapped with cascade="merge".
According to the API it saves a copy when you perform the merge and then returns a new instance. Based on my experience its always better to merge at the end after you have performed all the updates on the objects in detached state. Its better because you will call merge operation only at the end when the object state is ready to be persisted.
Also this will perform better because the object is moved to persistent context at the end and hence Hibernate will not have to come into picture till the end.
Hibernate, as I know updates the Database once the attached Objects in the session's value changes. Is there a way to obtain a fresh copy from the database and reattach the fresh copy discarding the changed object?
Below is an object I obtained from Querying the Database using Hibernate.
Rtpmast rtpmast = (Rtpmast) iterator.next();
Once executed the below code
rtp.setRptval1(promotionMethod.getType());
Hibernate registers that the the object has changed. And when I commit a transaction later on or query the Database the Session is flushed?
What I want is to temporally update the Rtpmast object and discard the changed object later.
Hibernate, as I know updates the Database once the attached Objects
in the session's value changes.
Actually,Hibernate updates the database only after invoking session.save(...), session.delete(...) etc. methods. If you're in a transaction you'd also have to commit changes.
What you want is either use evict() and re-query the object, as you've mentioned, or detach the instance you're working on, which means you need to close() the Session (there are other, less trivial, ways to do so as mentioned here).
I have been confused about transaction.rollback. Here is example pseudocode:
transaction = session.beginTransaction()
EntityA a = new EntityA();
session.save(a);
session.flush();
transaction.rollback();
What happens when this code works? Do I have the entity in the database or not?
Short answer: No, you won't have entity in the database.
Longer answer: hibernate is smart enough not to send insert/updates to the DB until it knows if the transaction is going to be committed or rolled back (although this behavior can be changed by setting a different FlushMode), in your case by calling flush you are forcing the SQL to be sent to the DB but you still have the DB transaction to protect you, when you call rollback the DB transaction will be rolled back removing the changes performed inside itself and hence nothing will be actually saved. Note that depending on your configured transaction isolation level perhaps other transactions will be able to see in some way the EntityA you saved for the short while between the save and the rollback.
Also note that flush is called automatically when you try to read from DB, in 99% of the cases calling it explicitly is not necessary. One exception that comes to mind is when unit testing with auto rolling back tests.
When you call session.save(a) Hibernate basically remembers somewhere inside session that this object has to be saved. It can decide if he wants to issue INSERT INTO... immediately, some time later or on commit. This is a performance improvement, allowing Hibernate to batch inserts or avoid them if transaction is rolled back.
When you call session.flush(), Hibernate is forced to issue INSERT INTO... against the database. The entity is stored in the database, but not yet commited. Depending on transaction isolation level it won't be seen by other running transactions. But now the database knows about the record.
When you call transaction.rollback(), Hibernate rolls-back the database transaction. Database handles rollback, thus removing newly created object.
Now consider the scenario without flush(). First of all, you never touch the database so the performance is better and rollback is basically a no-op. On the other hand if transaction isolation level is READ UNCOMMITTED, other transactions can see inserted record even before commit/rollback. Without flush() this won't happen, unless Hibernate does not decide to flush() implicitly.
I think you get confused with flush and commit.
flush() synchronizes the state with the database, but it is not doing a commit. The state is still visible by transaction, so that you can call rollback to rollback.
So the answer to your question is: no, you don't have the entity (a) in the database.
I'm gonna go with this design:
create an object and keep it alive during all web-app session.
And I need to synchronize its state with database state.
What I want to achieve is that :
IF between my db operations, that is, modifications that I persist to a db
someone intentionally spoils table rows, then on next saving to a database
all those changes WOULD BE OVERWRITTEN with the object state, that always contains valid data.
What Hibernate methods do you recommend me to use to persist the modifications in a database?
saveOrUpdate() is a possible solution, but maybe there's anything better?
Again, I repeat how it looks. First I create an object without collections. Persist it (save()).
Then user provides us with additional data. In a serviceLayer, again, we modify our object in memory (say, populate it with collections) and then, persist it again.
So every serviceLayer operation of the next step must simply guarantee that database contains the exact persistent copy of this object that we have in memory. If data in a database differ, it MUST BE OVERRIDDEN with the object (kept in memory) state.
What Session operations do you recommend?
FWIW saveOrUpdate() looks like the best option overall:
The saveOrUpdate() method is in practice more useful than update(),
save(), or lock(): In complex conversations, you don’t know if the item is in
detached state or if it’s new and transient and must be saved. The automatic
state-detection provided by saveOrUpdate() becomes even more useful when you
not only work with single instances, but also want to reattach or persist a network
of connected objects and apply cascading options.
However for your case, if you are sure the entity was modified in detached state, and/or don't mind occasionally hitting the DB with an unnecessary UPDATE, maybe update() is the safest choice:
The update() operation
on the Session reattaches the detached object to the persistence context and
schedules an SQL UPDATE. Hibernate must assume that the client modified the
object while it was detached. [...] The persistence context is flushed automatically
when the second transaction in the conversation commits, and any
modifications to the once detached and now persistent object are synchronized
with the database.
Quotes from Java Persistence with Hibernate, chapter 11.2.2.
We are using Hibernate Spring MVC with OpenSessionInView filter.
Here is a problem we are running into (pseudo code)
transaction 1
load object foo
transaction 1 end
update foo's properties (not calling session.save or session.update but only foo's setters)
validate foo (using hibernate validator)
if validation fails ?
go back to edit screen
transaction 2 (read only)
load form backing objects from db
transaction 2 end
go to view
else
transaction 3
session.update(foo)
transaction 3 end
the problem we have is if the validation fails
foo is marked "dirty" in the hibernate session (since we use OpenSessionInView we only have one session throughout the http request), when we load the form backing objects (like a list of some entities using an HQL query), hibernate before performing the query checks if there are dirty objects in the session, it sees that foo is and flushes it, when transaction 2 is committed the updates are written to the database.
The problem is that even though it is a read only transaction and even though foo wasn't updated in transaction 2 hibernate doesn't have knowledge of which object was updated in which transaction and doesn't flush only objects from that transaction.
Any suggestions? did somebody ran into similar problem before
Update: this post sheds some more light on the problem: http://brian.pontarelli.com/2007/04/03/hibernate-pitfalls-part-2/
You can run a get on foo to put it into the hibernate session, and then replace it with the object you created elsewhere. But for this to work, you have to know all the ids for your objects so that the ids will look correct to Hibernate.
There are a couple of options here. First is that you don't actually need transaction 2 since the session is open you could just load the backing objects from the db, thus avoiding the dirty check on the session. The other option is to evict foo from the session after it is retrieved and later use session.merge() to reattach it when you what your changes to be stored.
With hibernate it is important to understand what exactly is going on under the covers. At every commit boundary it will attempt to flush all changes to objects in the current session regardless of whether or not the changes where made in the current transaction or any transaction at all for that matter. This is way you don't actually need to call session.update() for any object that is already in the session.
Hope this helps
There is a design issue here. Do you think an ORM is a transparent abstraction of your datastore, or do you think it's a set of data manipulation libraries? I would say that Hibernate is the former. Its whole reason for existing is to remove the distinction between your in-memory object state and your database state. It does provide low-level mechanisms to allow you to pry the two apart and deal with them separately, but by doing so you're removing a lot of Hibernate's value.
So very simply - Hibernate = your database. If you don't want something persisted, don't change your persistent objects.
Validate your data before you update your domain objects. By all means validate domain objects as well, but that's a last line of defense. If you do get a validation error on a persistent object, don't swallow the exception. Unless you prevent it, Hibernate will do the right thing, which is to close the session there and then.
What about using Session.clear() and/or Session.evict()?
What about setting singleSession=false on the filter? That might put your operations into separate sessions so you don't have to deal with the 1st level cache issues. Otherwise you will probably want to detach/attach your objects manually as the user above suggests. You could also change the FlushMode on your Session if you don't want things being flushed automatically (FlushMode.MANUAL).
Implement a service layer, take a look at spring's #Transactional annotation, and mark your methods as #Transactional(readOnly=true) where applicable.
Your flush mode is probably set to auto, which means you don't really have control of when a DB commit happens.
You could also set your flush mode to manual, and your services/repos will only try to synchronize the db with your app when you tell them to.