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 ?
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();
}
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.
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