Использование транзакции с 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)