0

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)を持つEJBがあります。EJBの内部には、JNDIアペンダーとJDBCを使用してデータベースにログインするように構成されたロガーがあります。

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();
    }

} 

これで、EJBメソッドの呼び出し中に例外が発生した場合、トランザクションがロールバックされるため、データベースには何もログインしません(ただし、autoCommitをfalseに設定し、JNDIAppenderでトランザクションを手動でコミットしました)。

私の質問は、別のトランザクションでデータベースにログインする方法はありますか?(@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)をJNDIAppenderに追加しようとしましたが、役に立ちませんでした)。または、例外がスローされた場合でも、データベースにログインできる他の解決策はありますか?データベースにログインするために別のデータソースを使用するかもしれませんが、これはやり過ぎのようです。

UPD:ええと、実際にはJNDIAppenerはトランザクションをコミットします(したがってDBにログインします)、テストしていたときにいくつかの行を見逃しました:)しかし、問題は、例外の前にEJBで行われたすべてもコミットすることです(実際にコミットしてはいけません)。

永続層もJDBCであるため、基本的にEJBはJDBCを使用してDBと連携します。したがって、接続が作成されたときにJNDIAppenderで確認できる限り、EJBと同じトランザクションを使用します。すでに開かれているトランザクションが存在するときに、JDBCを使用して別のトランザクションを作成して管理できますか?

4

1 に答える 1

0

この問題の解決策は、ロギングに別のスレッド(新しいトランザクションを開始する)を使用することでした。これに似ていますが、これとは異なります(指先に正確なコードがありません):

Thread t = new Thread() {
            @Override
            public void run() {
                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();
                }
            }
        };
        t.run();
于 2013-09-05T14:25:05.537 に答える