I'm using Hibernate transaction for read operation from db as following excample code:
Session session = sessionFactory.openSession();
Transaction tx= session.beginTransaction();
session.get(Account.class, new Long(id));
tx.commit(); //Actually I use tx.rollback()
session.close();
Due to the second level cache, result is fetched from cache after the first time reading from db. But the database connection is distributed when session.beginTransaction() is invoked, and next commit statement will be executed at database, which degrades the performance dramatically(compared to the case when no transaction is used).
So is there any possible way to know that the result will be read from second level cache so I can avoid doing commit and even use a new database connection?
Assuming you are using spring transaction support, have your tried using SUPPORTS propagation level?
#Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
Related
I would like to flush all the entites stored in the current transaction to the database (without ending the current transaction via commit).
Do I need to check if the transaction is Active before doing so?
if (this.entityTransaction.isActive())
{
this.entityManager.flush();
}
Thank you
According to the javadoc of the method flush of eclipselink's EntityManager class:
https://www.eclipse.org/eclipselink/api/2.6/javax/persistence/EntityManager.html#flush()
void flush()
Synchronize the persistence context to the underlying database.
Throws:
TransactionRequiredException - if there is no transaction or if the entity manager has not been joined to the current transaction
PersistenceException - if the flush fails
So yes it looks like you need to check, other ways you might get an exception of type "TransactionRequiredException"
I am working an a JPA 2.0 project, where I am saving my Entity class objects like :-
InitialContext ctx = new InitialContext();
UserTransaction userTrans = (UserTransaction)
ctx.lookup("java:comp/UserTransaction");
EntityManagerFactory emf = Persistence.createEntityManagerFactory(PERSISTENCE_NAME);
EntityManager em = emf.createEntityManager();
User user = new User("ankit","nigam",25);
em.persist(user); // persisted in db after this executes
userTrans.commit(); // whether it is required OR not.
So whether I am using userTrans.commit() or not, my user object is getting saved in Db, after persist() executes. But some of my colleagues say, as a standard we should commit() the transaction.
What should be the approach which I follow and whats the logic behind commit() and persist(). Please throw some lights.
Is autocommit ON in your DB? If it is then that is the reason why the changes get permanently stored in your DB irrespective of whether or not you commit the transaction from your application. In production the autocommit is generally set OFF because it hampers the performance/response time of the DB, that is why developers are generally encouraged to control the commit or rollback of a transaction from their application. The link details the command to handle autocommit in db2: http://www.db2util.com/administration/options-db2-command-line-disable-autocommit/
Can you have multiple transactions within one Hibernate Session?
I'm unclear if this is an allowable desirable. In my code I have a long running thread and takes items from a Blocking Queue, depending on what is on the queue, it may need to create and save a hibernate object, or it may not need to do anything.
Each item is distinct so if item 1 is saved and item 2 fails to save whatever reason I don't want to that to prevent item 1 being added to the database.
So the simplest way to do this is for each item that needs to be created to create a new session, open transaction, save new object, commit transaction, close session
However, that means a new session is created for each item, which seems to go against Hibernates own recommendations to not do Session Per Request Pattern. So my alternative was to create one session in the thread, then just open and commit a new transaction as required when needed to create a new object. But I've seen no examples of this approach and I'm unsure if it actually works.
The session-per-request pattern uses one JDBC connection per session if you run local transactions. For JTA, the connections are aggressively released after each statement only to be reacquired for the next statement.
The Hibernate transaction API delegates the begin/commit/rollback to the JDBC Connection for local transactions and to the associated UserTransaction for JTA. Therefore, you can run multiple transactions on the same Hibernate Session, but there's a catch. Once an exception is thrown you can no longer reuse that Session.
My advice is to divide-and-conquer. Just split all items, construct a Command object for each of those and send them to an ExecutorService#invokeAll. Use the returned List to iterate and call Future#get() to make sure the original thread waits after all batch jobs to complete.
The ExecutorService will make sure you run all Commands concurrently and each Command should use a Service that uses its own #Transaction. Because transactions are thread-bound you will have all batch jobs run in isolation.
Obviously, you can. A hibernate session is more or less a database connection and a cache for database objects. And you can have multiple successive transactions in a single database connection. More, when you use a connection pool, the connection is not closed but is recycled.
Whether you should or not is a matter of reusing objects from session. If there is a good chance but you can reuse objects that a preceding transaction has put in session, you should keep one single session for multiple transactions. But if once an object has been committed, it will never be re-used, it is certainly better to close the session and re-open a new one, or simply clear it.
How to do it :
If you have a Session object, you create transactions with :
Transaction transaction;
transaction = session.beginTransaction();
... (operations in the context of transaction)
transaction.commit();
... (other commands outside of any transaction)
transaction = session.beginTransaction();
... (and so on and so forth ...)
From hibernates documentation
"A Session is an inexpensive, non-threadsafe object that should be used once and then discarded for: a single request, a conversation or a single unit of work. A Session will not obtain a JDBC Connection, or a Datasource, unless it is needed. It will not consume any resources until used."
so if you are creating sessions again and again it will not burden the system much. If you are continuing a session for too long it may create problems as session is not thread safe .In my opinion you simplest solution is the best "So the simplest way to do this is for each item that needs to be created to create a new session, open transaction, save new object, commit transaction, close session"
By the way if you are creating single record of anything you dont need transaction too much. creating single record is inherently " all or none" thing for which we use transaction
package hibernate;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
class Tester {
public static void main(String[] args) {
SessionFactory sf = new org.hibernate.cfg.Configuration().configure().buildSessionFactory(new StandardServiceRegistryBuilder().configure().build());
Session session = sf.openSession();
session.beginTransaction();
Student student = new Student();
student.setName("Mr X");
student.setRollNo(13090);
session.save(student);
session.getTransaction().commit();
session.getTransaction().begin();
session.load(Student.class,23);
student.setName("New Name");
student.setRollNo(123);
session.update(student);
session.getTransaction().commit();
session.close();
}
}
Short answer is yes, you can use same session for transaction. Take a look at org.hibernate.Transaction., it has required method to manage transaction.
docs.jboss.org Chapter 13. Transactions and Concurrency
Use a single database transaction to serve the clients request, starting and committing it when you open and close the Session. The relationship between the two is one-to-one and this model is a perfect fit for many applications.
It seemed we should always obey the "one-to-one relationship" rule.
But, although the sample below will trigger a exception in the line where the second "session.beginTransaction()" is called
Exception in thread "main" java.lang.IllegalStateException: Session/EntityManager is closed
private static void saveEmployees(SessionFactory factory) {
// crate session
//Session session = factory.openSession();
Session session = factory.getCurrentSession();
{
// start a transaction
Transaction trans = session.beginTransaction();
// create an employee
Employee tempEmployee = new Employee("Steve","Rogers", "The Avengers");
// save to database
session.save(tempEmployee);
// commit the transaction
trans.commit();
}
{
// start a transaction
Transaction trans = session.beginTransaction();
// create an employee
Employee tempEmployee = new Employee("Tony","Stark", "The Avengers");
// save to database
session.save(tempEmployee);
// commit the transaction
trans.commit();
}
// close session
session.close();
}
, another sample below will work properly.
The only difference is that the second sample uses "factory.openSession()" to get a session, instead of "factory.getCurrentSession()".
private static void saveEmployees(SessionFactory factory) {
// crate session
Session session = factory.openSession();
//Session session = factory.getCurrentSession();
{
// start a transaction
Transaction trans = session.beginTransaction();
// create an employee
Employee tempEmployee = new Employee("Steve","Rogers", "The Avengers");
// save to database
session.save(tempEmployee);
// commit the transaction
trans.commit();
}
{
// start a transaction
Transaction trans = session.beginTransaction();
// create an employee
Employee tempEmployee = new Employee("Tony","Stark", "The Avengers");
// save to database
session.save(tempEmployee);
// commit the transaction
trans.commit();
}
// close session
session.close();
}
I am a starter, and I don't know why "factory.getCurrentSession()" works differently from "factory.openSession()", yet.
As we all know that in Hibernate if no transaction commit, the changes won't affect in database. But I found something weird. And the code as follows:
ApplicationContext ctx = new ClassPathXmlApplicationContext("Spring.xml");
SessionFactory sessionFactory = (SessionFactory) ctx.getBean("sessionFactory");
Session session = sessionFactory.openSession();
Model model = new Model();
...
session.save(model);
session.flush();
session.close();
And the model was saved to database even there's no transaction, anyone can explain this?
Any comments would be appreciated! Thanks!
PS: I am using mysql.
The session.flush command saved the transaction. If it's wrong, you should use transaction.
usually hibernate needs the line session.beginTransaction(); to work. You didn't write that and your application worked, I guess your application runs in an Application server, which provides transaction management. e.g. jboss, weblogic...
However it doesn't mean that there is no transaction. Did you set auto-commit true?
btw, session.flush() and txn.commit() are different.
Flushing is the process of synchronizing the underlying persistent store with persistable state held in memory.
After session.flush(), you still can call txn.rollback() to rollback all changes.
edit
oh I saw you used spring. did you configured txnmanager in spring?
Hibernate doesn't need transactions, the most common problems in database-based applications are just easier to solve with transactions which is why usually everyone uses transactions with Hibernate. But that's mere coincidence/convention/laziness.
All Hibernate needs is a java.sql.Connection and if your container provides one even though there is no current transaction manager configured, Hibernate is fine with that.
In fact, Hibernate has no idea that there might be a transaction manager. So session.flush() will use the ApplicationContext to get a connection, generate the SQL and use JDBC to send the generated SQL code to the database.
From Hibernate's point of view, that's all that happens.
There can be several reasons why the data is committed to the database:
You forgot to turn of auto commit on the connection.
Your web container / spring config automatically wires a transaction manager that synchronizes with HTTP requests.
Your code is called form another method which is annotated with #Transactional; in this case, you inherit the existing transaction.
I've read somewhere that when a session is flushed or a transaction is committed, the session itself is closed by Hibernate. So, how can i reuse an Hibernate Session, in the same thread, that has been previously closed?
Thanks
I've read somewhere that when a session is flushed or a transaction is committed, the session itself is closed by Hibernate.
A flush does not close the session. However, starting from Hibernate 3.1, a commit will close the session if you configured current_session_context_class to "thread" or "jta", or if you are using a TransactionManagerLookup (mandatory JTA) and getCurrentSession().
The following code illustrates this (with current_session_context_class set to thead here):
Session session = factory.getCurrentSession();
Transaction tx = session.beginTransaction();
Product p = new Product();
session.persist(p);
session.flush();
System.out.println(session.isOpen()); // prints true
p.setName("foo");
session.merge(p);
tx.commit();
System.out.println(session.isOpen()); // prints false
See this thread and the section 2.5. Contextual sessions in the documentation for background on this.
So, how can i reuse an Hibernate Session, in the same thread, that has been previously closed?
Either use the built-in "managed" strategy (set the current_session_context_class property to managed) or use a custom CurrentSessionContext derived from ThreadLocalSessionContext and override ThreadLocalSessionContet.isAutoCloseEnabled().
Again, see the above links and also What about the extended Session pattern for long Conversations?
Wrong. The session is stays open, just a new transaction begins. The main thing is that all objects currently attached to the session STAY attached, so if you don't clear the session you have a memory leak here.