Search code examples
javahibernatederby

Hibernate with Derby: Cannot close a connection while a transaction is still active


I am using Hibernate (trying to learn) and Derby; I create and inspect the database using Squirrel and an SQL script, and am writing 3 records to it in my program.

I am getting "Cannot close a connection while a transaction is still active"; it seems to appear during the configuration of things for hibernate, but the records I'm creating in the database get written there anyway. I've tried the non-"40" driver, I've put username and password in as URL parameters instead of on their own in the hibernate configuration, I cannot figure out what other transaction might be running. I've put a breakpoint at the call to create() in the loop, and by the time it is hit, the error has already appeared in the console, so it isn't anything about the records I'm writing out. Anyone have other ideas here?

Here's the main program code:

package samplerc;

import sample.CreateUser;

public class CreateRecords
{
  public static void main(String[] args)
  {
    try
    {
      String[][] userList = 
        { { "rc", "1" },
          { "pbh", "2" },
          { "jbc", "3" }
        };

      CreateUser userCreator = new CreateUser();
      userCreator.addUserList(userList);
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }

  }

}

and here's CreateUser:

package sample;

import org.hibernate.HibernateException;
import sample.dao.DAO;
import sample.entity.User;

public class CreateUser extends DAO
{

  public void addUserList(String[][] userList)
    throws Exception
  {

      try
      {
        for (int i=0; i<userList.length; i++)
        {
          create(userList[i][0], userList[i][1]);
          System.out.println("Created user");
          DAO.close();
        }
      }
      catch (Exception e)
      {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
  }

  public User create(String username, String password) throws Exception
  {
    try
    {
      begin();
      User user = new User(username, password);
      getSession().save(user);
      commit();
      return user;
    }
    catch (HibernateException e)
    {
      throw new Exception("Could not create user " + username, e);
    }
  }
}

the DAO base class used to be here; it is now below in an edit because I replaced it:

the hibernate config file:

    <!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

    <hibernate-configuration>
      <session-factory>
        <property name="hibernate.connection.url"> jdbc:derby://localhost:1527/C:/Users/rcook/workspaceGalileoLaptop/BHChap3/BegHibernateDB</property>
        <property name="hibernate.connection.driver_class"> org.apache.derby.jdbc.ClientDriver40 </property>
        <property name="hibernate.connection.username">admin</property>
        <property name="hibernate.connection.password">admin</property>
        <property name="hibernate.dialect">   org.hibernate.dialect.DerbyDialect  </property>

        <property name="hibernate.connection.pool_size">0</property>
        <property name="hibernate.show_sql">false</property>

        <!-- "Import" the mapping resources here -->
        <mapping class="sample.entity.Message"/>
        <mapping class="sample.entity.Advert"/>
        <mapping class="sample.entity.Category"/>
        <mapping class="sample.entity.User"/>

