I am writing a web based application with hibernate and jsp/servlet. I have read about the sessionFactory.getCurrentSession and sessionFactory.openSession methods. I know the basic difference between them (using getCurrentSession you don't have to close the connection and when you commit the transaction, your session will automatically close). According to the my understanding, we should opt for getCurrentSession and do it through session-per-request.
Let's consider the following scenario:
Method A calls getCurrentSession and got the current session
In Method A, a transaction is started using the session from step 1
Method A calls Method B, which also has getCurrentSession and starts a transaction
Method B commits its transaction
Control returns to method A and it also commits the transaction
Now my questions are
Will the session found in step 1 and step 3 will be the same session?
If the answer for the question 1 is yes, then how would it handle the commit in step 4? Ideally it should close the session there itself and should throw an exception at step 5.
If the answer for the question 1 is no, then how do you handle such a scenario?
Will the session found in step 1 and step 3 will be the same session?
They should be the same, that's somehow part of the contract of getCurrentSession() and you'll get the Session bound to the thread as long as the unit of work has not been completed (i.e. a transaction has been committed or rolled back). Java Persistence with Hibernate puts it like this (p.481):
All the data-access code that calls getCurrentSession() on the global shared
SessionFactory gets access to the same current Session — if it’s called in the
same thread. The unit of work completes when the Transaction is committed (or rolled back). Hibernate also flushes and closes the current Session and its persistence context if you commit or roll back the transaction. The implication here is that a call to getCurrentSession() after commit or rollback produces a new Session and a fresh persistence context.
And you might also want to read what the javadoc of Session#beginTransaction() says.
If the answer for the question 1 is yes, then how would it handle the commit in step 4. Ideally it should close the session there itself and should give error at step 5.
Step 4 shouldn't be a problem, the Session will be flushed, the Transaction will be committed and the Session closed. But I expect step 5 to fail wih aTransactionException (that's my bet). But let me quote the javadoc of Transaction:
A transaction is associated with a Session and is usually instantiated by a call to Session.beginTransaction(). A single session might span multiple transactions since the notion of a session (a conversation between the application and the datastore) is of coarser granularity than the notion of a transaction. However, it is intended that there be at most one uncommitted Transaction associated with a particular Session at any time.
As underlined above, we are discussing around something that shouldn't happen (i.e. a design problem).
I don't have an answer for your scenario because I would not implement it that way as it seems to be asking for trouble. Instead, I'd start the transaction in C, where C invokes A and B, and have C issue the commit. Skeletally:
public void c(...) {
try {
transaction.begin();
a();
b();
transaction.commit();
catch (Exception e) {
transaction.rollback();
}
}
So here, a() and b() do not commit or rollback - how do they know the entire business task has been completed? They could throw an exception or perhaps return a boolean to tell the caller that something is amiss and a rollback is needed.
Related
I'm reading the Hibernate documentation at present and I came across the following quote:
If the Session throws an exception, including any SQLException, immediately rollback the database transaction, call Session.close() and discard the Session instance. Certain methods of Session will not leave the session in a consistent state. No exception thrown by Hibernate can be treated as recoverable. Ensure that the Session will be closed by calling close() in a finally block.
This all makes sense as far as I'm concerned, but it does make me wonder what the effect of closing a session is with a transaction neither committed nor rolled back?
E.g consider the following:
session = getSessionFactory().openSession();
session.beginTransaction();
session.save(carObject);
//session.getTransaction().commit();
session.close();
With commit commented out, and no rollback called here, what is the expected behaviour of session.close()? Does it simply rollback that commit automatically, does it leave a 'hanging' transaction? etc.
(I understand this obviously wouldn't be good practice - I'm just trying to get my head around the underlying concepts a bit more.)
I've done a bit of digging into Hibernate:
Persistence sessions keep their life-cycle somewhat independent from JDBC connections. When you close Hibernate's Session the connection is released. Exact meaning of "releasing connection" depends on how the connection was obtained in the first place:
if the connection was provided manually (e.g. via sessionFactory.openStatelessSession(connection)) you will get your connection with possibly unfinished transaction back when calling session.close()
in other cases calling session.close() will usually end up in calling connection.close()
No automatic session flushing or transaction commit / rollback is made by Hibernate. The same states for the JPA's EntityManager.
So what happens in the end depends on your connection provider / data source. With C3PO any unfinished transaction will be rolled-back when the connection is returned to the pool. On the other hand if you have managed JTA connection then the actual transaction handling might be completely out of scope to your application.
I have read in the book that:
Typically, the container begins a transaction immediately before an enterprise bean method starts. It commits the transaction just before the method exits.
Suppose my stateful EJB has exposed 3 business methods and the client calls these 3 methods sequentially and want wants to run these 3 methods within a single transaction. These methods would be called across multiple requests.
Now the spec says that the transaction is committed just before the method exits. How will I be able to rollback the whole transaction, if my 1st method is successful and 2nd (or 3rd) method fails?
Please note that I don't want to use create my own transaction boundary.
Any help or the pointer in the right direction would be highly appreciated.
You are using a stateful session bean to act as a buffer. And you want a transaction around multiple calls.
From a design perspective, it's OK, if the transaction is started and committed/rollbacked from a single request (from within a single HttpServletRequest for example). In this case you can use a UserTransaction to span the transaction over multiple calls to an EJB. But a UserTransaction is bound to the current thread, so it might be difficult to pass it to the next request.
If you start and commit from different requests, you lose control over the duration of the transaction: Now a client controls the transaction. If that client crashes, the transaction won't be rolled back until the transaction timeout is reached. So the recommendation in this case is to buffer in a HttpSession for example. If all data has been collected, call a (stateless) EJB to persist it.
Create a method in the bean that calls all the other 3 methods. Then they'll be in the same transaction.
I am using the following piece of code I found online (Here) as an example of JTA Transaction processing:
// Get a UserTransaction
UserTransaction txn = new InitialContext().lookup("java:comp/UserTransaction");
try {
System.out.println("Starting top-level transaction.");
txn.begin();
stmtx = conn.createStatement(); // will be a tx-statement
stmtx.executeUpdate("INSERT INTO test_table (a, b) VALUES (1,2)");
stmtx.executeUpdate("INSERT INTO test_table2 (a, b) VALUES (3,4)");
System.out.print("\nNow attempting to rollback changes.");
txn.rollback();
}
I have a few questions, in general, about the JTA that are drawn from the example above:
I presume the whole point of saying txn.begin and then rollback is to be able to (apperently) rollback TWO SQL statements correct?
Each of the update queries were TRANSACTIONS themselves, right? They must have succeded so that we can get to rollback call at the bottom. Well, if they succeded i.e. commited, how on earth can we roll them back all of a sudden?
The most important question: what happens when we say txn.begin()? I understand from the JTA API that it is supposed to register this transaction with a calling thread by TransactionManager instance. How is TM even linked to the UserTransaction? And finally, how is the txn aware of the fact that we modified the DB twice and is able to speak to DB to roll it back? We have not registered ANY ResourceManagers with it so it should not be aware of any resources being at play...
I am a bit lost here, so any info would be appreciated... Question 3 bothers me the most.
yes, or event just one. It's also the ability of committing the transaction at the end, and thus have the other concurrent transaction only see the new state after the transaction has been committed, and not all the temporary states between the beginning and the end of the transaction (i.e. the I in ACID)
No. An update is an update. It's executed as part of the transaction that you begun previously. If one of them doesn't succeed, you'll have an exception, and can still choose to commit the transaction (i.e. have all the previous updates committed), or to rollback the transaction (i.e. have all the previous updates canceled).
The UserTransaction has a reference to its transaction manager, presumably. When you get a connection from a DataSource in a Java EE environment, the DataSource is linked to the transaction manager of the Java EE container, and rollbacking the JTA transaction will use the XA protocol to rollback all the operations done on all the data sources during the transaction. That's the container's business, not yours.
There's a lot to learn about transactions, but maybe I can give you a head start:
Yes. But you will usually only want to rollback in case of a problem - some step of the transaction could not be completed because of a technical issue (syntax error, table not found, segment overrun, ...) or an application logic problem (customer has not enough funds for all order line items for example).
Given auto commit mode is disabled, the inserts are not committed before you actually commit. They are temporarily applied to the database using a Write-Ahead-Log (PostgreSQL, InnoDB-Engine, Oracle) with sophisticated Multi-Version-Concurrency-Control (MVCC) which determines which state of the database each transactional client can see. A very interesting topic :-).
A UserTransaction is registered with your current Thread. Resources (i.e. Databases or Messaging services) enlist themselves with the UserTransaction. This is usually only necessary when you are using distributed transactions (XA transactions, 2PC).
I suggest to get a good read on SQL programming (for example Head First SQL) and check out the Java EE 6 tutorial.
I need to know, whether the Hibernate's session is thread safe or not. But obvious a new session is attached to every thread for execution. But my question is if in one thread I have updated some value of an entity, so will that be reflected in other thread during same time execution?
My problem is when I fire update from two threads sequentially, the value is updated properly but when I fire the update almost altogether then it fails.
for eg.
current stage of table.
ID NAME MARKS
------- --------- --------
1 John 54
I am trying to do follwing :
Student student = session.load(Student.class, 1);
student.setMarks(student.getMarks() + 1);
session.update(student);
session.close();
When I try to run the above code in loop say 10, then value of "marks" in table "student" is properly updated i.e. the value gets updated to 64 which is proper.
But when I try to run the same code in threaded environment, it gives bad results.
It is not intended that implementors be threadsafe. Instead each thread/transaction should obtain its own instance from a SessionFactory.
Even with this in mind, your behaviour might still not be what you expect, because transactions come into play. You will have to set a proper transaction isolation level. See the configuration guide, hibernate.connection.isolation property.
Hibernate session and threads do not mix.
You should not use a session from multiple threads at once, and I recommend you only use a session from a single thread. DB session implementations are not even required to be theadsafe.
You also must consider what happens to the transactions when you start doing things in multiple threads. Transactions are tied to the current thread. This becomes quickly mindblowing and you enter areas where the implementers have not tested their products.
In the end life is too short to get lost in that swamp.
It depends on how you are creating a session.
Session can be created in two ways in hibernate.
getCurrentSession()
Yes. It offers thread safety as it'll ensure that it'll create a session for each thread if session not exist. transaction and automatic session closing is attached to this.
openSession()
It's not thread safe. developer manually needs to manage transactions and session flush and close operations.
Hibernate sessions are not thread safe. Use TheadLocal class to create sessions for each thread:-
private static ThreadLocal<Session> threadSafeSession = new ThreadLocal<Session>() {
protected Session initialValue(){
return sf.openSession();
}
};
In your method get session for each thread as:-
Session session = threadSafeSession.get();
Is there any way to "replay" transaction?
I mean, sometimes I get RollbackException and rollback the transaction. Can I then "clone" the transaction and try again, or once rollback is called, transaction is lost?
I really need the changes, and really don't want to trace every change for rerunning later...
thanks,
udi
Why do you get the exception in the first place ? This seems to me to be the crux of the matter.
Are you relying on optimistic writing ? If so, then you'll have to wrap your database writes in some form of loop, incorporating (perhaps) a backoff and a number of retries. You can't do this automatically, unfortunately (unless you investigate some form of AOP solution wrapping your database writes with a retry strategy ?)
That depends where that transaction comes from. In Java/JDBC, a transaction is tied to a connection. You start one by setting setAutoCommit() to false (otherwise, every statement becomes its own little transaction).
There is nothing preventing you from reusing the connection after a transaction failed (i.e. you called rollback).
Things get more tricky when you use Spring. Spring wraps your methods in a transaction handler and this handler tries to guess what it should do with the current transaction from the exceptions that get thrown in the method. The next question is: Which wrapper created the current transaction? I just had a case where I would call a method foo() which would in turn call bar(), both #Transactional.
I wanted to catch errors from bar() in foo() and save them into the DB. That didn't work because the transaction was created for foo() (so I was still in a transaction which Spring thought broken by the exception in bar()) and it wouldn't let me save the error.
The solution was to create baz(), make it #Transactional(propagation=Propagation.REQUIRES_NEW) and call it from foo(). baz() would get a new, fresh transaction and would be able to write to the DB even though it was called from foo() which already had a (broken) transaction.
Another alternative is to use JDBC savepoints to partly roll back.