Добавить группу в Hibernate Criteria-Query без проекции - PullRequest
5 голосов
/ 10 января 2010

У меня есть Criteria-Query, который соединяет вторую таблицу B, чтобы выбрать объекты из таблицы A. Проблема состоит в том, что этот запрос возвращает несколько объектов из таблицы A несколько раз. Но мне нужно, чтобы результаты были четкими.

Использование Criteria.DISTINCT_ROOT_ENTITY бесполезно, потому что это отфильтровывает множественные вхождения после выполнения SQL-запроса. Поэтому, когда я ограничиваю свои результаты 20 попаданиями, я получаю только 4, хотя есть больше записей, соответствующих моему запросу.

В чистом SQL я просто могу добавить «GROUP BY ID» к запросу, и все в порядке, потому что соединение таблицы B используется только для выбора объектов из таблицы A. Но с Criteria-API я не могу сделай это. Единственный способ добавить «GROUP BY» - использовать проекции. Но потом я получаю скалярные значения, а не реальный экземпляр моего класса. Использование SQL-ограничений также не работает, потому что hibernate добавляет ложное «1 = 1» после моего предложения «GROUP BY». (

Есть идеи?

Ответы [ 4 ]

2 голосов
/ 29 ноября 2011

Вы пытались использовать что-то подобное?

    ICriteria criteria = dc.GetExecutableCriteria(RepositoryInstance.Session)
            .SetProjection(Projections.distinct(Projections.projectionList()
                    .add(Projections.property("Prop1"), "Prop1")
                    .add(Projections.property("Prop2"), "Prop2")
                    .add(Projections.property("Prop3"), "Prop3")
                    .add(Projections.property("Prop4"), "Prop4")));
    result = criteria.List();

Вы можете динамически добавлять свойства через отражение класса.

Это создает SQl следующим образом: select distinct prop1,prop2,prop3,prop4 from yourClass

Я не включил DetachedCriteria dc, поскольку это не имеет значения.

1 голос
/ 25 сентября 2013

ГРУППА ПО БЕЗ ПРОЕКЦИИ: Это невозможно, так как это имеет смысл, во многих ответах вы можете найти, но большинство людей не хотят использовать проекцию, потому что они требуют, чтобы они проецировали каждый атрибут , но требование состоит в том, что бин должен быть спроецирован. (и вернул в результате). В приведенном ниже примере я попытался project требуемый bean как результирующий объект.

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

Вот то, чего я хотел достичь

select p.* FROM parent p INNER JOIN child c ON p.id_parent=c.id_father
WHERE c.child_name like '%?%' AND p.parent_name like '%?%' 
group by p.id_parent

В Java-коде я хотел, чтобы p.* был классом Parent, который является моим компонентом сущности, и я хотел, чтобы он был уникальным. Один из способов - получить список результатов в множестве, но мне не нравится этот способ по многим причинам. :)

Итак, я создал Критерии из Child.class вместо Parent.class, и этот трюк сработал для меня.

Criteria c = session.createCriteria(Child.class,"c");// starting from Child
    c.add(Restrictions.like("childName",   "abc", MatchMode.ANYWHERE));
    c.createAlias("parent", "p"); //remember parent is an attribute in Child.class
    c.add(Restrictions.like("p.parentName",   "xyz", MatchMode.ANYWHERE));
    c.setProjection( Projections.projectionList().add(Projections.groupProperty("parent"))); //projecting parent which is an attribute of Child.class

    List<Parent> result = c.list(); //get the result
    for (Parent p: result) {
        System.out.println(p);
    }

Если у вас все еще нет идеи, вот мои сопоставленные классы Entity Bean.

package com.mazhar.beans;

import static javax.persistence.GenerationType.IDENTITY;

import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "parent")
public class Parent {
    private Integer idParent;
    private String parentName;
    private List<Child> childs;

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "id_parent")
    public Integer getIdParent() {
        return idParent;
    }
    public void setIdParent(Integer idParent) {
        this.idParent = idParent;
    }

    @Column(name = "parent_name")
    public String getParentName() {
        return parentName;
    }
    public void setParentName(String parentName) {
        this.parentName = parentName;
    }

    @OneToMany(fetch=FetchType.LAZY, mappedBy="parent", cascade=CascadeType.ALL)
    public List<Child> getChilds() {
        return childs;
    }
    public void setChilds(List<Child> childs) {
        this.childs = childs;
    }

}

и мой детский класс

package com.mazhar.beans;

import static javax.persistence.GenerationType.IDENTITY;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name = "child")
public class Child {
    private Integer idChild;
    private String childName;
    private Parent parent; //this actually we projected in criteria query.

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "id_city", unique = true, nullable = false)
    public Integer getIdChild() {
        return idChild;
    }

    public void setIdChild(Integer idChild) {
        this.idChild = idChild;
    }

    @Column(name = "city_name", nullable = false)
    public String getChildName() {
        return childName;
    }

    public void setChildName(String cName) {
        this.childName = cName;
    }

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "id_father")
    public Parent getParent() {
        return parent;
    }

    public void setParent(Parent parent) {
        this.parent = parent;
    }
}
0 голосов
/ 15 мая 2015

Основная проблема группировки без проецирования, как вы хотите, состоит в том, что в некоторых СУБД, таких как Oracle, это не будет работать, Oracle возвратит ошибку.

Если вы группируете выборку, вы должны группировать по всем неагрегируемым полям, которые вы выбираете. Например, MySQL не имеет этого ограничения.

Подход, который я использовал, заключается в выборе только id в качестве проекции groupProperty со всеми фильтрами, упорядочениями и количеством результатов. Затем выполните другую фильтрацию запросов с этими полученными идентификаторами. Таким образом, реализация будет независима от СУБД.

0 голосов
/ 11 января 2010

Можно написать реальные запросы SQL, которые Hibernate может использовать для возврата сущностей. Поэтому, если вам действительно нужно, вы можете обойти HQL и написать именно тот запрос, который вам нужен, с указанием в нем значения GROUP BY.

Подробнее см. здесь .

Например, вы можете определить запрос примерно так в вашем файле hbm.xml:

<sql-query name="exampleQuery">
<query-param name="param" type="int"/>
<return alias="x" class="foo.bar.X"/>
<return alias="y" class="foo.bar.Y"/>
    <![CDATA[
        select {x.*}, {y.*}
        from XEntity x
        inner join YEntity y on y.xId = x.id
        where y.value = :param
    ]]>
</sql-query>

Обратите внимание на сокращенный синтаксис {x. } и {y. } для выбора всех свойств объекта X и объекта Y

.
...