      </session-factory>
    </hibernate-configuration>

The log file used to be here; due to posting restrictions, I've removed it since I've got another log in the edit below.

-- updated after Guido's answer

Guido, that was such an excellent idea I was certain it was right. Unfortunately the change I made doesn't seem to fix it. Here's the new DAO class, with your suggested change:

package sample.dao;

import java.util.logging.Level;
import java.util.logging.Logger;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class DAO
{
  private static final Logger         log = Logger.getAnonymousLogger();
  private static       SessionFactory sessionFactory = null;
  private static final ThreadLocal    threadLocalSession = new ThreadLocal();

  protected DAO()
  {
  }

  private static synchronized SessionFactory getSessionFactory()
  {
    if (sessionFactory == null)
    {
      sessionFactory = new Configuration().configure().buildSessionFactory();
    }
    return sessionFactory;
  }

  public static Session getSession()
  {
    String threadName = Thread.currentThread().getName();
    System.out.println(String.format("getting session, thread = '%s'", threadName));
    Session localSession = (Session) DAO.threadLocalSession.get();
    if (localSession == null)
    {
      try
      {
        localSession = getSessionFactory().openSession();
        DAO.threadLocalSession.set(localSession);
      }
      catch (HibernateException e)
      {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    }
    return localSession;
  }

  protected void begin()
  {
    try
    {
      getSession().beginTransaction();
    }
    catch (HibernateException e)
    {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }

  protected void commit()
  {
    try
    {
      getSession().getTransaction().commit();
    }
    catch (HibernateException e)
    {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }

  protected void rollback()
  {
    try
    {
      getSession().getTransaction().rollback();
    }
    catch (HibernateException e)
    {
      log.log(Level.WARNING, "Cannot rollback", e);
    }

    try
    {
      getSession().close();
    }
    catch (HibernateException e)
    {
      log.log(Level.WARNING, "Cannot close", e);
    }
    DAO.threadLocalSession.set(null);
  }

  public static void close()
  {
    getSession().close();
//    try { Thread.sleep(5000); } catch (InterruptedException ie) {}
    DAO.threadLocalSession.set(null);
  }
}

And here's the new log file. I stuck in a trace statement and find it's output interesting vis-a-vis the rest of the output. ANY other ideas you (or anyone else) have would be welcome.

Starting...
  getting session, thread = 'main'
  Feb 19, 2013 10:25:21 AM org.hibernate.annotations.common.Version <clinit>
  INFO: Hibernate Commons Annotations 3.2.0.Final
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.Environment <clinit>
  INFO: Hibernate 3.6.10.Final
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.Environment <clinit>
  INFO: hibernate.properties not found
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.Environment buildBytecodeProvider
  INFO: Bytecode provider name : javassist
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.Environment <clinit>
  INFO: using JDK 1.4 java.sql.Timestamp handling
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.Configuration configure
  INFO: configuring from resource: /hibernate.cfg.xml
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.Configuration getConfigurationInputStream
  INFO: Configuration resource: /hibernate.cfg.xml
  Feb 19, 2013 10:25:21 AM org.hibernate.util.DTDEntityResolver resolveEntity
  WARNING: recognized obsolete hibernate namespace http://hibernate.sourceforge.net/. Use namespace http://www.hibernate.org/dtd/ instead. Refer to Hibernate 3.6 Migration Guide!
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.Configuration doConfigure
  INFO: Configured SessionFactory: null
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.AnnotationBinder bindClass
  INFO: Binding entity from annotated class: sample.entity.Message
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.annotations.EntityBinder bindTable
  INFO: Bind entity sample.entity.Message on table Message
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.AnnotationBinder bindClass
  INFO: Binding entity from annotated class: sample.entity.Advert
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.annotations.EntityBinder bindTable
  INFO: Bind entity sample.entity.Advert on table Advert
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.AnnotationBinder bindClass
  INFO: Binding entity from annotated class: sample.entity.Category
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.annotations.EntityBinder bindTable
  INFO: Bind entity sample.entity.Category on table Category
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.AnnotationBinder bindClass
  INFO: Binding entity from annotated class: sample.entity.User
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.annotations.EntityBinder bindTable
  INFO: Bind entity sample.entity.User on table AdUser
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.Configuration applyHibernateValidatorLegacyConstraintsOnDDL
  INFO: Hibernate Validator not found: ignoring
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.search.HibernateSearchEventListenerRegister enableHibernateSearch
  INFO: Unable to find org.hibernate.search.event.FullTextIndexEventListener on the classpath. Hibernate Search is not enabled.
  Feb 19, 2013 10:25:21 AM org.hibernate.connection.DriverManagerConnectionProvider configure
  INFO: Using Hibernate built-in connection pool (not for production use!)
  Feb 19, 2013 10:25:21 AM org.hibernate.connection.DriverManagerConnectionProvider configure
  INFO: Hibernate connection pool size: 0
  Feb 19, 2013 10:25:21 AM org.hibernate.connection.DriverManagerConnectionProvider configure
  INFO: autocommit mode: false
  Feb 19, 2013 10:25:21 AM org.hibernate.connection.DriverManagerConnectionProvider configure
  INFO: using driver: org.apache.derby.jdbc.ClientDriver40 at URL: jdbc:derby://localhost:1527/BegHibernateDB
  Feb 19, 2013 10:25:21 AM org.hibernate.connection.DriverManagerConnectionProvider configure
  INFO: connection properties: {user=admin, password=****}
  Feb 19, 2013 10:25:21 AM org.hibernate.dialect.Dialect <init>
  INFO: Using dialect: org.hibernate.dialect.DerbyDialect
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory buildSettings
  INFO: Database ->
         name : Apache Derby
      version : 10.8.1.2 - (1095077)
        major : 10
        minor : 8
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory buildSettings
  INFO: Driver ->
         name : Apache Derby Network Client JDBC Driver
      version : 10.8.1.2 - (1095077)
        major : 10
        minor : 8
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory buildSettings
  WARNING: Could not obtain connection to query metadata
  java.sql.SQLException: Cannot close a connection while a transaction is still active.
    at org.apache.derby.client.am.SQLExceptionFactory40.getSQLException(Unknown Source)
    at org.apache.derby.client.am.SqlException.getSQLException(Unknown Source)
    at org.apache.derby.client.am.Connection.closeResourcesX(Unknown Source)
    at org.apache.derby.client.am.Connection.closeX(Unknown Source)
    at org.apache.derby.client.net.NetConnection.closeX(Unknown Source)
    at org.apache.derby.client.am.Connection.close(Unknown Source)
    at org.apache.derby.client.net.NetConnection.close(Unknown Source)
    at org.apache.derby.client.net.NetConnection40.close(Unknown Source)
    at org.hibernate.connection.DriverManagerConnectionProvider.closeConnection(DriverManagerConnectionProvider.java:160)
    at org.hibernate.cfg.SettingsFactory.buildSettings(SettingsFactory.java:143)
    at org.hibernate.cfg.Configuration.buildSettingsInternal(Configuration.java:2863)
    at org.hibernate.cfg.Configuration.buildSettings(Configuration.java:2859)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1870)
    at sample.dao.DAO.getSessionFactory(DAO.java:25)
    at sample.dao.DAO.getSession(DAO.java:39)
    at sample.dao.DAO.begin(DAO.java:55)
    at sample.CreateUser.create(CreateUser.java:77)
    at sample.CreateUser.addUserList(CreateUser.java:61)
    at samplerc.CreateRecords.main(CreateRecords.java:19)
  Caused by: org.apache.derby.client.am.SqlException: Cannot close a connection while a transaction is still active.
    at org.apache.derby.client.am.Connection.checkForTransactionInProgress(Unknown Source)
    ... 17 more
  Feb 19, 2013 10:25:21 AM org.hibernate.dialect.Dialect <init>
  INFO: Using dialect: org.hibernate.dialect.DerbyDialect
  Feb 19, 2013 10:25:21 AM org.hibernate.transaction.TransactionFactoryFactory buildTransactionFactory
  INFO: Using default transaction strategy (direct JDBC transactions)
  Feb 19, 2013 10:25:21 AM org.hibernate.transaction.TransactionManagerLookupFactory getTransactionManagerLookup
  INFO: No TransactionManagerLookup configured (in JTA environment, use of read-write or transactional second-level cache is not recommended)
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory buildSettings
  INFO: Automatic flush during beforeCompletion(): disabled
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory buildSettings
  INFO: Automatic session close at end of transaction: disabled
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory buildSettings
  INFO: Scrollable result sets: enabled
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory buildSettings
  INFO: JDBC3 getGeneratedKeys(): disabled
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory buildSettings
  INFO: Connection release mode: auto
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory buildSettings
  INFO: Default batch fetch size: 1
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory buildSettings
  INFO: Generate SQL with comments: disabled
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory buildSettings
  INFO: Order SQL updates by primary key: disabled
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory buildSettings
  INFO: Order SQL inserts for batching: disabled
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory createQueryTranslatorFactory
  INFO: Query translator: org.hibernate.hql.ast.ASTQueryTranslatorFactory
  Feb 19, 2013 10:25:21 AM org.hibernate.hql.ast.ASTQueryTranslatorFactory <init>
  INFO: Using ASTQueryTranslatorFactory
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory buildSettings
  INFO: Query language substitutions: {}
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory buildSettings
  INFO: JPA-QL strict compliance: disabled
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory buildSettings
  INFO: Second-level cache: enabled
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory buildSettings
  INFO: Query cache: disabled
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory createRegionFactory
  INFO: Cache region factory : org.hibernate.cache.impl.NoCachingRegionFactory
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory buildSettings
  INFO: Optimize cache for minimal puts: disabled
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory buildSettings
  INFO: Structured second-level cache entries: disabled
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory buildSettings
  INFO: Statistics: disabled
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory buildSettings
  INFO: Deleted entity synthetic identifier rollback: disabled
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory buildSettings
  INFO: Default entity-mode: pojo
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory buildSettings
  INFO: Named query checking : enabled
  Feb 19, 2013 10:25:21 AM org.hibernate.cfg.SettingsFactory buildSettings
  INFO: Check Nullability in Core (should be disabled when Bean Validation is on): enabled
  Feb 19, 2013 10:25:22 AM org.hibernate.impl.SessionFactoryImpl <init>
  INFO: building session factory
  Feb 19, 2013 10:25:22 AM org.hibernate.type.BasicTypeRegistry register
  INFO: Type registration [characters_clob] overrides previous : org.hibernate.type.PrimitiveCharacterArrayClobType@494eaec9
  Feb 19, 2013 10:25:22 AM org.hibernate.type.BasicTypeRegistry register
  INFO: Type registration [materialized_clob] overrides previous : org.hibernate.type.MaterializedClobType@4cb533b8
  Feb 19, 2013 10:25:22 AM org.hibernate.type.BasicTypeRegistry register
  INFO: Type registration [wrapper_materialized_blob] overrides previous : org.hibernate.type.WrappedMaterializedBlobType@c569c60
  Feb 19, 2013 10:25:22 AM org.hibernate.type.BasicTypeRegistry register
  INFO: Type registration [materialized_blob] overrides previous : org.hibernate.type.MaterializedBlobType@a3468f4
  Feb 19, 2013 10:25:22 AM org.hibernate.type.BasicTypeRegistry register
  INFO: Type registration [blob] overrides previous : org.hibernate.type.BlobType@76d67067
  Feb 19, 2013 10:25:22 AM org.hibernate.type.BasicTypeRegistry register
  INFO: Type registration [java.sql.Blob] overrides previous : org.hibernate.type.BlobType@76d67067
  Feb 19, 2013 10:25:22 AM org.hibernate.type.BasicTypeRegistry register
  INFO: Type registration [clob] overrides previous : org.hibernate.type.ClobType@786db724
  Feb 19, 2013 10:25:22 AM org.hibernate.type.BasicTypeRegistry register
  INFO: Type registration [java.sql.Clob] overrides previous : org.hibernate.type.ClobType@786db724
  Feb 19, 2013 10:25:22 AM org.hibernate.type.BasicTypeRegistry register
  INFO: Type registration [wrapper_characters_clob] overrides previous : org.hibernate.type.CharacterArrayClobType@11c7865b
  Feb 19, 2013 10:25:22 AM org.hibernate.impl.SessionFactoryObjectFactory addInstance
  INFO: Not binding factory to JNDI, no JNDI name configured
  getting session, thread = 'main'
  getting session, thread = 'main'
  Created user
  getting session, thread = 'main'
  getting session, thread = 'main'
  getting session, thread = 'main'
  getting session, thread = 'main'
  Created user
  getting session, thread = 'main'
  getting session, thread = 'main'
  getting session, thread = 'main'
  getting session, thread = 'main'
  Created user
  getting session, thread = 'main'

Solution

  • It is because in the initialization ( <clinit>, not the constructor ) of each of your four mapped classes extending DAO, this is executed:

    private static final SessionFactory sessionFactory = 
       new Configuration().configure().buildSessionFactory();
    

    Write a proper HibernateUtil class (like in here for instance); or do something like:

    private static final SessionFactory sessionFactory;
    
    private static synchronized SessionFactory getSessionFactory() {
         if (sessionFactory == null) {
              sessionFactory = new Configuration().configure().buildSessionFactory();
         }
         return sessionFactory;
    }
    

    then refer to sessionFactory only through getSessionFactory().

    EDIT: have a look at this: https://forum.hibernate.org/viewtopic.php?f=6&t=953499

    It is exactly your problem, check the schema of your database and that database is actually in expected location.

    EDIT 2: read the comment here at line 101, then try to set the property hibernate.temp.use_jdbc_metadata_defaults property to false.

    That property has been removed from hibernate 4.x