我写了一个 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
的映射?
我通过引入一个新类
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;
}
...