title: Managing JDBC transaction inside EJB container transaction
body: <p>I have an EJB, which has @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW). Inside EJB I have a logger, which has been configured to log into a database using JNDI appender and JDBC:</p>
  
  <pre><code>public class JNDIAppender extends AppenderSkeleton {
  
      private Connection connection;
      private Statement statement;
      private String sql;
      private String dataSourceLookupAddress;
  
      /**
       * Constructor.
       */
      public JNDIAppender() {
      }
  
      /**
       * @return the sql
       */
      public final String getSql() {
          return sql;
      }
  
      /**
       * @param sql the sql to set
       */
      public final void setSql(final String sql) {
          this.sql = sql;
      }
  
      /**
       * @return the dataSourceLookupAddress
       */
      public final String getDataSourceLookupAddress() {
          return dataSourceLookupAddress;
      }
  
      /**
       * @param dataSourceLookupAddress the dataSourceLookupAddress to set
       */
      public final void setDataSourceLookupAddress(final String dataSourceLookupAddress) {
          this.dataSourceLookupAddress = dataSourceLookupAddress;
      }
  
      private synchronized Connection getConnection() {
          if (connection == null) {
              try {
                  final Context ctx = new InitialContext();
                  final DataSource ds = (DataSource) ctx.lookup(getDataSourceLookupAddress());
                  connection = ds.getConnection();
                  connection.setAutoCommit(false);
              } catch (final NamingException e) {
                  errorHandler.error("Datasource JNDI lookup failed: " + dataSourceLookupAddress + "!");
                  errorHandler.error(e.toString());
              } catch (final SQLException e) {
                  errorHandler.error("Sql connection failed to " + dataSourceLookupAddress + "!");
                  errorHandler.error(e.toString());
              }
          }
          return connection;
      }
  
      private synchronized Statement getStatement() {
          if (statement == null) {
              try {
                  statement = getConnection().createStatement();
              } catch (final SQLException e) {
                  errorHandler.error(e.toString());
              }
          }
          return statement;
      }
  
      @Override
      public void activateOptions() {
          if (getSql() == null) {
              errorHandler.error("param 'sql' is null!");
          }
          if (getDataSourceLookupAddress() == null) {
              errorHandler.error("param 'DataSourceLookupAddress' is null!");
          }
      }
  
      /*
       * (non-Javadoc)
       * @see org.apache.log4j.AppenderSkeleton#append(org.apache.log4j.spi.LoggingEvent)
       */
      @Override
      protected synchronized void append(final LoggingEvent event) {
          try {
              ((PatternLayout) getLayout()).setConversionPattern(getSql());
              final String sqlClause = getLayout().format(event);
              getStatement().executeUpdate(sqlClause);
              getConnection().commit();
          } catch (final SQLException e) {
              errorHandler.error(e.toString());
          } finally {
              close();
          }
      }
  
      /*
       * (non-Javadoc)
       * @see org.apache.log4j.AppenderSkeleton#close()
       */
      public void close() {
          try {
              if (statement != null) {
                  statement.close();
                  statement = null;
              }
          } catch (final SQLException e) {
              errorHandler.error(e.toString());
          } finally {
              if (connection != null) {
                  try {
                      connection.close();
                      connection = null;
                  } catch (final SQLException e) {
                      errorHandler.error(e.toString());
                  }
              }
          }
      }
  
      /*
       * (non-Javadoc)
       * @see org.apache.log4j.AppenderSkeleton#requiresLayout()
       */
      public boolean requiresLayout() {
          return true;
      }
  
      /*
       * (non-Javadoc)
       * @see org.apache.log4j.AppenderSkeleton#finalize()
       */
      @Override
      public void finalize() {
          close();
          super.finalize();
      }
  
  } 
  </code></pre>
  
  <p>Now, when exception occurs during EJB method invocation nothing is logged into database, because transaction is rolled back (however, I have set autoCommit to false and commiting transaction manually in JNDIAppender).</p>
  
  <p>My question is: is there a way to log into database in a separate transaction? (I have tried to add @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) to JNDIAppender, but this didn't helped). Or is there any other solution to be able to log into a database, even if exception has been thrown? I might use a separate data source for logging into a database, but this seems like overkill.</p>
  
  <p><strong>UPD:</strong> well, actually JNDIAppener does commits transaction (and thus logs into DB), I've just missed some rows, when I was testing it:) But the problem is that it also commits everything, which was done in EJB before exception (which must not actually be committed). </p>
  
  <p>I must say, that our persistence layer is also JDBC, so basically EJB works with DB using JDBC. So, as far as I see it in JNDIAppender when connection is created it still uses the same transaction, as in EJB. Can I create a separate transaction with JDBC and manage it, while there is already opened transaction exists?</p>