Reputation: 25397
I am having the use case where I construct my result by joining two tables ITEM
and ITEM_DESCRIPTION
. From there I am taking several columns which I then would like to conveniently convert into a list of objects. In my case these objects are actually DTO objects but of course they could be business objects as well.
This is the way I am doing it now:
public Map<Long, List<StoreItemDTO>> getItems(Long storeId) {
LOGGER.debug("getItems");
// Get all item_ids for the store
SelectHavingStep<Record1<Long>> where = this.ctx
.select(STORE_ITEM.ID)
.from(STORE_ITEM)
.where(STORE_ITEM.STORE_ID.eq(storeId))
// GROUP BY store_item.id
.groupBy(STORE_ITEM.ID);
// Get all store_item_details according to the fetched item_ids
TableLike<?> storeItemDetails = this.ctx
.select(
STORE_ITEM_DETAILS.ID,
STORE_ITEM_DETAILS.STORE_ITEM_ID,
STORE_ITEM_DETAILS.NAME,
STORE_ITEM_DETAILS.DESCRIPTION,
STORE_ITEM_DETAILS.STORE_LANGUAGE_ID
)
.from(STORE_ITEM_DETAILS)
.where(STORE_ITEM_DETAILS.STORE_ITEM_ID.in(where))
.asTable("storeItemDetails");
// Join the result and use
Field<Long> itemIdField = STORE_ITEM.ID.as("item_id");
Result<?> fetch = this.ctx
.select(
STORE_ITEM.ID.as("item_id"),
itemIdField,
storeItemDetails.field(STORE_ITEM_DETAILS.ID),
storeItemDetails.field(STORE_ITEM_DETAILS.NAME),
storeItemDetails.field(STORE_ITEM_DETAILS.DESCRIPTION),
storeItemDetails.field(STORE_ITEM_DETAILS.STORE_LANGUAGE_ID)
)
.from(STORE_ITEM)
.join(storeItemDetails)
.on(storeItemDetails.field(STORE_ITEM_DETAILS.STORE_ITEM_ID).eq(STORE_ITEM.ID))
.fetch();
Map<Long, ?> groups = fetch.intoGroups(STORE_ITEM.ID);
return null;
}
As you can see, the result should be a list of items where each item has an item-details in different languages:
StoreItemDTO
- Long id
// Maps language-id to item details
- Map<Long, StoreItemDetails> itemDetails
StoreItemDetails
- Long id
- String name
- String description
I couldn't find a version of intoGroups()
that would return a useful type. I could imagine to have something like Map<Long, List<Record>>
but I can't manage to do so.
However, there is a intoGroups(RecordMapper<? super R, K> keyMapper)
which could be what I am looking for. If a mapper would also allow me to actually convert the resulting records into a custom object like MyCustomPojo
then I could retrieve and convert the data quite conveniently. I don't know if this is somehow possible. Something like:
public static class MyCustomPojo {
public Long itemId;
// etc.
}
// ..
Map<Long, List<MyCustomPojo>> result = fetch.intoGroups(STORE_ITEM.ID, new RecordMapper<Record, List<MyCustomPojo>>() {
@Override
public List<MyCustomPojo> map(List<Record> record) {
// 'record' is grouped by STORE_ITEM.ID
// Now map each 'record' into every item of each group ..
return resultList;
}
});
But unfortunately the compiler only allows
fetch.intoGroups(new RecordMapper<Record, Result<?>>() {
@Override
public Result<?> map(Record record) {
return null;
}
});
Upvotes: 1
Views: 91
Reputation: 25397
After some fiddling around with the compiler it turns out that it can be done.
I had to "cheat" a little by declaring my resulting map as final
outside of the anonymous and I am actually not "using" the keyMapper
parameter as I am just returning null
.
This is what I came up with:
public Map<Long, StoreItemDTO> getItems(Long storeId) {
// Get all item_ids for the store
SelectHavingStep<Record1<Long>> where = this.ctx
.select(STORE_ITEM.ID)
.from(STORE_ITEM)
.where(STORE_ITEM.STORE_ID.eq(storeId))
.groupBy(STORE_ITEM.ID);
// Get all store_item_details according to the fetched item_ids
TableLike<?> storeItemDetails = this.ctx
.select(
STORE_ITEM_DETAILS.ID,
STORE_ITEM_DETAILS.STORE_ITEM_ID,
STORE_ITEM_DETAILS.NAME,
STORE_ITEM_DETAILS.DESCRIPTION,
STORE_ITEM_DETAILS.STORE_LANGUAGE_ID
)
.from(STORE_ITEM_DETAILS)
.where(STORE_ITEM_DETAILS.STORE_ITEM_ID.in(where))
.asTable("storeItemDetails");
// Join the result and use
final Field<Long> itemIdField = STORE_ITEM.ID.as("item_id");
Result<?> fetch = fetch = this.ctx
.select(
itemIdField,
storeItemDetails.field(STORE_ITEM_DETAILS.ID),
storeItemDetails.field(STORE_ITEM_DETAILS.NAME),
storeItemDetails.field(STORE_ITEM_DETAILS.DESCRIPTION),
storeItemDetails.field(STORE_ITEM_DETAILS.STORE_LANGUAGE_ID)
)
.from(STORE_ITEM)
.join(storeItemDetails)
.on(storeItemDetails.field(STORE_ITEM_DETAILS.STORE_ITEM_ID).eq(STORE_ITEM.ID))
.fetch();
final Map<Long, StoreItemDTO> itemIdToItemMap = new HashMap<>();
fetch.intoGroups(
record -> {
Long itemDetailsId = record.getValue(STORE_ITEM_DETAILS.ID);
// ... sake of compactness
StoreItemDetailsDTO storeItemDetailsDto = new StoreItemDetailsDTO();
storeItemDetailsDto.setId(itemDetailsId);
// ... sake of compactness
Long itemId = record.getValue(itemIdField);
StoreItemDTO storeItemDto = new StoreItemDTO();
storeItemDto.setId(itemId);
storeItemDto.getItemDetailsTranslations().put(languageId, storeItemDetailsDto);
StoreItemDTO itemDetailsList = itemIdToItemMap.get(itemId);
if(itemDetailsList == null) {
itemDetailsList = new StoreItemDTO();
itemIdToItemMap.put(itemId, itemDetailsList);
}
itemDetailsList.getItemDetailsTranslations().put(languageId, storeItemDetailsDto);
return null;
});
return itemIdToItemMap;
}
Since I am not sure if this is the most elegant solution one could have I'm still open for suggestions and willing to accept any answer that can elegantly shorten this code - if that is possible at this point. :)
Upvotes: 1