I´m starting with Hibernate.
Since I read that I need to configure the pool connection I start using C3P0
everything is fine, but when I reach the maximum connections the application freezes, I have to close the application and start it again.
this is my C3P0 part in hibernate.cfg.xml
<property name="hibernate.c3p0.min_size">1</property>
<property name="hibernate.c3p0.max_size">15</property>
<property name="hibernate.c3p0.timeout">3000</property>
<property name="hibernate.c3p0.max_statements">20</property>
<property name="hibernate.c3p0.idle_test_period">300</property>
in my function when I save the object I close the session.
public void Save item(Item item)throws Exception{
try{
SessionFactory sf= NewHibernateUtil.getSessionFactory();
Session session;
session = sf.openSession();
Transaction tx= session.beginTransaction();
session.save(item);
tx.commit();
session.close();
}
catch(Exception ex){
throw new Exception(ex);
}
}
if I check the connections in MySql I see that all the connections are sleep, but the application freezes.
what am I missing here?
You are not reliably close()ing Connections (wrapped within Sessions), so they leak. Consider what happens when an Exception occurs.
Either use Java 7+ try-with-resources, if Session supports that, or use the old robust cleanup idiom, see the Appendix to my answer here.
If you fix this and are still experiencing a Connection leak, c3p0 has configuration parameters to help you track down the leak.
Related
I have a spring managed web app where we're using Hibernate. In my hibernate.cfg.xml we have
<property name="hibernate.current_session_context_class">thread</property>
It's also configured to use HikariCP connection pooling.
When we open sessions, we follow the paradigm of this. Note that I'm not explicitly closing the session.
Transaction tx = null;
Session session = sessionFactory.getCurrentSession();
try {
tx = session.beginTransaction();
// do DB stuff.
tx.commit(); // or, sometimes session.getTransaction().commit();
} catch (Exception e) {
if (tx != null) {
tx.rollback();
}
throw new CustomDbException(e);
}
In some cases, when closing the session, we do session.getTransaction().commit() at a minimum, this is incosistent, but I'm not sure if it's giving a different trasancation or otherwise causing some problems.
We've been seeing intermittent issues with LockAcquisitionException: could not execute statement, StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect), and TransactionException: nested transactions not supported. We're not trying to do nested exceptiosn and always clean up the transaction, but I'm starting to think that maybe the sessions is being shared.
The application is multi threaded, and I'm starting to suspect that getCurrentSession is actually being shread across threads. I was under the impression that by setting hibernate.current_session_context_class to thread, then getCurrentSession() would provide a separate session per thread. Should I be taking a different approach here? Should I be using openSession() instead, or somehow configuring hibernate to give me unique sessions per thread?
We use #JmsListener methods, and the transactional exceptions seem most prevalent there, but are not localized there.
Exact hibernate versions are as follows:
hibernate: [ 'org.hibernate.common:hibernate-commons-annotations:4.0.4.Final',
'org.hibernate:hibernate-core:4.3.11.Final',
'org.hibernate:hibernate-hikaricp:4.3.11.Final'],
hikari: 'com.zaxxer:HikariCP:2.4.1',
I am facing one weird issue when I update the table and after a couple of seconds when I try to fetch that then I still receive the old data. When I again fetch with same query after couple of second then I receive refreshed data. Basically what I see is it takes some time to return the fresh data.
I have disabled all caching from hibernate also while fetching I am making session.clear() and marked query as uncachable.
I also look into mysql query log and I figured out that hibernate is querying to mysql, but I am receiving old data.
How can I make sure that at any given point of time I receive refreshed data only
Below is my hibernate config file
<hibernate-configuration>
<session-factory>
<property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<property name="show_sql">true</property>
<property name="connection.url">jdbc:mysql://127.0.0.1:4804/aluminidb?autoReconnect=true</property>
<property name="connection.username">root</property>
<property name="connection.password">root</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- Example mapping file inclusion -->
<property name="hibernate.cache.use_second_level_cache">false</property>
<property name="hibernate.cache.use_query_cache">false</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<mapping resource="com/alumini/spring/model/Alumini.hbm.xml"/>
<mapping resource="com/alumini/spring/model/Question.hbm.xml"/>
<mapping resource="com/alumini/spring/model/Events.hbm.xml"/>
</session-factory>
Below is the code to fetch the object
#Override
public Alumini login(String email, String password) {
Session session=sessionFactory.openSession();
session.clear();
Transaction t;
try{
t=session.beginTransaction();
Query query = session.getNamedQuery("chkLogIn");
query.setParameter("email",email);
query.setParameter("password",password);
query.setCacheMode(CacheMode.REFRESH);
query.setCacheable(false);
List<Alumini> aluminiList=query.list();
if(aluminiList!=null && aluminiList.size()>0){
System.out.println(aluminiList.get(0).getLastUpdated());
t.commit();
return aluminiList.get(0);
}else{
t.rollback();
return null;
}
}finally{
session.close();
}
}
So I am clearing the session, also in my config I have set all cache disabled. still when I update the record and if with in couple of seconds if I fetch the record using above method then I receive old data for once. After that it gives me latest data.
If some entities are loaded in the current Session and you run a native query, the Session might not flush automatically.
Hibernate Session offers application-level repeatable reads, so if other database transaction changes an entity, Hibernate will not refresh the current entity states.
Now, since you did not post the UPDATE part, it's hard to tell what you are doing there. The best way to address this issues is to simply log all JDBC statements as explained in this article. Then, you will know for sure whether the update was executed or not.
More, the way you do transaction and Session management is flawed as well. You don't even rollback in a finally block, and since you are using MySQL, this can lead to locks being held and causing deadlocks.
Just use a framework like Spring or Java EE to handle the Persistence Context and transaction management for you.
In your example:
Session session=sessionFactory.openSession();
session.clear();
How can one tell whether this is a new Session, and calling clear would not make any sense, or it is the same Session you used for the update?
From this code, I would assume that this is the case:
if(aluminiList!=null && aluminiList.size()>0){
System.out.println(aluminiList.get(0).getLastUpdated());
t.commit();
return aluminiList.get(0);
}else{
t.rollback();
return null;
}
But it points out that you might have skipped the Hibernate User Guide and jumped to coding Hibernate.
The aluminiList can never be null. It can only be empty.
Logging via System.out is wrong. Use a Logging framework for that.
Why do you want to commit after the query was executed? Maybe the change was not flushed at all and the query did not trigger the flush because either you set the FlushMode.MANUAL or the query is a native SQL, not a JPQL. Check out this article for more details about the difference.
You call rollback on else? What's the point? You don't trust the database that it issued the UPDATE properly and now you want to roll back that change. Or, you suspect that Hibernate did not flush, but then, why do you would you roll it back if the change didn't happen, right? But if it happened, then you should read-your-writes because that's how ACID isolation levels work.
All in all, there are many issues in the code that you posted. So, read the Hibernate User Guide and these tutorials, and you will fix all your issues. There's no other way.
I have a web application that currently uses c3p0 and Hibernate to connect to a Firebird 1.5 database.
I am facing a problem from time to time where the database just stops responding, even trying to manually restart the service doesn't have any effect, and it doesn't generate any logs, so I have to manually reboot the machine to get it working again.
I think that maybe Firebird hangs when the pool tries to acquire a specific number of connections or something like that. So, I need to test my app without connection pooling, to check if this is or is not the problem.
I can't simply remove c3p0 configs from persistence because this way Hibernate would use its own integrated connection pool. So how to do it?
The most flexible solution is to use an explicit DataSource, instead of configuring the connection pooling through Hibernate. One option to configure a non-pooling DataSource is by using DriverManagerDataSource:
#Override
protected Properties getProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
//log settings
properties.put("hibernate.hbm2ddl.auto", "update");
//data source settings
properties.put("hibernate.connection.datasource", newDataSource());
return properties;
}
protected ProxyDataSource newDataSource() {
DriverManagerDataSource actualDataSource = new DriverManagerDataSource();
actualDataSource.setUrl("jdbc:hsqldb:mem:test");
actualDataSource.setUsername("sa");
actualDataSource.setPassword("");
ProxyDataSource proxyDataSource = new ProxyDataSource();
proxyDataSource.setDataSource(actualDataSource);
proxyDataSource.setListener(new SLF4JQueryLoggingListener());
return proxyDataSource;
}
This way you can choose a pooling or a non-pooling DataSource.
To get a better understanding of you connection pooling resources usage, you can configure FlexyPool to collect metrics for:
concurrent connections
concurrent connection requests
data source connection acquiring time
connection lease time
maximum pool size
total connection acquiring time
overflow pool size
retries attempts
I found documentation for hibernate 3.3 and 4.3 that says:
Just replace the hibernate.connection.pool_size property with
connection pool specific settings. This will turn off Hibernate's
internal pool.
Hibernate will use its org.hibernate.connection.C3P0ConnectionProvider
for connection pooling if you set hibernate.c3p0.* properties
So remove hibernate.connection.pool_size and any hibernate.c3p0... properties from configuration, than connection pooling is disabled.
Adding to Vlad's answer:
If somebody still faces this:
Be sure to remove "hibernate-c3p0" from your classpath, if exists, since this will automatically enable MChange c3p0 connection pool.
Another option that, you can close the connection manually when closing the entity manager:
....
SessionImpl ses = (SessionImpl) session;
close(ses.connection());
try {
session.close();
} catch (Exception e) {
logger.error(e);
}
........
Note: the above manual closing will work with the default pool of hibernate, not hibernate default one.
Good Luck
I'm writing a simple project, a business app written in Swing, using Hibernate for back-end. I come from Spring, that gave me easy ways to use hibernate and transactions. Anyway I managed to have Hibernate working. Yesterday, while writing some code to delete a bean from DB, I got this:
org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions
The deletion code is simply:
Session sess = HibernateUtil.getSession();
Transaction tx = sess.beginTransaction();
try {
tx.begin();
sess.delete(ims);
} catch (Exception e) {
tx.rollback();
throw e;
}
tx.commit();
sess.flush();
and my HibernateUtil.getSession() is:
public static Session getSession() throws HibernateException {
Session sess = null;
try {
sess = sessionFactory.getCurrentSession();
} catch (org.hibernate.HibernateException he) {
sess = sessionFactory.openSession();
}
return sess;
}
additional details: I never close a hibernate session in my code, just on application closing. Is this wrong? Why do I get this on delete (only for that bean, others do work), and I don't on other operations (Insert, query, update)?
I read around and I tried to modify my getSession method simply in a sessionFactory.getCurrentSessionCall(), but I got: org.hibernate.HibernateException: No CurrentSessionContext configured!
Hibernat conf:
<hibernate-configuration>
<session-factory >
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost/joptel</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">******</property>
<property name="hibernate.connection.pool_size">1</property>
<property name="show_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
..mappings..
</session-factory>
</hibernate-configuration>
I wanted to ask you one thing, why are you trying to use "OpenSession" method?
public static Session getSession() throws HibernateException {
Session sess = null;
try {
sess = sessionFactory.getCurrentSession();
} catch (org.hibernate.HibernateException he) {
sess = sessionFactory.openSession();
}
return sess;
}
You don't have to call openSession(), because getCurrentSession() method is always returns current session (Thread in case if you have configured it to be).
I got it!...
You have to specify current context in your hibernate.cfg.xml file
it should be:
<property name="hibernate.current_session_context_class">thread</property>
No CurrentSessionContext configured
Read the reference guide on Contextual Sessions. You're required to configure some provided or custom strategy for this. In a hibernate.cfg.xml, you'd configure it with
<property name="hibernate.current_session_context_class">...</property>
You'd probably want to use "thread" as the value to get per-thread sessions. When using Spring, it automatically sets this to a SpringSessionContext, allowing Spring to easily integrate Hibernate with its transaction management framework.
I come from Spring, that gave me easy ways to use hibernate and transactions.
If you're familiar with Spring, why aren't you using it to manage Hibernate here? You must already know how simple and foolproof it makes it.
I never close a hibernate session in my code, just on application closing. Is this wrong?
Yes, this is very wrong. Every session not closed is an open database connection, so your app is currently hemorrhaging connections.
Illegal attempt to associate a collection with two open sessions
That means exactly what it says. You tried to do some persistence operation (save(), update(), delete()) on something that was already associated to a different session. That's what will happen when you go randomly opening new sessions whenever, which is what's happening since SessionFactory.getCurrentSession() will always fail when no "current session context" is set. In general, never open a session just because one wasn't already there. You need to have well-defined strategies for opening and closing sessions and never let anything open a session outside of these "strategies". That's a sure path to resource leaks and errors like the one you've encountered.
I faced the same problem when I am working on a portal where I am using spring remoting with hibernate.
This kind of problem arise only if when the called service method contains multiple DAO calls that hit database with hibernate session.
And the solution is set the #Transaction annotation for those methods with multiple DAO calls. (Implies all the DOA calls with in this method should be under one transaction.)
I am having a problem getting a JDBC connection in an EJB SessionBean. The error is:
org.jboss.util.NestedSQLException: Could not enlist in transaction on entering meta-aware object!; - nested throwable: (javax.transaction.SystemException: java.lang.Throwable: Unabled to enlist resource, see the previous warnings.
I thought this happens, because I already have an open connection from a different datasource, so I configured an XA datasource to avoid transaction problems, but it doesn't work at all, so I don't know if I am doing something wrong in my code. Here it is:
try
{
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY,"org.jnp.interfaces.NamingContextFactory");
p.put(Context.PROVIDER_URL,"jnp://localhost:11099");
p.put("java.naming.factory.url.pkgs", "org.jboss.naming");
InitialContext ic = new InitialContext(p);
DataSource dataSource = (DataSource)ic.lookup("java:/jdbc/etlreportservices");
return dataSource.getConnection();
}
catch(Exception e)
{
e.printStackTrace();
}
The exception is thrown while calling dataSource.getConnection().
Can try,
for old Jboss-es:
/server/all/conf/jbossjta-properties.xml
<properties depends="arjuna" name="jta">
<property name="com.arjuna.ats.jta.allowMultipleLastResources" value="true"/>
</properties>
for new:
standalone\configuration\standalone.xml (or other what you use)
<system-properties>
<property name="com.arjuna.ats.arjuna.allowMultipleLastResources" value="true"/>
</system-properties>
I have noticed this in cases where the tx times out. FWIW.
Using JBoss 6.0.0, the error message is slightly different:
Caused by: org.jboss.resource.JBossResourceException: Could not enlist in transaction on entering meta-aware object!
As for the reason: A quote from here
Within the same process, two calls were being made to different non-XA data sources. This is not supported by default on JBoss.
The same site shows a solution which was not applicable for JBoss 6.0.0.
The general solution is to change all data sources involved in the same transaction into XA data sources. Then it works both with bean managed and container managed transactions. For example, this solution is proposed in a CodeRanch and in a JBoss forum as well.
I changed my transaction manager to be bean-managed and it works perfectly.