2015-12-04 4 views
9

У меня есть устаревшая база данных (фактически файлы Cobol), к которой я обращаюсь, используя запатентованный JDBC-драйвер с Hibernate/JPA.JPA Query возвращает nulls - Composite Key с нулевым столбцом

Объект имеет составной первичный ключ с 2 столбцами: CODE и SITE.

В унаследованных данных имеются записи для одной и той же CODE, которые могут иметь либо конкретное значение для SITE, или может быть запись с NULL в SITE столбце, который представляет «Все сайты». Теория этого файла, если вы не можете найти CODE для своего конкретного SITE, то вы просматриваете запись с помощью NULL в SITE («catch-all»).

Я не могу изменить структуру этой «таблицы», поскольку это потребует перезаписи больших частей старой системы Cobol, которую мы не хотим делать. Я также не могу создавать представления данных.

Теперь, когда я делаю em.find с первичным ключом класса композит, содержащий конкретный code и нуль для site, то Hibernate правильно находит запись соответствия со значением NULL в столбце - Все хорошо!

Но если я пытаюсь сделать запрос, используя em.createQuery, подобный следующему:

SELECT x FROM TareWeight x WHERE x.pk.code = 'LC2' 

, для которых есть 2 записей, она возвращает пустой объект в результирующий список для записи с NULL в SITE column.

Если я беру SQL, который Hibernate использует для этого запроса, тогда «база данных» возвращает две записи: одну с сайтом NULL и одну с определенным сайтом. Похоже, что когда Hibernate загружает Entities из этих результатов, он сопоставляет его с нулевым объектом Entity.

Так что либо Hibernate поддерживает его, либо нет. Почему бы работать em.find, но не em.createQuery?

Я знаю, что this question аналогичен, но ответы, похоже, предполагают, что это невозможно. Но ясно, что Hibernate может правильно найти, так почему запрос не работает?


EDIT: ОК, так что я нашел определение в NullableStringType класс на this Hibernate JIRA Issue и добавить его в мой проект.

Если добавить @Type определения на site колонки ПК с помощью этого класса типа, то я могу успешно получить ненулевые Сущности назад от SELECT, запроса, с site поля, содержащего все, что строка текстом я определяю как представление от null.

Однако он по-прежнему ведет себя по-другому. find возвращает объект с полем site, содержащим null, но запрос возвращает объект с полем site, содержащим «NaN» (представление по умолчанию null).

По-прежнему кажется, что они должны вести себя одинаково.


UPDATE 2: Некоторые из вас хотят знать особенности о «базе данных» в вопросе.

Это движок Genesis RDBMS, написанный Trifox Inc.. Данные хранятся в файлах AcuCobol (теперь Micro Focus) Vision.

У нас есть набор настроек для перевода пустых (SPACES) буквенно-цифровых полей в NULL, поэтому наши записи файлов, содержащие пробелы для поля PK, переводятся в NULL. Я могу специально выбрать соответствующую запись, используя WHERE site_id IS NULL, поэтому RDBMS обрабатывает эти пустые поля как SQL NULL.

Сказав все, что я не верю, что эта проблема имеет какое-либо отношение к «базе данных», помимо того факта, что необычные поля PK являются нулевыми.

Более того, если я зарегистрирую SQL, который Hibernate использует как для find, так и для запроса, они почти идентичны.

Вот SQL для находки:

select tareweight0_.CODE as CODE274_0_, tareweight0_.SITE as SITE274_0_, 
     tareweight0_.WEIGHT as WEIGHT274_0_ from TARE_WEIGHT tareweight0_ 
     where tareweight0_.CODE=? and tareweight0_.SITE=? 

И вот SQL для запроса:

select tareweight0_.CODE as CODE274_, tareweight0_.SITE as SITE274_, 
     tareweight0_.WEIGHT as WEIGHT274_ from TARE_WEIGHT tareweight0_ 
     where tareweight0_.CODE=? and tareweight0_.SITE=? 

