Типы данных Oracle JDBC и Oracle CHAR

У меня есть сложная проблема с обработкой драйверов Oracle JDBC типов данных CHAR. Возьмем эту простую таблицу:

create table x (c char(4));
insert into x (c) values ('a');  -- inserts 'a   '

Поэтому, когда я вставляю что-то в CHAR(4), строка всегда заполняется пробелами. Это также выполняется, когда я выполняю запросы следующим образом:

select * from x where c = 'a';    -- selects 1 record
select * from x where c = 'a ';   -- selects 1 record
select * from x where c = 'a   '; -- selects 1 record

Здесь константа 'a' заполняется пробелами. Поэтому запись всегда возвращается. Это выполняется, когда эти запросы выполняются с помощью JDBC PreparedStatement. Теперь сложнее всего, когда я хочу использовать переменную bind:

PreparedStatement stmt = 
  conn.prepareStatement("select * from x where c = ?");
stmt.setString(1, "a");    // This won't return any records
stmt.setString(1, "a   "); // This will return a record
stmt.executeQuery();

Это обходное решение:

PreparedStatement stmt = 
  conn.prepareStatement("select * from x where trim(c) = trim(?)");
stmt.setString(1, "a");    // This will return a record
stmt.setString(1, "a   "); // This will return a record
stmt.executeQuery();

EDIT: теперь это ограничения:

  • Вышеописанное обходное решение нежелательно, так как оно изменяет как содержимое c, так и ?, и оно делает использование индексов на c довольно сложным.
  • Перемещение столбца от CHAR до VARCHAR (что должно быть, конечно) невозможно

EDIT. Причины этих ограничений связаны с тем, что я задаю этот вопрос с точки зрения разработчика jOOQ, библиотека абстракции базы данных. Поэтому мои требования состоят в том, чтобы предоставить очень общее решение, которое ничего не сломает в коде клиента jOOQ. Вот почему я не очень большой поклонник обходного пути. И поэтому у меня нет доступа к этому объявлению столбца CHAR. Но тем не менее, я хочу иметь возможность справиться с этим делом.

Что бы вы сделали вместо этого? Какая хорошая практика для обработки типов данных CHAR, когда я хочу игнорировать конечные пробелы?

Ответ 1

Если вы хотите

stmt.setString(1, "a");    // This won't return any records

чтобы вернуть запись, попробуйте

conn.prepareStatement("select * from x where c = cast(? as char(4))")

Ответ 2

Я не вижу причин использовать тип данных CHAR, даже если это char (1) в Oracle. Вы можете изменить тип данных вместо этого?

Ответ 3

Решение Гэри работает хорошо. Вот альтернатива.

Если вы используете драйвер JDBC Oracle, вызов prepareStatement() будет фактически возвращать OraclePreparedStatement, который имеет метод setFixedCHAR(), который автоматически вводит ваши входы с пробелами.

String sql = "select * from x where c = ?";
OraclePreparedStatement stmt = (OraclePreparedStatement) conn.prepareStatement(sql);
stmt.setFixedCHAR(1, "a");
...

Очевидно, что бросок является безопасным только при использовании драйвера Oracle.

Единственная причина, по которой я бы предположил, что вы используете этот ответ по Гэри, заключается в том, что вы можете изменить размеры столбцов, не изменяя свой код JDBC. Драйвер заполняет правильное количество пробелов без участия разработчика, который должен знать/управлять размером столбца.