Возврат порядкового номера на вставке

После вставки заказа в базу данных HSQL через мой объект OrderDAO я хочу иметь возможность получить порядковый номер, который был назначен для заказа при вставке.

У меня для моего PreparedStatement есть:

public long saveOrder(Order order) {
    long orderId = 0;
    try (Connection conn = MyDataSource.getDataSource().getConnection();
         PreparedStatement ps = conn.prepareStatement("INSERT INTO orders(id, order_number) VALUES (NEXT VALUE FOR seq1, ?)",
                                                            PreparedStatement.RETURN_GENERATED_KEYS)) {

        ps.setString(1, order.getOrderNumber());
        ps.execute();

        ResultSet rs = ps.getResultSet();
        if (rs.next()) {
            orderId = rs.getLong(1);
        }

    } catch (Exception e) {
        throw new RuntimeException(e);
    }
    return orderId;
}

Я бы предположил, что после выполнения запроса и запроса его набора результатов первым столбцом в наборе результатов будет порядковый номер. Но, похоже, у меня так не получается.

Где я ошибаюсь в этом?

Покажите нам код, который вы пробовали, а также сообщите нам, какую базу данных и драйвер вы используете. Не все БД могут поддерживать сгенерированные ключи, и не все поддерживают это одинаково.

Tim Biegeleisen 20.10.2018 15:36

Добавил код. И я использую hsqldb в памяти.

Jaroslav Gorshkov 20.10.2018 15:38

Выполните первый запрос, чтобы получить следующее значение из последовательности, затем используйте результат в качестве параметра для второго запроса.

JB Nizet 20.10.2018 15:43
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
3
651
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Исходя из этот ТАК вопрос и ответ, ваш синтаксис должен быть примерно таким:

Connection conn = MyDataSource.getDataSource().getConnection();
long orderId = 0;
String sql = "INSERT INTO orders (id, order_number) VALUES (NEXT VALUE FOR seq1, ?)";
PreparedStatement ps = conn.prepareStatement(sql, RETURN_GENERATED_KEYS);
ps.setString(1, order.getOrderNumber());
int numAffected = ps.executeUpdate();
ResultSet rs = ps.getGeneratedKeys();
if (rs.next()) {
    orderId = rs.getLong(1);
}

Проверка количества обновлений совершенно не нужна

Mark Rotteveel 21.10.2018 10:36

@MarkRotteveel entirely unnecessary ... значит, вы говорите, что не существует мыслимого сценария, при котором количество обновлений DML не равно единице?

Tim Biegeleisen 21.10.2018 10:38

Это вставка, которая либо завершилась успешно и вставила строку, либо не удалась с исключением. Также учтите, что счетчик обновлений сложен и в некоторых базах данных может возвращать значение выше 1, даже если была вставлена ​​только 1 строка (например, если триггер вставил запись аудита, это может быть включено в счетчик обновлений в некоторых системах баз данных).

Mark Rotteveel 21.10.2018 10:41
Ответ принят как подходящий

В вашем коде есть две проблемы:

  1. Вы используете неправильный метод для получения набора результатов сгенерированных ключей. Сгенерированный набор результатов ключей можно получить только с помощью getGeneratedKeys() (или, по крайней мере, этого требует спецификация JDBC).

    Вам нужно изменить свой код, чтобы использовать ps.getGeneratedKeys() вместо ps.getResultSet()

  2. Другая проблема заключается в том, что ваш код предполагает нестандартное поведение сгенерированных ключей: ваша вставка на самом деле не использует сгенерированный ключ в том смысле, который предполагает спецификация JDBC, поскольку вы сами генерируете идентификатор в операторе вставки (используя NEXT VALUE FOR seq1) вместо ключ создается как побочный эффект оператора вставки (например, столбцом идентификаторов или триггером).

    HSQLDB не возвращает сгенерированный ключ в этой ситуации, поскольку он не рассматривает id как сгенерированный столбец. Вместо этого вам нужно либо определить столбец как столбец идентификаторов (и не указывать его явно во вставке), либо явно указать возвращаемый столбец.

    Для создания столбца идентификаторов обратитесь к документации HSQLDB. Чтобы явно указать возвращаемый столбец, замените

    conn.prepareStatement("<query>", PreparedStatement.RETURN_GENERATED_KEYS)
    

    либо с указанием индекса возвращаемых столбцов (то есть 1 является первым столбцом):

    conn.prepareStatement("<query>", new int[] { 1 })
    

    или спецификация имени столбца для возвращаемых столбцов

    conn.prepareStatement("<query>", new String[] { "id" })
    

Ваш окончательный код должен выглядеть примерно так:

try (PreparedStatement ps = conn.prepareStatement(
         "INSERT INTO orders(id, order_number) VALUES (NEXT VALUE FOR seq1, ?)",
         new String[] { "id" })) {
    ps.setString(1, order.getOrderNumber());
    ps.execute();

    try (ResultSet rs = stmt.getGeneratedKeys()) {
        if (rs.next()) {
            return rs.getLong(1);
        }
    }
}

Другие вопросы по теме