Вероятно, каждый из нас сталкивался с этой проблемой хотя бы раз в жизни программиста — как эмулировать последовательность базы данных? Ниже вы можете найти мой вариант решения этой проблемы.
Предположим, что у нас есть интерфейс, определяющий желаемый API для возврата последовательности целых чисел:
|
1
2
3
4
5
|
public interface Sequences { int nextValue(String sequenceName) throws SQLException;} |
и реализация этого API в следующем виде:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
class SequencesService implements Sequences { private static final String SQL_QUERY = "SELECT SEQ_NAME, SEQ_VALUE FROM SEQUENCE WHERE SEQ_NAME = ? FOR UPDATE"; private final DataSource dataSource; SequencesService(final DataSource dataSource) { this.dataSource = dataSource; } @Override public int nextValue(final String sequenceName) throws SQLException { final long threadId = Thread.currentThread().getId(); try (final Connection connection = dataSource.getConnection()) { connection.setAutoCommit(false); try (final PreparedStatement statement = connection.prepareStatement( SQL_QUERY, TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE)) { statement.setString(1, sequenceName); try (final ResultSet resultSet = statement.executeQuery()) { System.out.println( String.format("[%d] - select for update", threadId)); int nextValue = 1; if (resultSet.next()) { nextValue = 1 + resultSet.getInt(2); resultSet.updateInt(2, nextValue); resultSet.updateRow(); } else { resultSet.moveToInsertRow(); resultSet.updateString(1, sequenceName); resultSet.updateInt(2, nextValue); resultSet.insertRow(); } System.out.println( String.format("[%d] - next val: %d", threadId, nextValue)); return nextValue; } } finally { System.out.println(String.format("[%d] - commit", threadId)); connection.commit(); } } }} |
Вы должны простить мне две вещи 🙂 — использование println, которое я добавил для генерации некоторой визуальной обратной связи;) и отсутствие подробного объяснения, как работает это решение;) Я просто упомяну, что подсказка — это способ, которым готовится утверждение создание и обработка результирующего набора: updateRow / moveToInsertRow / insertRow Использование;) (подробности см. в ссылках внизу этого поста).
Я написал простой тестовый пример для наблюдения и проверки этого кода, что-то вроде:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
@Autowiredprivate Sequences sequences;private Callable<Integer> callable() { return () -> { System.out.println(String.format("[%d] - starting", Thread.currentThread().getId())); return sequences.nextValue("My Sequence"); };}@Testpublic void test() throws Exception { final ExecutorService executor = Executors.newFixedThreadPool(3); final CompletionService<Integer> completion = new ExecutorCompletionService<>(executor); for (int i = 0; i < 3; i++) { completion.submit(callable()); } for (int completed = 1; completed <= 3; completed++) { final Future<Integer> result = completion.take(); System.out.println(String.format("Result %d - %d", completed, result.get())); assertEquals(Integer.valueOf(completed), result.get()); }} |
При запуске приведенного выше кода результат будет примерно таким (идентификаторы потоков в скобках):
[16] — начало
[18] — начало
[17] — начало
[17] — выберите для обновления
[17] — следующий вал: 1
[17] — совершить
[18] — выберите для обновления
Результат 1 — 1
[18] — следующий вал: 2
[18] — совершить
[16] — выберите для обновления
[16] — следующий вал: 3
[16] — совершить
Результат 2 — 2
Результат 3 — 3
Этот код только для демонстрационных целей 🙂 — если вы хотите сделать что-то подобное в своем проекте, вероятно, вы скорее будете использовать для ex. @Transactional аннотация Spring Framework вместо ручной обработки транзакций или даже JPA, делегирующий эту работу JDBC. Например, в Hibernate вы можете сделать это как-то так:
|
1
2
3
4
5
|
import org.hibernate.Session;...entityManager.unwrap(Session.class) .doReturningWork(connection -> { ... code derived from my example ... }); |
Несколько ссылок на десерт:
- Обновление строк в объектах ResultSet (JDBC)
- Вставка строк в объекты ResultSet (JDBC)
- Декларативное управление транзакциями и использование @Transactional (Spring Framework)
- ReturningWork (JPA, Hibernate)
… И я почти забыл;) — репозиторий GitHub, содержащий все мои коды кода для этого поста
|
Смотрите оригинальную статью здесь: JDBC — эмуляция последовательности Мнения, высказанные участниками Java Code Geeks, являются их собственными. |