Генерация идентификатора транзакции для баз данных в памяти - PullRequest
0 голосов
/ 11 июня 2011

На момент написания этой статьи TRANSACTION_ID () не поддерживает базы данных в памяти. Я могу сгенерировать свои собственные идентификаторы, используя таблицу последовательности, но не ясно, как сообщить существующие идентификаторы триггерам. Первый триггер должен генерировать новый идентификатор. Последующие триггеры (в той же транзакции) должны использовать существующий идентификатор.

Я мог бы использовать локальные переменные потока для совместного использования существующего идентификатора, но это кажется хрупким. Есть ли лучший способ сделать это?

Ответы [ 2 ]

0 голосов
/ 13 июня 2011

Я нашел (очень хакерский) обходной путь:

/**
 * 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 равно нулю, зарегистрируйте новый прослушиватель событий и увеличьте последовательность.
    2. Если @TRANSACTION_ID не равно нулю, извлеките из него идентификатор текущей транзакции.

Две основные проблемы с этим обходным путем:

  1. Это очень хрупкий.Если Session.unlinkAtCommit () изменится в будущем, он, вероятно, сломает прослушиватель событий.
  2. Мы должны повторить много шаблонного кода в верхней части каждого триггера просто для получения идентификатора транзакции.

Было бы намного проще реализовать это как встроенную функцию TRANSACTION_LOCAL_ID ().Эта функция возвращает идентификатор транзакции, специфичной для экземпляра базы данных, аналогично HSQLDB.

0 голосов
/ 11 июня 2011

А как насчет использования последовательностей вместо идентификаторов транзакций?

CREATE SEQUENCE SEQ;

Первая операция в транзакции устанавливает переменную сеанса следующим образом:

SET @TID = SEQ.NEXTVAL;

Другие операции в этой транзакции используют переменную сеанса:

CALL @TID;
...