Лицо
@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, есть ли у вас какие-нибудь идеи?
Дополнительно: Я заметил, что на Макбуке включены умные кавычки. Я отключил его, но мы продолжаем получать ошибку.
Да, я согласен, но мне все еще любопытно, почему это происходит только на Mac PC и BigDecimal.
Если вы поместите числовое значение в кавычки, то автоматическое преобразование должно произойти где-то между вашим приложением и оракулом или оракулом и macOS. Итак, возможно, драйвер oracle для Mac более явный во время преобразования.
@SveKamenska Это внутренняя часть базы данных. Запрос OP передается в базу данных и анализируется механизмом SQL, и там происходит неявное приведение, которое зависит от параметров сеанса сеанса базы данных пользователя для управления ожидаемым форматом числа.
@MT0, спасибо за подробное объяснение - это то, что я имел в виду под вторым предположением - взаимодействие oracle <-> os
Это не ОС как таковая. ОС не имеет никакого отношения к тому, что отправляется через соединение с базой данных. Это будет драйвер JDBC или само приложение. И я ожидаю, что это поведение зависит от версии драйвера JDBC, а не от специального поведения драйвера JDBC для MacOS. (Последнее было бы извращением... IMO.) Драйверы предоставляются Oracle или третьей стороной, а не Apple.
@StephenC Драйвер должен просто передать запрос (и все параметры привязки) механизму SQL базы данных, и база данных проанализирует запрос и выполнит любые неявные приведения между типами данных, используя свои настройки. На это может повлиять то, что драйвер инициализируется с настройкой NLS_LANG или параметры сеанса NLS_TERRITORY или NLS_NUMERIC_CHARACTERS задаются в сеансе базы данных пользователя (в противном случае будут использоваться настройки базы данных по умолчанию); это, пожалуй, все, что здесь окажет какое-то влияние.
Хорошая точка зрения. Но это приложение (или конфигурация приложения) устанавливает их. Не ОС.
@StephenC Я обновил свой ответ, добавив некоторые параметры, с помощью которых драйвер JDBC может получать параметры сеанса, которые он использует в своих соединениях. При загрузке драйвера для инициализации параметров сеанса в сеансе базы данных могут потребоваться некоторые настройки ОС, либо они могут быть переопределены настройками приложения или явно путем прямой установки параметров сеанса.




Резюме: НИКОГДА не полагайтесь на неявные преобразования строк в числа. Если вы хотите передать числовое значение, передайте его как число, а НЕ как строку.
Когда вы вставляете строку в столбец 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 это может быть:
Параметры сеанса соединения по умолчанию, которые могут быть получены из языка ОС или переменной среды NLS_LANG, установленной в ОС.
Запуск приложения Java и явное указание языка и локали при запуске Java:
-Duser.country=en -Duser.language=en
Это может привести к тому, что драйвер JDBC переопределит любые настройки базы данных по умолчанию и установит параметры сеанса NLS_LANG и NLS_TERRITORY (которые будут устанавливать параметр сеанса NLS_NUMERIC_CHARACTERS по умолчанию для этой территории).
Установка локали в Java-приложении:
Locale.setDefault(Locale.<your locale here>);
Это может привести к тому, что драйвер JDBC переопределит любые настройки базы данных по умолчанию и установит параметр сеанса NLS_TERRITORY (и, возможно, NLS_LANG) (который установит параметр сеанса NLS_NUMERIC_CHARACTERS по умолчанию для этой территории).
В SQL из приложения Java явная установка параметра NLS_TERRITORY в сеансе базы данных (который неявно устанавливает значение по умолчанию NLS_NUMERIC_CHARACTERS для этой территории) и переопределяет любые параметры по умолчанию для соединения.
ALTER SESSION SET TERRITORY = 'United States'
В SQL из приложения Java явная установка параметра сеанса NLS_NUMERIC_CHARACTERS (опять же, переопределяя значения по умолчанию).
ALTER SESSION SET NLS_NUMERIC_CHARACTERS = '.,'
Но гораздо лучше НИКОГДА не полагаться на неявные преобразования.
@MTO Благодарю за подробные ответы. Ответ очень помог раскрыть тему.
Не уверен насчет конкретных проблем вашего клиента, но, как правило, числовые значения в операторах Oracle
insertне следует заключать в кавычки.