I have way too many threads being used. I keep running out of memory in my unit tests. Do I need to close my session if I'm using sessionFactory. Won't the commit below end the session?
Session session = sessionFactory.getCurrentSession();
Transaction transaction = null;
try
{
transaction = session.beginTransaction();
transaction.commit();
}
catch (Exception e)
{
if (transaction != null)
{
transaction.rollback();
throw e;
}
}
finally
{
//Is this close necessary?
session.close();
}
No, it won't end the session. One session can span any number of transactions. Close the session explicitly. BTW such things are clearly documented.
In yout catch, verify if the transaction isActive() too.
Related
In Java Hibernate, when we need to do something with DB we need:
1. Open session
2. Begin transaction
3. Finish transaction
4. Close session
For example if I want to get Student list:
public static List<Student> getStudentList()
{
List<Student> l = null;
Session session = HibernateUtil.getSessionFactory().openSession();
try {
String hql = "from Student";
Query query = session.createQuery(hql);
l = query.list();
} catch (HibernateException ex) {
//Log the exception
System.err.println(ex);
} finally {
session.close();
}
return l;
}
Insert a student
public static boolean addStudent(Student s)
{
Session session = HibernateUtil.getSessionFactory().openSession();
if (... /* check if student is already exists*/)
{
return false;
}
Transaction transaction = null;
try {
transaction = session.beginTransaction();
session.save(s);
transaction.commit();
} catch (HibernateException ex) {
//Log the exception
transaction.rollback();
System.err.println(ex);
} finally {
session.close();
}
return true;
}
Why there is no transaction in getStudentList()? Thank in advance
This hibernate article explains the behavior of SELECT operations which are not explicitly executed within a transaction.
The following example is given in the article but it's also true for your query example.
Session session = sessionFactory.openSession();
session.get(Item.class, 123l);
session.close();
A new Session is opened. It doesn’t obtain a database connection at this point.
The call to get() triggers an SQL SELECT. The Session now obtains a JDBC Connection from the connection pool. Hibernate, by default, immediately turns off the autocommit mode on this connection with setAutoCommit(false). This effectively starts a JDBC transaction!
The SELECT is executed inside this JDBC transaction. The Session is closed, and the connection is returned to the pool and released by Hibernate — Hibernate calls close() on the JDBC Connection. What happens to the uncommitted transaction?
The answer to that question is, “It depends!” The JDBC specification doesn’t say anything about pending transactions when close() is called on a connection. What happens depends on how the vendors implement the specification. With Oracle JDBC drivers, for example, the call to close() commits the transaction! Most other JDBC vendors take the same route and roll back any pending transaction when the JDBC Connection object is closed and the resource is returned to the pool.
Obviously, this won’t be a problem for the SELECT [...]
Lets extend the above example to provoke a possible problem.
Session session = sessionFactory.openSession();
Item item = session.get(Item.class, 123l);
item.setPrice(10);
session.close();
Now it depends on the JDBC driver if the new price for the Item is persisted or not.
So you can neglect begin and commit transactions on pure SELECT operations even when your JDBC driver will rollback the transaction as long as you have no database changes.
But anyway I would strongly recommend using transactions on any operation to avoid misunderstanding and problems.
The method shown below is not fetching data from db with the criteria strProductId. I'm getting the value of strProductId inside the method. Can anyone please help..Thanks in advance....
public List<ProductServices> getAllServices(String strProductId){
Session session = sessionFactory.getCurrentSession();
Criteria cr = session.createCriteria(ProductServices.class);
cr.add(Restrictions.eq("productId", strProductId));
return (List<ProductServices>) cr.list();
}
Method getAllServices must be in a transaction. Check it, please.
Updated
You must open a transaction, do a request and close a transaction. It can be done by Spring of course.
See this example. UserManagerImpl has a #Transactional annotation on methods.
you can do some thing like this
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Criteria cr = session.createCriteria(ProductServices.class);
cr.add(Restrictions.eq("productId", strProductId));
return (List<ProductServices>) cr.list();
tx.commit();
}
catch (Exception e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
}finally {
session.close();
}
Probably this is stupid or weird question but I'm facing a strange bug in the app for many weeks and do not know how to solve it.
There are some Quartz jobs that periodically updating database (change the status of orders and stuff). There is only Hibernate 3.1.3 without spring and all transactions handled in code manually with explicit call session.close() in finally block.
It all worked fine with Hibernate built-in connection pool, however after I have changed built-in connection pool to c3p0 pool there are some bugs appeared connected with database transaction/session management. There is no exceptions or anything in the log files, so it is hard to tell what exactly was the reason.
Is there any way to make c3p0 connection pool behave like built-in pool by configuration? I need only one c3p0 feature - checking dead connections. Before this pool was implemented there was idle SQL Connection Reset issue. To keep connections alive I decided to use c3p0 pooling instead of built-in.
here is my current c3p0 configuration:
c3p0.min_size=0
c3p0.max_size=100
c3p0.max_statements=0
c3p0.idle_test_period=600
c3p0.testConnectionOnCheckout=true
c3p0.preferredTestQuery=select 1 from dual
c3p0.autoCommitOnClose=true
c3p0.checkoutTimeout=120000
c3p0.acquireIncrement=2
c3p0.privilegeSpawnedThreads=true
c3p0.numHelperThreads=8
Thanks in advance.
c3p0 is an old version: 0.9.0.4, because I have Java 1.4 environment.
Here is a rough example how transactions are managed by the code:
SessionFactory sessionFactory = null;
Context ctx = null;
Object obj = null;
ctx = new InitialContext();
obj = ctx.lookup("HibernateSessionFactory");
sessionFactory = (SessionFactory) obj;
session = sessionFactory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
// some transactional code
if (!tx.wasRolledBack() && !tx.wasCommitted()) {
tx.commit();
}
} catch (Exception ex) {
if (tx != null && !tx.wasRolledBack() && !tx.wasCommitted()) {
// if it is runtime exception then rollback
if (ex instanceof RuntimeException) {
logger.log(Level.ERROR, "10001", ex);
try {
tx.rollback();
} catch (RuntimeException rtex) {
logger.log(Level.ERROR, "10002", rtex);
}
}
else {
// attempt to commit if there are any other exceptions
try {
tx.commit();
} catch (RuntimeException rtex) {
logger.log(Level.ERROR, "10004", rtex);
// if commit fails then try to rollback
try {
tx.rollback();
} catch (RuntimeException rtex2) {
logger.log(Level.ERROR, "10002", rtex2);
}
// then rethrow this exception
throw rtex;
}
}
}
finally {
session.close();
}
I am using jersey with mysql and hibernate 4 and c3p0. I have created a initialization servlet that configures hibernate and sets the current session context class to thread.
I have created hibernateUtils class that contains static methods for getting and committing sessions and I am using a filter to start a session on inbound request and commit it upon response.
The problem is that at some random intervals I am getting org.hibernate.TransactionException: nested transactions not supported exception, but I am not trying to create a new session except on the filter.
Correct me if I am wrong but when setting the current session class to thread I don't need to create a threadlocal in the hibernateutil, hibernate does this. So my question is, is this a safe way to handle that and what could cause the error happening on random intervals?
====================== EDIT ===========================
Sorry for not posting the code earlier.
So the filter implements the ContainerRequestFilter,ContainerResponseFilter
in the request filter I am doing
Session session = sessionfactory.getCurrentSession();
session.getTransaction().begin();
session.setDefaultReadOnly(readOnly);
and in the response
Transaction transaction = sessionfactory.getCurrentSession().getTransaction();
try {
if (transaction != null && !transaction.wasCommitted()
&& !transaction.wasRolledBack() && transaction.isActive()) {
transaction.commit();
}
} catch (HibernateException e) {
Transaction transaction = sessionfactory.getCurrentSession().getTransaction();
try {
if (transaction != null && transaction.isActive()) {
transaction.rollback();
}
} catch (HibernateException e) {
} finally {
Session session = sessionfactory.getCurrentSession();
try {
if (session != null && session.isOpen()) {
session.close();
}
} catch (HibernateException e) {
log.error("Closing session after rollback error: ", e);
throw e;
}
}
It seems that you are using programmatic transaction demarcation in your filter (as far as I understood). So please double check that you terminate properly each transaction, nevermind what append during the request (i.e. rollback if you get an exception and commit otherwise) :
try {
session.getTransaction().begin();
// call the filter chain
session.getTransaction().commit()
}
catch (RuntimeException e) {
session.getTransaction().rollback();
}
Without the code it's difficult to be sure, but I guess that for some request you didn't terminate the transaction properly (i.e. by a commit or by a rollback). So the transaction remains associated to the thread and the thread go back to the thread pool (in a very strange state since there is still a transaction associated with it), then another request reuse the same thread, a new transaction is created in the filter... and you got the exception.
EDIT
After looking carrefully at your code, it (may) confirms my assumptions.
Look at the flow when transaction.wasRolledBack()==true : it won't be commited nor rolled back.
And if you the javadoc for Transaction.wasRolledBack() :
Was this transaction rolled back or set to rollback only?
If the transaction is marked as "RollBack only" : it will return true, but it don't means that the transaction is ended. It means that the only possible ending state for the transaction is "RollBack".
But, on the other hand the same javadoc also say this:
Returns: boolean True if the transaction was rolled back via this local transaction; false otherwise.
I found that ambiguous.
So I suggest you to do this:
if (transaction != null && !transaction.wasCommitted()
&& !transaction.wasRolledBack() && transaction.isActive()) {
transaction.commit();
}else if(transaction.wasRolledBack()){
transaction.rollback();
}
I'm trying to write DAOs for my database models using the transaction pattern like such,
Session session = null;
Transaction tx = null;
try{
session = HibernateUtil.getSessionFactory().openSession();
tx = session.beginTransaction();
tx.setTimeout(5);
//doSomething(session);
tx.commit();
}catch(RuntimeException e){
try{
tx.rollback();
}catch(RuntimeException rbe){
log.error("Couldn’t roll back transaction", rbe);
}
throw e;
}finally{
if(session!=null){
session.close();
}
}
What's a good approach to encapsulate this pattern in a method with
//doSomething(session);
as an argument to be performed as part of the transaction? Sometimes I run a query, sometimes I operate on session.saveOrUpdate, etc. I have many DAOs to write and this pattern of code duplication is bothering me.
EDIT
Is there a direct mapping between session operations and HQL (saveOrUpdate, delete, etc) so all I need to pass into this method is just a query?
Thanks for the insights.
Something like this might be what you're after
public void doSomething(MyQuery myQuery) {
...
Transaction tx = null;
try {
...
myQuery.execute(tx);
...
} catch (...) {
} finally {
}
}
public class MyQuery {
public void execute(Transaction tx) {
// run queries on transaction
}
}
Either create a new MyQuery instance or a new MyQuery subclass for each query or set of queries you want to execute