为内存数据库生成事务ID

时间:2011-06-11 14:47:58

标签: database h2

在撰写本文时,TRANSACTION_ID()不支持内存数据库。我可以使用序列表生成自己的ID,但不清楚如何将现有ID传递给触发器。第一个触发器应该生成一个新ID。后续触发器(在同一事务中)应共享现有ID。

我可以使用线程局部变量来共享现有ID,但这看起来很脆弱。有更好的方法吗?

2 个答案:

答案 0 :(得分:0)

如何使用序列而不是事务ID?

CREATE SEQUENCE SEQ;

事务中的第一个操作设置会话变量,如下所示:

SET @TID = SEQ.NEXTVAL;

此事务中的其他操作使用会话变量:

CALL @TID;

答案 1 :(得分:0)

我找到了一个(非常hacky)的解决方法:

/**
 * 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()来监听事务提交(我假设这也挂钩回滚,但我还没有验证这个)
  • 由于我们无法预测触发器调用的次数和顺序,因此我们必须在每个触发器中执行以下检查:

    1. 如果@TRANSACTION_ID为null,则注册一个新的事件侦听器并递增序列。
    2. 如果@TRANSACTION_ID不为空,请从中获取当前的交易ID。

此解决方法的两个主要问题是:

  1. 非常脆弱。如果Session.unlinkAtCommit()将来发生变化,它可能会破坏事件监听器。
  2. 我们必须在每个触发器的顶部重复很多样板代码才能检索事务ID。
  3. 将其作为内置函数TRANSACTION_LOCAL_ID()实现起来要容易得多。此函数将返回类似于HSQLDB的数据库实例特定事务ID。