У меня есть таблица с именем Person
, мой select sql обычно приносит количество разрешений, скажем, 100K человек, так как у меня так много времени, когда я получаю исключение для чтения. Поэтому я знаю, что я должен использовать ROWNUM
для ограничения размера результата.
Class MyService {
@Transactional(rollbackFor = Exception.class)
doJob(){
jobService.process();
}
}
Class JobService {
public void process() {
List<Person> personlList= jdbcQuery.query ("Select * from ... ... where rownum<1000" , ROWMAPPAR, parameter);
//Process all record list
}
Все в порядке до тех пор, пока не будет известно. Но я хочу быть уверенным, что вся запись позволяет говорить, что 100K обрабатывается, и, если есть ошибка при обработке одной из партий, должен произойти откат.
Нужно ли мне возвращать метод process()
рекурсивно?
Использование Spring 3.5 Oracle 11g
Использование ROWNUM, как показано в вашем запросе, может очень не дать вам ожидаемых результатов. (Но, с другой стороны, это возможно, по крайней мере иногда :-). ROWNUM генерируется по мере того, как строки испускаются из запроса, ПОСЛЕ определения предложения WHERE, но ПЕРЕД какими-либо предложениями ORDER BY или HAVING. Это может привести к тому, что ваш запрос вернет результаты, которые могут вас удивить.
Попробуйте создать следующую таблицу:
create table t(n number);
И заселение его:
insert into t (n)
select n from
(select rownum n from dual connect by level <= 2000)
where n > 1234;
Таким образом, таблица будет иметь строки со значениями от 1235 до 2000.
Запустите каждый из следующих запросов в следующем порядке:
select *
from t
order by n;
select n, rownum
from t
where rownum < 100
order by n;
select n, rownum as r from
(select n
from t
order by n);
select n, r from
(select n, rownum as r from
(select n
from t
order by n))
where r < 100
order by n;
и наблюдайте различия в выходе, который вы получаете.
Для тех, у кого нет экземпляра Oracle, здесь SQLFiddle с указанным выше.
Поделитесь и наслаждайтесь.
Нужно ли мне возвращать метод process() рекурсивно?
Я бы этого не сделал. Просто перепишите свой код на это:
class MyService {
@Transactional(rollbackFor = Exception.class)
void doJob(){
// Continue processing within the same transaction, until process() returns false
while (jobService.process());
}
}
class JobService {
public boolean process() {
List<Person> personlList= jdbcQuery.query(
"Select * from ... ... where rownum<=1000" , ROWMAPPAR, parameter);
// I've changed your predicate ------^^
// process() returns false when the above select returns less than 1000 records
return personList.size() == 1000;
}
}
Остерегайтесь, однако, что одна из проблем, которые вы испытываете, заключается в том, что вы сохраняете очень длительную транзакцию. Это вызовет много параллелизма внутри вашей базы данных и может способствовать медленному выполнению пакетного задания. Если вам не требуется абсолютно атомное пакетное задание (все исправлено или все откат), вы можете рассмотреть возможность запуска каждого подзадания в своей собственной транзакции.