[英]Managing JDBC transaction inside EJB container transaction
I have an EJB, which has @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW). 我有一个EJB,它具有@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)。 Inside EJB I have a logger, which has been configured to log into a database using JNDI appender and JDBC:
在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();
}
}
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). 现在,当在EJB方法调用期间发生异常时,由于回滚了事务,因此没有任何内容记录到数据库中(但是,我将autoCommit设置为false并在JNDIAppender中手动提交了事务)。
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).
(我尝试将@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)添加到JNDIAppender,但这没有帮助。 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.
我可能会使用单独的数据源登录数据库,但这似乎有些过分。
UPD: 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). UPD:好吧,实际上JNDIAppener确实提交了事务(并因此登录到DB),当我对其进行测试时,我只是错过了一些行:)但是问题在于它还提交了所有东西,这是在EJB异常之前完成的(实际上不得提交)。
I must say, that our persistence layer is also JDBC, so basically EJB works with DB using JDBC. 我必须说,我们的持久层也是JDBC,因此EJB基本上可以与使用JDBC的DB一起使用。 So, as far as I see it in JNDIAppender when connection is created it still uses the same transaction, as in EJB.
因此,据我在创建连接时在JNDIAppender中看到的那样,它仍然使用与EJB中相同的事务。 Can I create a separate transaction with JDBC and manage it, while there is already opened transaction exists?
在已经存在打开的事务的情况下,是否可以使用JDBC创建单独的事务并进行管理?
Solution to that issue was using separate Thread (which starts a new transaction) for logging. 该问题的解决方案是使用单独的线程(启动新事务)进行日志记录。 Something similar to this, but not like this (I don't have exact code under my fingertips):
与此类似,但并非如此(我指尖没有确切的代码):
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();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.