jpa установить where-предложение в запросе для полей более 2 mapjoins - PullRequest
4 голосов
/ 14 февраля 2012

Я пытаюсь построить сложный запрос. Мои сущности выглядят следующим образом:

@Entity
public class Configuration{

   @Id
   @Column(name="CONF_ID")
   protected Long configurationId;

   @ManyToMany
   @MapKey(name="componentType")
   @JoinTable(name="CONF_COMP",
      joinColumns={@JoinColumn(name="CONF_ID", referencedColumnName="CONF_ID")},
      inverseJoinColumns={@JoinColumn(name="COMP_ID", referencedColumnName="componentId")})
   protected Map<String, Component> components;
}

А

@Entity
public class Component {

    @Id
    protected long componentId; 
    @ElementCollection
    protected Map<String, String> properties;

    @ManyToMany(mappedBy="components")
    private List<Configuration> configurations;

    @Column(name="COMP_TYPE")
    protected String componentType;
 }

Моя проблема заключается в правильном запросе к полю properties. Я не могу создать запрос, чтобы получить все конфигурации, где компонент A имеет свойство Prop1 = 1, а компонент B имеет свойство Prop2 = 2. Я попробовал следующее безуспешно.

Root<Configuration> conf = cq.from(Configuration.class);    
MapJoin<Configuration, String, Component> compJoin = conf.join(Configuration_.components, JoinType.LEFT);
MapJoin<Component, String, String> propJoin = compJoin.join(Component_.properties, JoinType.LEFT); 

    Predicate p1 = cb.and(
                cb.equal(mapJoin.key(), "A"),
                cb.equal(propJoin.key(), "Prop1"), cb.equal(propJoin.value(), "1"));
    Predicate p2 = cb.and(
                cb.equal(mapJoin.key(), "B"),
                cb.equal(propJoin.key(), "Prop2"), cb.equal(propJoin.value(), "2"));

Predicate[] pArray = new Predicate[]{p1, p2};
cq.where(pArray);
cq.select(conf).distinct(true);

РЕДАКТИРОВАТЬ: запрос, выводимый регистратором, выглядит следующим образом:

SELECT DISTINCT  t2.CONF_ID, t2.DTYPE, t2.TOTALPRICE, t2.NAME
 FROM CONFIGURATION t2 
 LEFT OUTER JOIN (CONF_COMP t3 JOIN COMPONENT t1 ON (t1.COMPONENTID = t3.COMP_ID)) ON (t3.CONF_ID = t2.CONF_ID) LEFT OUTER JOIN Component_PROPERTIES t0 ON (t0.Component_COMPONENTID = t1.COMPONENTID) 
WHERE (((((t1.COMP_TYPE = ?) AND (t0.PROPERTIES_KEY = ?)) AND t0.PROPERTIES LIKE ?) AND (((t1.COMP_TYPE = ?) AND (t0.PROPERTIES_KEY = ?)) AND t0.PROPERTIES LIKE ?)) AND (t2.DTYPE = ?))
bind => [7 parameters bound]

Я предполагаю, что он пытается найти Конфигурацию, где все условия применимы к ТО ЖЕ компоненту, может быть? Это работает, если я применяю ограничения только к одному Компоненту, но я получаю пустой список результатов при применении 2 или более, хотя есть записи в БД, которые удовлетворяют условиям.

UPDATE

После того, как я последовал предложению Pimgd, я получил запрос, который выглядит следующим образом:

SELECT DISTINCT t1.CONF_ID, t1.DTYPE, t1.TOTALPRICE, t1.NAME
FROM CONFIGURATION t1 LEFT OUTER JOIN (CONF_COMP t2 JOIN COMPONENT t0 ON (t0.COMPONENTID = t2.COMP_ID)) ON (t2.CONF_ID = t1.CONF_ID) 
WHERE ((( 
    t0.COMPONENTID IN (SELECT t3.COMPONENTID 
                        FROM COMPONENT t3 LEFT OUTER JOIN Component_PROPERTIES t4 ON (t4.Component_COMPONENTID = t3.COMPONENTID) 
                        WHERE ((t4.PROPERTIES_KEY = Brand) AND (t4.PROPERTIES = Intel)))) 

    AND 
    t0.COMPONENTID IN (SELECT t6.COMPONENTID 
                        FROM COMPONENT t6 LEFT OUTER JOIN Component_PROPERTIES t7 ON (t7.Component_COMPONENTID = t6.COMPONENTID) 
                        WHERE ((t7.PROPERTIES_KEY = Capacity) AND t7.PROPERTIES LIKE 4GB%))))

Один критерий работает, два не дают результатов.

Любая помощь высоко ценится!

1 Ответ

1 голос
/ 21 февраля 2012

Я скажу это mapJoin.key() должно быть compJoin.key().

Кроме этого, я не вижу ничего плохого.

Если мое предлагаемое решение не работает, вот несколько бонусных вопросов для вас:

  • Какие результаты вы получаете?
  • Можно ли показать сгенерированные запросы?

Интересный момент, который я заметил: WHERE (((((t1.COMP_TYPE = ?) AND (t0.PROPERTIES_KEY = ?)) AND t0.PROPERTIES LIKE ?) AND (((t1.COMP_TYPE = ?) AND (t0.PROPERTIES_KEY = ?)) AND t0.PROPERTIES LIKE ?)) AND (t2.DTYPE = ?))

Что если я возьму этот бит ... только для пояснения. ((t1.COMP_TYPE = ?) AND (t0.PROPERTIES_KEY = ?))

WHERE (((SNIP AND t0.PROPERTIES LIKE ?) AND (SNIP AND t0.PROPERTIES LIKE ?)) AND (t2.DTYPE = ?))

Так что да ... Теперь, как бы вы исправили ваш запрос? К сожалению, я не эксперт. Но что я знаю, так это то, что если вы сделали запрос с указанием куда идти WHERE t2.CONF_ID IN (Subselect for criteria A) AND t2.CONF_ID IN (Subselect for criteria B).

Мое предложение состояло бы в том, чтобы искать подвыборы и злоупотреблять ими. JPA 2.0, Criteria API, Subqueries, In Expressions объясняет это и кажется достаточно уместным.

...