Как вы можете видеть, единственное различие в названиях столбца псевдонимов.


UPDATE 3: Вот некоторые примеры данных:

select code, site, weight from tare_weight where code like 'LC%'; 

CODE SITE WEIGHT 
------ ------ ------- 
LC1    .81 
LC2    .83 
LC2 BEENLH  .81 
LC3    1.07 
LC3 BEENLH 1.05 
LC4    1.05 
LCH1    .91 
LCH2    .93 
LCH2 BEENLH  .91 
LCH6    1.13 
LCH6 BEENLH 1.11 

и поиск специально для NULL:

select code, site, weight from tare_weight where code like 'LC%' and site IS NULL; 

CODE SITE WEIGHT 
------ ------ ------- 
LC1    .81 
LC2    .83 
LC3    1.07 
LC4    1.05 
LCH1    .91 
LCH2    .93 
LCH6    1.13 
+0

Если вы сравниваете поиск по вашему запросу, не следует ли в предикате вашего запроса «x.pk.site равно null»? Я знаю, что это не отвечает на ваш вопрос о том, почему запрос возвращает весь нулевой объект, но что-то, что нужно учитывать при попытке отладить поведение между этими двумя подходами. – dispake

+0

Справедливый комментарий. Поэтому, если я добавлю предложение where и AND.pk.site IS NULL, я все равно получаю единственный нулевой объект в результирующем списке. Таким образом, они по-прежнему ведут себя по-разному, даже несмотря на то, что SQL теперь по существу тот же. Следует отметить, что запрос находит правильное количество записей, а в результирующем списке всегда содержится правильное количество записей, просто они равны нулю, если столбец сайта равен нулю. – DuncanKinnear

+0

Что такое ваша СУБД? Также, пожалуйста, покажите некоторые данные примера. Пожалуйста, прекратите использовать «null», когда база данных фактически не содержит SQL NULL или когда соответствующее значение Java используется в типах Nullable. NULL обрабатывается специально SQL, как и NaN Java. Прежде всего, они никогда не сравниваются ни с чем, включая себя. Также важно знать, когда что-то является строковым представлением некоторого нестрокового значения. Важно понять, какое значение видит тот уровень и что он переходит на следующий уровень. – philipxy

ответ

7

«Так как они поддерживают его, либо нет "

TL; DR Это ожидание/чувство необоснованно. Неподдерживаемая функциональность в вашей ссылке (& минута) - это именно ваша. «Не поддерживать» означает, что если вы это сделаете, Hibernate сможет делать все, что захочет. Вам повезло, что они (кажется) возвращают разумные ценности. (Хотя это всего лишь догадка, как они действуют. У вас нет спецификации.) Нет причин ожидать чего-либо, не говоря уже о последовательности. Когда поведение является следствием какого-то неподдерживаемого случая, любое «почему» скорее всего является просто артефактом того, как код был написан с учетом других случаев.


Вот (п старая) поддержка потоков ответил на Hibernate Team:

Заголовок сообщения: Составной ключ с нулевым значением для одного столбца
PostPosted: пн 3 июля 2006 2:21 утра

у меня есть таблица с составным первичным ключом, и я создал следующие отображения для table.As его можно вставить нулевое значение для любого столбца составного ключа до тех пор, как сочетание все столбцы: Уникальный, у меня есть запись в таблице, которая имеет нулевое значение для столбца V_CHAR2 (который является частью составного ключа). когда я выполняю запрос на этой сущности, я получаю нулевые значения для записей, которые имеют значение значения столбца V_CHAR2. Что плохого в моем картографирования и реализации ..

Добавлено: Вт 11 июля 2006 9:09 утра
Hibernate Team

первичный ключ не может быть нулевым (ни полностью или частично)

Добавлено: Сб 6 января 2007 5:35 утра
Hibernate Team

