Вероятно, каждый из нас сталкивался с этой проблемой хотя бы раз в жизни программиста — как эмулировать последовательность базы данных? Ниже вы можете найти мой вариант решения этой проблемы.
Предположим, что у нас есть интерфейс, определяющий желаемый 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
|
@Autowired private Sequences sequences; private Callable<Integer> callable() { return () -> { System.out.println(String.format( "[%d] - starting" , Thread.currentThread().getId())); return sequences.nextValue( "My Sequence" ); }; } @Test public 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, являются их собственными. |