2

我有一个名为 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 上创建 Projections,这意味着我想从 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").as("email")); 并将电子邮件(按原样)更改为列表,但没有帮助

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

4

3 回答 3

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();
}
于 2016-09-23T18:53:36.267 回答
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 或类似的东西。

于 2012-08-14T07:29:47.727 回答
0

即使有点晚了,但永远不会太晚,您可以在里面使用列表进行正常投影,然后最后可以在休眠状态下使用以下命令返回一行:

.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)

此处的详细信息:Criteria.DISTINCT_ROOT_ENTITY 与 Projections.distinct

于 2017-11-09T08:54:06.153 回答