Database 为内存中数据库生成事务id
在撰写本文时,不支持内存中的数据库。我可以使用序列表生成自己的ID,但不清楚如何将现有ID与触发器通信。第一个触发器应生成新ID。同一事务中的后续触发器应共享现有IDDatabase 为内存中数据库生成事务id,database,h2,Database,H2,在撰写本文时,不支持内存中的数据库。我可以使用序列表生成自己的ID,但不清楚如何将现有ID与触发器通信。第一个触发器应生成新ID。同一事务中的后续触发器应共享现有ID 我可以使用线程局部变量来共享现有ID,但这似乎很脆弱。有更好的方法吗?使用序列而不是事务ID怎么样 CREATE SEQUENCE SEQ; 事务中的第一个操作设置会话变量,如下所示: SET @TID = SEQ.NEXTVAL; 此事务中的其他操作使用会话变量: CALL @TID; 我发现了一个非常棘手的解决办法: /
我可以使用线程局部变量来共享现有ID,但这似乎很脆弱。有更好的方法吗?使用序列而不是事务ID怎么样
CREATE SEQUENCE SEQ;
事务中的第一个操作设置会话变量,如下所示:
SET @TID = SEQ.NEXTVAL;
此事务中的其他操作使用会话变量:
CALL @TID;
我发现了一个非常棘手的解决办法:
/**
* Invoked when a transaction completes.
*/
public abstract class TransactionListener extends Value
{
private boolean invoked;
/**
* Invoked when the transaction completes.
*/
protected abstract void onCompleted();
@Override
public String getSQL()
{
return null;
}
@Override
public int getType()
{
throw new AssertionError("Unexpected method invocation");
}
@Override
public long getPrecision()
{
throw new AssertionError("Unexpected method invocation");
}
@Override
public int getDisplaySize()
{
throw new AssertionError("Unexpected method invocation");
}
@Override
public String getString()
{
throw new AssertionError("Unexpected method invocation");
}
@Override
public Object getObject()
{
throw new AssertionError("Unexpected method invocation");
}
@Override
public void set(PreparedStatement prep, int parameterIndex) throws SQLException
{
throw new AssertionError("Unexpected method invocation");
}
@Override
protected int compareSecure(Value v, CompareMode mode)
{
throw new AssertionError("Unexpected method invocation");
}
@Override
public int hashCode()
{
throw new AssertionError("Unexpected method invocation");
}
@Override
public boolean equals(Object other)
{
throw new AssertionError("Unexpected method invocation");
}
@Override
public boolean isLinked()
{
return !invoked;
}
@Override
public void close()
{
invoked = true;
onCompleted();
}
}
// -------------TRIGGER BELOW-----------
public void fire(final Connection connection, ResultSet oldRow, ResultSet newRow)
throws SQLException
{
Statement statement = connection.createStatement();
long transactionId;
ResultSet rs = statement.executeQuery("SELECT @TRANSACTION_ID");
try
{
rs.next();
transactionId = rs.getLong(1);
if (transactionId == 0)
{
// Generate a new transaction id
rs.close();
JdbcConnection jdbcConnection = (JdbcConnection) connection;
final Session session = (Session) jdbcConnection.getSession();
session.unlinkAtCommit(new TransactionListener()
{
@Override
protected void onCompleted()
{
boolean oldAutoCommit = session.getAutoCommit();
session.setAutoCommit(false);
try
{
Statement statement = connection.createStatement();
statement.executeQuery("SELECT SET(@TRANSACTION_ID, NULL)");
statement.close();
}
catch (SQLException e)
{
throw new AssertionError(e);
}
finally
{
session.setAutoCommit(oldAutoCommit);
}
}
});
rs = statement.executeQuery("SELECT SET(@TRANSACTION_ID, "
+ "audit_transaction_sequence.NEXTVAL)");
rs.next();
transactionId = rs.getLong(1);
}
}
finally
{
rs.close();
}
assert (transactionId != 0);
// ...
}
下面是它的工作原理:
我们使用Session.unlinkAtCommit来监听事务提交,我假设这个钩子也会回滚,但我还没有验证它
由于我们无法预测触发器调用的数量和顺序,因此必须对每个触发器执行以下检查:
如果@TRANSACTION_ID为null,则注册一个新的事件侦听器并递增序列。
如果@TRANSACTION_ID不为空,则从中获取当前事务ID。
此解决方案的两个主要问题是:
它非常脆弱。如果Session.unlinkAtCommit将来发生更改,则可能会中断事件侦听器。
为了检索事务id,我们必须在每个触发器的顶部重复大量样板代码。
将其实现为内置函数TRANSACTION\u LOCAL\u ID要容易得多。此函数将返回类似于HSQLDB的数据库实例特定事务ID。我无法预测触发器的调用顺序,因此我不知道何时调用NEXTVAL。有没有一种方法可以注册一个钩子,这样在每个事务开始时NEXTVAL都会被调用一次?很抱歉,我的回答不正确,因为如果其他会话调用NEXTVAL,CURRVAL会发生变化。所以这不起作用。。。相反,如果使用CURRVAL,则可以使用会话变量SET@TID=SEQ.NEXTVAL。这看起来很有希望,但让我相信变量的定义将跨越多个事务,因为它是会话范围的,而不是事务范围的。如何确保每个事务使用不同的ID?我刚刚检查过,如果每个连接有一个事务,那么使用用户变量就可以了。我们需要一个针对每个连接的多个事务的解决方案,特别是考虑到连接池的使用。有什么想法吗?