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

У меня есть проблема с обработкой драйвера JDBC Oracle 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 также. Теперь сложно, когда я хочу использовать переменную связывания:

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();

РЕДАКТИРОВАТЬ: Теперь это ограничения:

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

РЕДАКТИРОВАТЬ: причины этих ограничений, потому что я задаю этот вопрос с точки зрения разработчика jOOQ, библиотеки абстракции базы данных. Поэтому мои требования состоят в том, чтобы предоставить очень общее решение, которое ничего не нарушает в клиентском коде jOOQ. Вот почему я не очень большой поклонник обходного пути. И вот почему у меня нет доступа к этому CHAR объявление столбца. Но все же я хочу иметь возможность разобраться с этим делом.

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

6 ответов

Решение

Если ты хочешь

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

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

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

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

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

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

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

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

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

У меня есть хорошее решение для этого. Вы должны добавить одно свойство при получении соединения из базы данных.

NLS_LANG=american_america.AL32UTF8

или в соединении Java вы можете использовать следующий код:

java.util.Properties info = new java.util.Properties();
info.put ("user", user);
info.put ("password",password);
info.put("fixedString","TRUE");
info.put("NLS_LANG","american_america.AL32UTF8");
info.put("SetBigStringTryClob","TRUE");
String url="jdbc:oracle:thin:@"+serverName;
log.debug("url="+url);
log.debug("info="+info);
Class.forName("oracle.jdbc.OracleDriver");
conn  = DriverManager.getConnection(url,info);

Другой способ изменить ваш SQL как

select * from x where NVL(TRIM(c),' ') = NVL(TRIM('a'),' ')

Просто добавьте RTRIM() к имени столбца (которое определено) в запросе на обновление.

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