ORA-01722: неверный номер (только для ПК с MacOS)

Лицо

@Entity
@Table(name = "policy_restriction_bound",
    uniqueConstraints = @UniqueConstraint(columnNames = {"policy_restriction_id", "upperBound", "currency"},
        name = "uq_policy_restriction_bound_restr_id_bound_currency"))
public class PolicyRestrictionBoundEntity {
    public static final int UPPER_BOUND_SCALE = 5;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence-generator")
    @SequenceGenerator(name = "sequence-generator", sequenceName = "hibernate_sequence", allocationSize = 5)
    private Long id;

    @NotNull
    @ManyToOne
    @JoinColumn(name = "policy_restriction_id", foreignKey = @ForeignKey(name = "fk_policy_restr_bound2policy_restr"))
    private PolicyRestrictionEntity policyRestriction;

    @NotNull
    private Integer minimumApprovesCount;

    @Positive
    @Access(AccessType.PROPERTY)
    private BigDecimal upperBound;

    @Size(min = 1, max = 3)
    private String currency;
}

Контейнер

private static final OracleContainer DB_CONTAINER = new OracleContainer("gvenzl/oracle-xe:18.4.0-slim-faststart");

Следующее вставляется в базу данных Oracle

protected List<String> getInsertTestDataAfterMigrationCommands() {
        return List.of("""
            """
                INSERT into policy_restriction_bound(id, policy_restriction_id, minimum_approves_count, upper_bound, currency)
                VALUES ('1', '1', '2', '99.99000', 'USD')"""
        );
    }

Ошибка

java.lang.RuntimeException: java.sql.SQLSyntaxErrorException: ORA-01722: invalid number
Caused by: Error : 1722, Position : 134, Sql = INSERT into policy_restriction_bound(id, policy_restriction_id, minimum_approves_count, upper_bound, currency)
VALUES ('1', '1', '2', '99.99000', 'USD'), OriginalSql = INSERT into policy_restriction_bound(id, policy_restriction_id, minimum_approves_count, upper_bound, currency)
VALUES ('1', '1', '2', '99.99000', 'USD'), Error Msg = ORA-01722: invalid number

    at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:637)

Привет,

Я вставляю данные с помощью метода getInsertTestDataAfterMigrationCommands(), как указано выше. Но я получаю ошибку ORA-01722: invalid number. Когда я удаляю одинарные кавычки в '99.99000', ошибка исчезает. Но что меня интересует, так это то, что мы получаем эту ошибку только для всех пользователей Macbook в команде. Эта ошибка не возникает на компьютерах с Linux и Windows, которые также проходят конвейер Jenkins.

Кроме того, когда я снова удаляю одинарные кавычки, передайте конвейер Jenkins. Также интересно, что мы получаем только выпуски в одинарных кавычках со значением BigDecimal. С другими полями проблем нет.

Я не смог найти причину, по которой эта ошибка возникает только на компьютерах Macbook, есть ли у вас какие-нибудь идеи?

Дополнительно: Я заметил, что на Макбуке включены умные кавычки. Я отключил его, но мы продолжаем получать ошибку.

Не уверен насчет конкретных проблем вашего клиента, но, как правило, числовые значения в операторах Oracle insert не следует заключать в кавычки.

pmdba 17.09.2023 23:56

Да, я согласен, но мне все еще любопытно, почему это происходит только на Mac PC и BigDecimal.

Yusuf BESTAS 18.09.2023 00:08

Если вы поместите числовое значение в кавычки, то автоматическое преобразование должно произойти где-то между вашим приложением и оракулом или оракулом и macOS. Итак, возможно, драйвер oracle для Mac более явный во время преобразования.

Sve Kamenska 18.09.2023 00:27

@SveKamenska Это внутренняя часть базы данных. Запрос OP передается в базу данных и анализируется механизмом SQL, и там происходит неявное приведение, которое зависит от параметров сеанса сеанса базы данных пользователя для управления ожидаемым форматом числа.

MT0 18.09.2023 00:29

@MT0, спасибо за подробное объяснение - это то, что я имел в виду под вторым предположением - взаимодействие oracle <-> os

Sve Kamenska 18.09.2023 01:43

Это не ОС как таковая. ОС не имеет никакого отношения к тому, что отправляется через соединение с базой данных. Это будет драйвер JDBC или само приложение. И я ожидаю, что это поведение зависит от версии драйвера JDBC, а не от специального поведения драйвера JDBC для MacOS. (Последнее было бы извращением... IMO.) Драйверы предоставляются Oracle или третьей стороной, а не Apple.

Stephen C 18.09.2023 05:58

@StephenC Драйвер должен просто передать запрос (и все параметры привязки) механизму SQL базы данных, и база данных проанализирует запрос и выполнит любые неявные приведения между типами данных, используя свои настройки. На это может повлиять то, что драйвер инициализируется с настройкой NLS_LANG или параметры сеанса NLS_TERRITORY или NLS_NUMERIC_CHARACTERS задаются в сеансе базы данных пользователя (в противном случае будут использоваться настройки базы данных по умолчанию); это, пожалуй, все, что здесь окажет какое-то влияние.

MT0 18.09.2023 10:13

Хорошая точка зрения. Но это приложение (или конфигурация приложения) устанавливает их. Не ОС.

Stephen C 18.09.2023 10:29

@StephenC Я обновил свой ответ, добавив некоторые параметры, с помощью которых драйвер JDBC может получать параметры сеанса, которые он использует в своих соединениях. При загрузке драйвера для инициализации параметров сеанса в сеансе базы данных могут потребоваться некоторые настройки ОС, либо они могут быть переопределены настройками приложения или явно путем прямой установки параметров сеанса.

MT0 18.09.2023 10:42
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
9
63
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Резюме: НИКОГДА не полагайтесь на неявные преобразования строк в числа. Если вы хотите передать числовое значение, передайте его как число, а НЕ как строку.


Когда вы вставляете строку в столбец NUMBER, вы запрашиваете Oracle выполнить неявное преобразование строки в число, и если десятичный разделитель не ., то '99.99000' выдаст ошибку:

ORA-01722: invalid number

(В Oracle 23 ошибка более информативна: ORA-01722: unable to convert string value containing '.' to a number:.)

Вы можете увидеть это, если измените параметры сеанса:

CREATE TABLE policy_restriction_bound(
  id                     NUMBER(8,0),
  policy_restriction_id  NUMBER(8,0),
  minimum_approves_count NUMBER(8,0),
  upper_bound            NUMBER(10,5),
  currency               VARCHAR2(3)
);

Затем:

ALTER SESSION SET NLS_NUMERIC_CHARACTERS = ',.';
INSERT into policy_restriction_bound(id, policy_restriction_id, minimum_approves_count, upper_bound, currency)
VALUES ('1', '1', '2', '99.99000', 'USD');

Выходы:

ORA-01722: invalid number

и:

ALTER SESSION SET NLS_NUMERIC_CHARACTERS = '.,';
INSERT into policy_restriction_bound(id, policy_restriction_id, minimum_approves_count, upper_bound, currency)
VALUES ('1', '1', '2', '99.99000', 'USD');

Работает.


Что вам следует делать, так это НИКОГДА не полагаться на неявные преобразования строк в числа. Если вы хотите передать числовое значение, передайте его как число, а НЕ как строку.

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

В зависимости от драйвера JDBC это может быть:

  1. Параметры сеанса соединения по умолчанию, которые могут быть получены из языка ОС или переменной среды NLS_LANG, установленной в ОС.

  2. Запуск приложения Java и явное указание языка и локали при запуске Java:

    -Duser.country=en -Duser.language=en
    

    Это может привести к тому, что драйвер JDBC переопределит любые настройки базы данных по умолчанию и установит параметры сеанса NLS_LANG и NLS_TERRITORY (которые будут устанавливать параметр сеанса NLS_NUMERIC_CHARACTERS по умолчанию для этой территории).

  3. Установка локали в Java-приложении:

    Locale.setDefault(Locale.<your locale here>);
    

    Это может привести к тому, что драйвер JDBC переопределит любые настройки базы данных по умолчанию и установит параметр сеанса NLS_TERRITORY (и, возможно, NLS_LANG) (который установит параметр сеанса NLS_NUMERIC_CHARACTERS по умолчанию для этой территории).

  4. В SQL из приложения Java явная установка параметра NLS_TERRITORY в сеансе базы данных (который неявно устанавливает значение по умолчанию NLS_NUMERIC_CHARACTERS для этой территории) и переопределяет любые параметры по умолчанию для соединения.

    ALTER SESSION SET TERRITORY = 'United States'
    
  5. В SQL из приложения Java явная установка параметра сеанса NLS_NUMERIC_CHARACTERS (опять же, переопределяя значения по умолчанию).

    ALTER SESSION SET NLS_NUMERIC_CHARACTERS = '.,'
    

Но гораздо лучше НИКОГДА не полагаться на неявные преобразования.

рабочий пример

@MTO Благодарю за подробные ответы. Ответ очень помог раскрыть тему.

Yusuf BESTAS 25.09.2023 09:03

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