Использование транзакции с JDBI/IDBI/Dropwizard - проблемы с откатом

У меня много проблем с получением транзакций для работы с IDBI. Мы используем фреймворк dropwizard, а простые вставки, обновления, выборки и удаленные файлы работают, но теперь мы не можем заставить транзакции работать правильно. Вот что я пытаюсь

public class JDb {
    private JustinTest2 jTest2 = null;
    private Handle dbHandle = null;

    public JDb(final IDBI idbi) {
        try {
            dbHandle = idbi.open();
            dbHandle.getConnection().setAutoCommit(false);
            jTest2 = dbHandle.attach(JustinTest2.class);
        } catch( SQLException e ) {

        }
    }

   public void writeJustin(final int styleId, final int eventId) {
        dbHandle.begin();
        int num = jTest2.findByStyleId(styleId);

        try {
            jTest2.doStuff(styleId, eventId);
            dbHandle.commit();
        } catch(Exception e) {
            dbHandle.rollback(); // Never rolls back here, always get the inserted row!
        }

        num = jTest2.findByStyleId(styleId); 
   } 
}

И вот мой класс JustinTest2

public abstract class JustinTest2 {

    @SqlUpdate("INSERT INTO jTest2 (styleId, jNum) VALUES (:styleId, :jNum)")
    public abstract void insert(@Bind("styleId") int styleId, @Bind("jNum") int jNum);

    @SqlQuery("SELECT count(styleId) " +
            "FROM jTest2 " +
            "WHERE styleId=:styleId")
    public abstract int findByStyleId(@Bind("styleId") int styleId);


    public int doStuff(int styleId, int eventId) throws Exception{
        int count = findByStyleId(styleId);

        insert(styleId, eventId);

        count = findByStyleId(styleId);

        if(count==1) {
            throw new Exception("Roll back");
        }

        return count;
    }
}

Я также попытался реализовать writeJustin как:

public void writeJustin(final int styleId, final int eventId) throws Exception {
    int rows_updated = jTest2.inTransaction(new Transaction<Integer, JustinTest2>() {
        @Override
        public Integer inTransaction(JustinTest2 transactional, TransactionStatus status) throws Exception {

            jTest2.insert(styleId, eventId);
            int num = transactional.findByStyleId(styleId);

            try {
                if(num == 1) throw new Exception("BOOM");    
            } catch (Exception e) {
                transactional.rollback();
                throw e;
            }

            num = transactional.findByStyleId(styleId);
            return num;
        }
    });
}

Я не могу заставить транзакцию откатиться, причем каждый из этих способов вставил строку после отката, независимо от того, пытаюсь ли я непосредственно через дескриптор или использую inTransaction (который, по моему мнению, не должен совершать транзакцию если в обратном вызове выбрано исключение) Кто-нибудь знает, что я могу сделать неправильно?

Ответ 1

Я понял это. Оказывается, таблица, которую я тестировал, использовала MyISAM, а не InnoDB как механизм хранения. MyISAM не поддерживает транзакции. Я перестроил таблицу с помощью InnoDB, и код выше работал нормально.

Для тех, кто не знает, вы можете видеть, какой движок использует таблица:

show create table <tablename>;

Должен увидеть что-то вроде:

CREATE TABLE `grades` (
    `id` int(11) NOT NULL,
    `percent` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 

Ответ 2

Это касается вашего вопроса, но я добавляю его в качестве ответа, потому что ваш вопрос высок в результатах Google, и примеров там нет.

С JDBI v2 вы можете использовать @Transaction аннотация, чтобы упростить код. Просто оформите публичный метод с помощью аннотации, и JDBI обработает начало, фиксацию и откат за кулисами.

public abstract class JustinTest2 {

    @SqlUpdate("INSERT INTO jTest2 (styleId, jNum) VALUES (:styleId, :jNum)")
    protected abstract void insert(@Bind("styleId") int styleId, @Bind("jNum") int jNum);

    @SqlQuery("SELECT count(styleId) " +
            "FROM jTest2 " +
            "WHERE styleId=:styleId")
    protected abstract int findByStyleId(@Bind("styleId") int styleId);

    @Transaction
    public int doStuff(int styleId, int eventId) throws Exception{
        int count = findByStyleId(styleId);

        insert(styleId, eventId);

        count = findByStyleId(styleId);

        if(count==1) {
            throw new Exception("Roll back");
        }

        return count;
    }
}

Обратите внимание, что я защитил методы insert и findByStyleId; с public, чтобы обеспечить их совместное выполнение в транзакции (в общедоступном методе doStuff); а не private, потому что автоматическая сгенерированная реализация JDBI не сможет их переопределить (при этом методы private abstract не работают по этой причине - вы вынуждаете компилятор принять метод без тела).

Вы также можете указать TransactionIsolationLevel в аннотации, чтобы переопределить настройки базы данных по умолчанию.

@Transaction(TransactionIsolationLevel.REPEATABLE_READ)