Тип данных 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() к имени столбца (которое определено) в запросе на обновление.