在Hibernate中为List添加一个Projection

时间:2012-08-07 18:50:01

标签: hibernate projection

我有一个名为Order的@Entity我有一个字段或一个名为orderEmails的成员变量,如下所示。

@Entity
@Table(name = "order")
public class Order {

@Id
@Column(name = "order_int")
private Long id;

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "order_int", insertable = false, updatable = false)
private List<OrderEmail> orderEmails;

... }

我正在尝试在此订单上创建预测,这意味着我想从Order实体中选择一些特定列,并从OrderEmail实体中选择一列

但是,当我在orderEmails字段上创建投影时。我没有得到完整的电子邮件列表。这就是我想要的。这是我正在尝试的代码

ProjectionList columnList = Projections.projectionList();
...
columnList.add(Projections.property("id").as("id"));
...
columnList.add(Projections.property("orderemails.EmailAddress").as("email"));

注意,我也试过了 columnList.add(Projections.property( “orderemails”)作为( “电子邮件”)。); 并将电子邮件(in as)更改为List,但没有帮助

是否可以在Hibernate中的List上创建投影?

3 个答案:

答案 0 :(得分:1)

我猜hibernate没有提供这样的功能。为此,您必须使用数据库特定的函数,如Oracle的LISTAGG或MySQL的GROUP_CONCAT。它会将所有电子邮件(字符串)分组为一个列,结果将是:

ORDER_ID     EMAILS
1            nemo@email, dory@email, whale@email
2            spongebob@email, squarepants@email

你可以使用sqlProjection在Hibernate中使用特定于数据库的函数,所以代码看起来像这样:

public List<Map> emailsByOrder(){
    Criteria c = session.createCriteria(Order.class,"order");

    Criteria critEmail = c.createCriteria("orderEmails", "emails");
    String listAgg = "LISTAGG({alias}.EMAIL_ADDRESS_COLUMN_NAME, ', ') WITHIN GROUP(ORDER BY {alias}.EMAIL_ADDRESS_COLUMN_NAME ASC) AS EMAILS";
    critEmail.setProjection(Projections.projectionList().add(Projections.groupProperty("order.idOrder").as("ORDER_ID"))
                                                        .add(Projections.sqlProjection(listAgg, new String[]{"EMAILS"}, new Type[]{new StringType()}))); 
    c.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);

    return (List<Map>) c.list();
}

答案 1 :(得分:0)

这不可能像你期望的那样。 Hibernate必须对根实体上的记录进行分组,它只对完整的实体进行记录。

  • 您可以加载完整的实体,以便稍后获取电子邮件并将其转换为内存。
  • 您获取每个电子邮件地址重复的根实体记录,并将它们分组在内存中

更新

List<Object[]> results = session.createCriteria(Order.class)
    .joinAlias("orderEmails", "email")
    .setProjection(Projections.projectionList()
        .add(Projections.property("id").as("id"))
        .add(Projections.property("email.EmailAddress").as("email")))
    .list<Object[]>();

Map<int, List<String>> groups = new Hashmap<int, List<String>>();
for (Object[] items : results)
{
    if (groups.containsKey((long)items[0]))
        groups.get((long)items[0]).add((String)items[1]);
    else
        groups.add((long)items[0], new List<String>().add((String)items[1]));
}

return groups;

而不是地图,也可以有dtos或类似的东西。

答案 2 :(得分:0)

即使它有点迟了但从来没有太晚, 您可以使用内部列表进行常规投影 在休眠状态下,可以使用以下命令返回一行:

.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)

详细信息:Criteria.DISTINCT_ROOT_ENTITY vs Projections.distinct