2010-07-23 8 views
0

У меня есть запрос с самостоятельным присоединением, который выглядит следующим образом:SqlResultSetMapping with self join table

выберите t1. , t2. из таблицы t1 левое внешнее соединение таблицы t2 на t2.LFT < t1.LFT и t2.RGT> t1.RGT И t2.REG_CODE_PAR = 'ALL' И t1.STATUS_CODE = 'A' И t2.STATUS_CODE = 'A'

Я использую @NamedNativeQuery с отображением результирующего набора, чтобы получить результат.

@NamedNativeQuery(
    name="findTree", 
    query="..... the query above", 
    resultSetMapping = "regionT") 

следующий результат установить отображение

@SqlResultSetMapping(name = "regionT" , entities ={ 
    @EntityResult( 
     entityClass = Tree.class 
     fields = { 
      @FieldResult(name = "regCode", column = "REG_CODE") 
      @FieldResult(name = "rgt", column = "RGT"), 
      @FieldResult(name = "lft", column = "LFT"), 
      @FieldResult(name = "name", column = "NAME"), 
      @FieldResult(name = "regCodePar", column = "REG_CODE_PAR"), 
      @FieldResult(name = "statusCode", column = "STATUS_CODE") 
     } 
    ), 
    @EntityResult(
     entityClass = TreeSelf.class 
     fields = { 
      @FieldResult(name = "regCode1", column = "REG_CODE") 
      @FieldResult(name = "rgt1", column = "RGT"), 
      @FieldResult(name = "lft1", column = "LFT"), 
      @FieldResult(name = "name1", column = "NAME"), 
      @FieldResult(name = "regCodePar1", column = "REG_CODE_PAR"), 
      @FieldResult(name = "statusCode1", column = "STATUS_CODE") 
     } 
    ) 
}) 

Класс объект содержит выглядит следующим образом.

@NamedNativeQuery(...) 
@SqlResultSetMapping(...) 
@Entity 
@Table(name = "table") 
public class Tree implements Serializable { 

    @Id 
    @Column(name = "REG_CODE") 
    private String regCode; ... ..getters and setters...} 

При запуске запроса с помощью em.createQuery («findTree»), я получаю один и тот же объект в обоих

1-й и 2-й элементы возвращенного массива объектов. Даже если я создаю класс под названием TreeSelf, который идентичен Дерево и использовать его в качестве 2-го

EntityResult вместо того, 2 EntityResults используя тот же entityClass, я получаю то же

результат.

Может кто-нибудь указать, что случилось с конфигурацией?

+0

Возможно, вы имели в виду 'select t1. *, T2. *' Вместо 'select t1., T2.'? Похоже, что форматирование сработало. –

ответ

1

Посмотрим, понял ли я ваш вопрос. Вы ожидаете захватить два объекта Tree из каждой исходной строки запроса. Первый объект должен быть сформирован из столбцов t1. Второй объект должен быть сформирован из столбцов t2. Вопреки ожиданиям, вы фактически получаете два экземпляра, сформированных из t1. Не отображаются экземпляры с t2. Вы сделали объект Doppelganger Entity для Tree под названием TreeSelf во время отладки, но TreeSelf в конечном счете не нужен, и вы хотите избавиться от него. Остановите меня, если что-то не так.

Я думаю, проблема связана с неоднозначными именами столбцов. Каждое имя столбца в собственном запросе появляется дважды, один раз от t1 и один раз от t2. Кажется, что обработчик результата произвольно выбирает первое вхождение каждого неоднозначного имени столбца для объектов Tree. Я удивлен, что работает вообще. Я бы ожидал, что SQLException жалуется на двусмысленность ссылки на столбец.

Кроме того, вы уверены, что хотите левое внешнее соединение? Что делать, если не найдено ни одного совпадения для строки t1? Он будет спарен со всеми NULL в столбцах t2. Тогда у вас есть нулевая сумма Tree. Я думаю. Я даже не знаю, что будет делать в этом случае обработчик результатов. Возможно, вам нужно внутреннее соединение?

Рассмотрите возможность перевода этого собственного запроса в запрос JPQL. (API JPA Criteria API так же хорошо, но я нахожу его более громоздким для примеров.) Вот версия JPQL родного запроса:

