jOOQ 抽象记录到 DTO 映射

问题描述 投票:0回答:1

我写了一个 DAO 抽象,我的所有 DAO 都从它扩展而来。此 DAO 抽象提供了具有通用查询参数模型的典型远程分页。

它从多个表中获取连接数据。然后,这个连接数据以

List<Record> records
参数的形式提供给具体的 DAO,现在开发人员必须将这些
Record
实例转换为
DTO
实例(其中 DTO 实例可能需要从多个记录实例)。这是一个例子:

public class ProductViewDAO extends AbstractViewDAO<ProductRecord, ProductDTO, Long> {
...
    @Override
    protected TableOnConditionStep<Record> getViewJoins() {
        return Product.PRODUCT
                .leftJoin(ProductLang.PRODUCT_LANG)
                .on(ProductLang.PRODUCT_LANG.PRODUCTID
                        .eq(Product.PRODUCT.PRODUCTID));
    }

    @Override
    protected List<ProductDTO> recordsToView(List<Record> records) {
        List<ProductDTO> products = new ArrayList<>();
        Map<Long, ProductDTO> productMap = new HashMap<>();

        for (Record record : records) {
            Long productId = record.get(Product.PRODUCT.PRODUCTID);
            ProductDTO productDTO = productMap.get(productId);

            if (productDTO == null) {
                ProductRecord productRecord = record.into(new ProductRecord());
                productDTO = productRecord.into(new ProductDTO());
                List<ProductLangDTO> xLangs = new ArrayList<>();
                productDTO.setLangs(xLangs);
                products.add(productDTO);
                productMap.put(productId, productDTO);
            }
            ProductLangRecord xLangRecord = record.into(new ProductLangRecord());
            ProductLangDTO xLang = xLangRecord.into(new ProductLangDTO());
            productDTO.getLangs().add(xLang);

            if (xLang.getLangId().equals(requestContext().getLangId())) {
                productDTO.setLang(xLang);
            }
        }
        return products;
    }
...
}
...
public abstract class AbstractViewDAO<R extends UpdatableRecord<R>, P extends AbstractDTO, T> extends AbstractBaseDAO<R, T> {
...
  public List<P> query(final QueryParameters queryParameters) throws DataAccessException {
...
    var result = getViewQuery().where(equal(pk, ids)).fetch();
    return recordsToView(result);
  }
}



如何才能更轻松地做到这一点?

Java 或 jOOQ 是否提供帮助程序类来帮助自动化

Record
DTO
的映射?

java jooq
1个回答
0
投票

我通过引入一个新类

RecordToViewMapper
来解决这个问题,该类封装了从混合记录中提取唯一记录并将其映射到 DTO 的重复任务。

这不是最好的解决方案,但在此类的帮助下/通过使用此类,开发人员代码将足够干净/无样板。

public class RecordToViewMapper<E extends AbstractDTO, K extends Serializable> {

    private Class<E> clazz;
    private Field<K> groupField;
    private List<Field<?>> uniqueFields;

        public RecordToViewMapper(
            Class<E> clazz,
            Field<K> groupField,
            List<Field<?>> uniqueFields) {
        this.clazz = clazz;
        this.groupField = groupField;
        this.uniqueFields = uniqueFields;
    }

    /**
     * Helper-function that filters the given records to return a list,
     * that contains each record that fulfills the unique-criteria only once.
     *
     * @param records records
     * @return filtered records.
     */
    private Map<K, List<Record>> filterRecordsByUniqueFieldsAndGroupByIdField(List<Record> records) {
        final Map<String, Record> uniqueRecordMap = new LinkedHashMap<>();
        for (Record record : records) {
            String key = "";
            for (Field<?> uniqueField : uniqueFields) {
                String test = record.getValue(uniqueField).toString();
                key += test;
            }
            uniqueRecordMap.put(key, record);
        }
        final List<Record> uniqueRecords = uniqueRecordMap.values().stream().toList();
        final Map<K, List<Record>> result = new LinkedHashMap<>();
        for (Record record : uniqueRecords) {
            K key = record.getValue(groupField);
            if(result.containsKey(key)){
                List<Record> list = result.get(key);
                list.add(record);
            } else {
                List<Record> list = new ArrayList<Record>();
                list.add(record);
                result.put(key, list);
            }
        }
        return result;
    }

    /**
     * Convert the given records to the expected DTO class type
     * @param records records
     * @return dtos
     */
    private List<E> convertRecordsToView(List<Record> records) {
        List<E> viewDTOs = new ArrayList<>();
        for (Record record : records) {
            try {
                viewDTOs.add(record.into(clazz.newInstance()));
            } catch (InstantiationException e) {
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        return viewDTOs;
    }

    public List<E> extractRecords(List<Record> records) throws RuntimeException {
        Map<K, List<Record>> map = filterRecordsByUniqueFieldsAndGroupByIdField(records);
        List<Record> filteredRecords = new ArrayList<>();
        for (List<Record> v : map.values()) {
            // we expect that we always have exactly 1 record in this case per key.
            filteredRecords.add(v.get(0));
        }
        return convertRecordsToView(filteredRecords);
    }
    public Map<K, List<E>> extractRecordsGroupedBy(List<Record> records) throws RuntimeException {
        Map<K, List<Record>> map = filterRecordsByUniqueFieldsAndGroupByIdField(records);
        Map<K, List<E>> convertedMap = new LinkedHashMap<>();
        for (Map.Entry<K, List<Record>> entry : map.entrySet() ) {
            List<E> viewDTOs = convertRecordsToView(entry.getValue());
            convertedMap.put(entry.getKey(), viewDTOs);
        }
        return convertedMap;
    }
}

在 DAO 中的用法:

...
    private final RecordToViewMapper<ProductDTO, Long> productViewMapper = new RecordToViewMapper<>(
            ProductDTO.class,
            Product.PRODUCT.PRODUCTID,
            List.of(Product.PRODUCT.PRODUCTID));

    private final RecordToViewMapper<ProductLangDTO, Long> productLangViewMapper = new RecordToViewMapper<>(
            ProductLangDTO.class,
            Product.PRODUCT.PRODUCTID,
            List.of(ProductLang.PRODUCT_LANG.PRODUCTID, ProductLang.PRODUCT_LANG.LANGID));

    @Override
    protected List<ProductDTO> recordsToView(List<Record> records) {
        final List<ProductDTO> products = productViewMapper.extractRecords(records);
        final Map<Long, List<ProductLangDTO>> langs = productLangViewMapper.extractRecordsGroupedBy(records);
        for (ProductDTO product : products) {
            final List<ProductLangDTO> productLangs = langs.get(product.getProductId());
            product.setLangs(productLangs);
            for (ProductLangDTO productLang : productLangs) {
                if (productLang.getLangId().equals(requestContext().getLangId())) {
                    product.setLang(productLang);
                }
            }
        }
        return products;
    }
...
© www.soinside.com 2019 - 2024. All rights reserved.