Stefan Falk
Stefan Falk

Reputation: 25397

How can I use intoGroups() if the returned type is not a known type?

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

Answers (1)

Stefan Falk
Stefan Falk

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

Related Questions