SELECT t1, t2 
FROM Tree t1, Tree t2 
WHERE t2.lft < t1.lft AND t2.rgt > t1.rgt AND t2.regCodePar = 'ALL' AND 
     t1.statusCode = 'A' AND t2.statusCode = 'A' 

нотабене: Это изменяет семантику соединения на внутреннюю, а не на внешнюю.

Вот эскиз кода, который может выполнить этот запрос:

EntityManager em = ... // EntityManager by injection, EntityManagerFactory, etc. 
String jpql  = ... // Like example above 
TypedQuery<Object[]> q = em.createQuery(jpql, Object[].class); 

for (Object[] oa : q.getResultList()) { 
    Tree t1 = (Tree)oa[0]; 
    Tree t2 = (Tree)oa[1]; 
} 

В случае, если вы застряли с нативным запросом по какой-либо причине, вот как вы можете работать вокруг имени столбца двусмысленности. Вместо того, чтобы начинать собственный запрос, например select t1.*, t2.*, укажите каждый столбец с номером AS. Предложение SELECT будет напоминать это:

SELECT t1.REG_CODE AS t1_REG_CODE, t1.RGT AS t1_RGT, (... rest of t1 cols ...), 
     t2.REG_CODE AS t2_REG_CODE, t2.RGT AS t2_RGT, (... rest of t2 cols ...) 

атрибут column в каждом FieldResult необходимо изменить соответствующим образом. Поэтому атрибуты column при первом EntityResult должны начинаться с t1_, а второе должно начинаться с t2_.

Я бы смиренно рекомендовал удалить исходный запрос и обработчик результатов sql и использовать JPA Query Language или Criteria API, если вы можете найти способ.

Обновление: Как подтверждено в ваших комментариях, полезный ответ на ваш вопрос должен сохранять левую (внешнюю) семантику соединения. К сожалению, JPQL и API критериев не поддерживают сложные условия левого соединения. Невозможно квалифицировать левое соединение JPQL с явным условием ON.

К моему знанию, единственный способ сделать левое внешнее соединение под спецификацией - это пересечение сущности. Затем реализация JPA генерирует условие ON, которое проверяет равенство идентичности. Соответствующие биты спецификации равны 4.4.5 «Соединения» и 4.4.5.2 «Левые внешние соединения».

Чтобы удовлетворить этому ограничению, каждый Tree, который вы хотите, чтобы присоединиться к своему окончательному родительскому члену, должен иметь дополнительный столбец, в котором хранится идентификатор конечного родителя. Возможно, вы сможете обманывать это ограничение различными способами (представления?). Но путь наименьшего сопротивления, похоже, изменяет собственный запрос, чтобы использовать аргументы с псевдонимом, удаляя TreeSelf и соответствующим образом обновляя mapper результатов. Cleverer solutions приветствуется, хотя ...

+0

Привет, Дэн, спасибо за понимание. Да, вы правильно поняли мой вопрос, но позвольте мне рассказать вам немного истории. Это существующий запрос в системе. Моя цель состоит в том, чтобы преобразовать его в API JPA Criteria, но столкнулся с проблемами из-за множественных условий в левом соединении (да, требуется левое соединение. Нулевой случай обрабатывается в приложении). Я разместил этот вопрос здесь: http://stackoverflow.com/questions/3280381/how-to-specify-multiple-conditions-on-left-join-using-jpa-criteria-api – Jonathan

+0

Еще одна проблема с JPQL, с которой я столкнулся, заключается в том, что Структура данных дерева является иерархией в таблице. Каждая строка имеет столбец (parent_id), который ссылается на PK другой строки в таблице. Выполнение @ManyToOne @JoinColumn (name = "parent_id") частное дерево деревьев; Я бы получил своего непосредственного родителя. Результат t2 в собственном запросе фактически возвращает родительскую запись верхнего уровня, а не непосредственный родитель. Используя JPQL, мне нужно будет использовать Tree.getTree(). GetTree(). GetTree() ..... getTree() до 6 или 7 уровней, чтобы получить родителя верхнего уровня. – Jonathan

+0

Все, что я сказал, мое предпочтение заключается в использовании критериев JPA, если можно разрешить объединение нескольких условий и проблемы с родителями. Если у вас есть мнение, я бы очень хотел это услышать. Спасибо – Jonathan