Adding a Projection to a List in Hibernate

a 夏天 提交于 2019-12-02 01:45:42

问题


I have a @Entity called Order in this I have a field or a member variable called orderEmails as show below.

@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;

... }

I am trying to create Projections on this Order , meaning I want to select some specific columns from the Order entity and a column from the OrderEmail entity

But, when I create a projection on the orderEmails field. I don't get the complete list of emails. Which is what I want. Here is the code that I am trying

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

Note, I have also tried columnList.add(Projections.property("orderemails").as("email")); and changed the email (in as) to be a List, but didn't help

Is it possible to create a projection on a List in Hibernate ?


回答1:


I guess hibernate does not provide such a function. To do this you will have to use database specific functions like LISTAGG from Oracle or GROUP_CONCAT from MySQL. It will group all emails (the String) into one colum, so the result would be:

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

You can use database specific functions in Hibernate with sqlProjection, so the code will look something like this:

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:


it's not possible like you expect. Hibernate has to group the records on the root entity which it only does for complete entities.

  • you could load the complete entity eager fetching the emails and transform it in memory later.
  • you fetch the root entity records duplicated for each email address and group them together in memory

Update:

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;

instead of the map one could also have dtos or something like that.




回答3:


Even it's a little bit late but never too late, You make your normal projection with a list inside then in the end it's possible in hibernate to return one line using this :

.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)

Details here: Criteria.DISTINCT_ROOT_ENTITY vs Projections.distinct



来源:https://stackoverflow.com/questions/11852230/adding-a-projection-to-a-list-in-hibernate

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!