【问题标题】:Adding a Projection to a List in Hibernate在 Hibernate 中向列表添加投影
【发布时间】:2012-08-04 20:06:21
【问题描述】:

我有一个名为 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 上创建投影?

【问题讨论】:

    标签: hibernate projection


    【解决方案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();
    }
    

    【讨论】:

      【解决方案2】:

      这不可能像你期望的那样。 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 或类似的东西。

      【讨论】:

      • 感谢您的回复。我仍在努力做到这一点。正如你提到的,我已经尝试了一些选项。我急切地去取。我做了 aliasToBean 转换。这工作正常。但显示并未将电子邮件合并为一行。我在单独的行中获取记录。我想组合结果,然后将其发送到表示层。您能否提供一些示例,说明如何按照您在第二个要点中的建议进行分组?
      【解决方案3】:

      即使有点晚,但永远不会太晚, 最后,您使用列表进行正常投影 在休眠中可以使用此返回一行:

      .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
      

      详情请看:Criteria.DISTINCT_ROOT_ENTITY vs Projections.distinct

      【讨论】:

      • 感谢您的回答。下次检查如何更好地格式化代码和链接。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-11
      • 1970-01-01
      • 2015-10-09
      • 1970-01-01
      • 2012-11-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多