Извините, что disapoint вас, но null в первичном ключе не поддерживается - прежде всего потому, что для выполнения объединений и сравнений потребуется много больно глупый код, который просто не нужен нигде ... думаю об этом, и вы будете см. (например, как вы делаете правильно присоединиться такую ​​таблицу)

Это не удивительно, потому что NULL не допускается в колонке ПК в SQL. Объявление PRIMARY KEY является синонимом UNIQUE NOT NULL. NULL ничем не отличается от (ошибочного) намерения, что некоторая незаписанная величина не известна как равная. (Ваши ожидания какого-то исключения по крайней мере в некоторых случаях NULL в PK , равном, NULL в состоянии противоречит SQL.) Учитывая, что NULL не допускается в значениях PK, мы можем ожидать, что оптимизация PK связана с 1 : 1 сопоставления и вместо наборов строк, чтобы предположить, что нет нулей, когда это удобно. Можно ожидать, что Hibernate решила не беспокоиться о том, что их реализация сделала с случаями, которые не должны возникать в SQL. Жаль, что они не говорят вам об этом при компиляции или исполнении. Надеюсь, это указано в документации.)

Даже find отличается от createQuery re NULL неудивительно. Первое включает в себя одно значение, в то время как последнее включает в себя то, что, как ожидается, будет множеством (а не мешками) строк без NULL (но это не так).

Обходным решением может быть не обрабатывать любой столбец первичного ключа как NULL, а как фактическую строку пробелов в памяти. (Независимо от того, что это означает, учитывая ваш стек хранения/СУБД/гибернации/JPA/Java. Вы не предоставили нам достаточно информации, чтобы узнать, будет ли препятствовать вашему представлению Cobol базы данных, если не сопоставить пространства с NULL для вашей JPA). С вашими данными вы можете объявить индекс UNIQUE в столбцах.

+0

Да, я тоже нашел эту тему, но, как я уже сказал, я ** могу ** «найти» запись успешно, а Hibernate возвращает Entity с полем сайта, содержащим null. Поэтому либо они поддерживают это, либо нет. Почему 'find' отличается от запроса? Ваше обходное решение не является жизнеспособным. Мы не можем изменить структуру или данные в этой «таблице», не переписывая части существующего кода Cobol, чего мы не будем делать. Теперь у меня есть жизнеспособное обходное решение, которое является «NullableStringType», как указано выше, но поведение по-прежнему несовместимо. – DuncanKinnear

+1

Итак, если бы мужчина без ног хотел кататься на велосипеде, вы бы издевались над ним и говорили ему, что велосипеды не поддерживают безногих людей? Или вы попытаетесь предложить ему решение для удовлетворения своих желаний? Я действительно ненавижу быть одним из ваших клиентов, чтобы постоянно говорить, что Нет, они не могут иметь такую ​​функциональность, которую они хотели, потому что программное обеспечение не поддерживает его. Полагаю, именно поэтому наша компания была вокруг и успешна почти 30 лет. Потому что мы никогда не говорим, что что-то невозможно. Это называется [Kiwi Can Do] (http://www.nzherald.co.nz/nz/news/article.cfm?c_id=1&objectid=11188987). – DuncanKinnear

+0

Кажется, есть недоразумение. Я пытаюсь прояснить неправильные представления и ответить на фактически заданный вопрос. (Обходной путь вызван, но это не вопрос. Если реальный вопрос касается обходного пути, пожалуйста, сделайте это ясно.) (И в вашем заголовке тоже для большего количества просмотров.) Ваше предисловие/предпосылка: «Таким образом, либо Hibernate поддерживает это, либо это не так ». Unsound. Вопрос: «Зачем работать em.find, но не em.createQuery?«A: find * не работает на SQL (с NULL <> NULL) и в любом случае либо может что-либо сделать, потому что контракт нарушен. (Мы точно не знаем, когда работает @ Type/NullableStringType.) – philipxy

Смежные вопросы