@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を使用して別のトランザクションを作成して管理できますか?