У меня есть следующий метод, который я использую с Spring JDBC
public String getState() {
String stateLink = template.queryForObject(
"select state_url from state_scrape_queue where in_use = false ORDER BY scrape_timestamp NULLS FIRST LIMIT 1",
(result, rowNum) -> {
return result.getString("state_url");
});
return stateLink;
}
Я не могу найти пример того, как сделать для обновления с помощью Spring JDBC. Я хочу, чтобы для in_use было установлено значение true, используя для обновления.
Мне нужно использовать выбор для обновления, так как это приложение будет использоваться в многопоточном режиме. Я не хочу, чтобы несколько потоков получали одну и ту же строку, и способ предотвратить это — использовать select for update
Я смог сделать это с помощью простого JDBC, вот вопрос, который я задал, как это сделать с помощью простого JDBC.
выбрать «для обновления» с JDBC?
Кто-нибудь знает, как это будет сделано?
Обновление имеет свою цель, поэтому оно существует. Это очень полезно в многопоточных приложениях. Это предотвращает получение той же строки другим потоком, потому что использование select for update блокирует строку и в этом примере обновит in_use до true, поэтому другой поток не получит ту же строку.
Предложение for update в select не будет обновлять строку, оно только заблокирует строку, поэтому параллельные транзакции не могут обновляться и, в зависимости от модели блокировки, не могут читать эту строку. Ваше предположение, что оно изменится in_use на true, неверно.
@MarkRotteveel Этот ответ предназначен для выполнения select for update с использованием простого JDBC. stackoverflow.com/a/46995306/492015 Пользуюсь очень давно и работает отлично. Два потока никогда не получали одну и ту же строку. Мне просто нужно сделать то же самое с Spring JDBC
И это будет работать точно так же, как с JDBCTemplate. Основное отличие, вероятно, заключается в границе вашей транзакции. Вызывает ли какой-либо из методов в цепочке этот метод @Transactional, если нет, то транзакция зафиксирована и снятые на момент блокировки queryForObject возвращаются.
Я думаю, что автоматическая фиксация должна быть отключена, чтобы это работало? А поскольку Spring JDBC использует объединение соединений, реализовать это будет проблематично. Я могу просто использовать простой JDBC для выбора методов обновления.
Пул соединений не имеет к этому никакого отношения, он связан со временем жизни транзакции. Также обратите внимание, что простой пример JDBC, который вы связываете, на самом деле явно обновляет набор результатов, а не то, что делает ваш код: он просто выбирает данные.
Мне нужно изменить мой код, чтобы он работал. Похоже, мне нужно переопределить метод внутри getState, чтобы установить для autoCommit значение false, затем вызвать выбор для обновления, чтобы заблокировать строку, затем выполнить обновление и затем зафиксировать
Давайте продолжить обсуждение в чате.
@MarkRotteveel, что вы думаете об ответе?
Это в основном просто ответ на вопрос, который вы связали. В любом случае, почему вы не можете использовать UPDATE для этого напрямую?
@MarkRotteveel в значительной степени, единственная разница в том, что он получает соединение из пула соединений. Т.к. в промежутке между выбором и обновлением другой поток может получить ту же строку.
Вот что я придумал, не стесняйтесь рекомендовать улучшения
public String getState() throws SQLException {
String state = null;
Connection conn = DataSourceUtils.getConnection(template.getDataSource());
try {
conn.setAutoCommit(false);
String[] colNames = { "id", "state_url", "in_use" };
String query = "select " + Stream.of(colNames).collect(Collectors.joining(", "))
+ " from state_scrape_queue where in_use = false ORDER BY scrape_timestamp NULLS FIRST LIMIT 1 FOR UPDATE";
System.out.println(query);
try (Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet rs = stmt.executeQuery(query)) {
while (rs.next()) {
// Get the current values, if you need them.
state = rs.getString(colNames[1]);
rs.updateBoolean(colNames[2], true);
rs.updateRow();
conn.commit();
}
}
} catch (SQLException e) {
conn.setAutoCommit(true);
e.printStackTrace();
} finally {
conn.setAutoCommit(true);
}
return state;
}
Я попробовал это, не отключая AutoCommit, и похоже, что он работает нормально.
FOR UPDATEничего не обновляет, а только блокирует выбранные строки, как если бы они были обновлены. Таким образом, вы не можете установить что-то в true, используяFOR UPDATE, вам нужно будет выполнить отдельный операторUPDATE. В любом случае, с использованием spring-jdbc все работает так же, как и с прямым выполнением операторов. В нынешнем виде неясно, что вы на самом деле спрашиваете.