Skip to content

Bean属性填充工具类

在有些场景下,有些开发会循环查询数据库来关联相应的数据,例如:

java
public List<DataVo> listDataVo(String name) {
    // 例如根据名称模糊查询到一批与用户id进行关联数据,没有关联用户name
    List<Data> datas = this.dataFeign.list(name);
    // 但是我们可能会封装一层vo类,可能会涉及到一些别数据,例如需要查询到这个用户的名称
    return datas.stream().map(m -> {
        DataVo dataVo = new DataVo();
        dataVo.setId(m.getId());
        dataVo.setDataName(m.getDataName());
        dataVo.setUserId(m.getUserId());
        // 循环查询 关联用户name
        User userById = this.userMapper.getUserById(m.getUserId());
        dataVo.setUserName(userById.getName());
        return dataVo;
    }).collect(Collectors.toList());
}

以上这种方式极其不好,如果数据量过大,循环查询n次数据库性能消耗极大,通常会改成这样

java
public List<DataVo> listDataVo(String name) {
    // 例如根据名称模糊查询到一批与用户id进行关联数据,没有关联用户name
    List<Data> datas = this.dataFeign.list(name);
    // 先查询到这个List<Data>用到的有所用户
    Set<Integer> userIds = datas.stream().map(Data::getUserId).collect(Collectors.toSet());
    // 根据用户id批量查询 实际只需要查询使用到的字段
    List<User> users = this.userMapper.getUserByIds(userIds);
    // 做有关id与username的映射关系
    Map<Integer, String> maps = users.stream().collect(Collectors.toMap(User::getId, User::getName));
    // 处理数据
    return datas.stream().map(m -> {
        DataVo dataVo = new DataVo();
        dataVo.setId(m.getId());
        dataVo.setDataName(m.getDataName());
        dataVo.setUserId(m.getUserId());
        // 关联用户name
        String username = maps.get(m.getUserId());
        dataVo.setUserName(username);
        return dataVo;
    }).collect(Collectors.toList());
}

使用此工具类

java
Filler.fill(
    () -> datavos,                        
    userIds -> this.userMapper.getUserByIds(userIds).stream().collect(Collectors.toMap(User::getId, User::getName)),
    DataVo::getUserId,     
    DataVo::setUserName
);

使用方法

java
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.junit.Assert;
import org.junit.Test;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 填充工具类
 * <p>
 * 多服务调用时候,聚合数据使用
 * 比如订单服务查询时,订单的用户相关参数需要填充,通过工具类可省去很多中间步骤
 *
 * @author pursue-wind
 */
public class FillerTest {

	Random random = new Random();

	/**
	 * 模拟feignClient 或者 dao 根据用户id列表查询UserName
	 *
	 * @param userIds 用户id列表
	 * @return 返回 Map{userId : userName}
	 */
	private Map<Integer, String> queryMapByIds(Collection<Integer> userIds) {
		return userIds.stream()
				.map(i -> User.builder().id(i).userName("用户" + i + "号").build())
				.collect(Collectors.toMap(User::getId, User::getUserName));
	}

	/**
	 * 填充前
	 * <p>
	 * Order [{id = 1, createUserId = x1, createUserName = null} ...]
	 * <p>
	 * 填充后
	 * <p>
	 * Order [{id = 1, createUserId = x1, createUserName = 用户x1号} ...]
	 */
	@Test
	public void fill() {
		List<Order> orders = IntStream.rangeClosed(1, 5)
				.mapToObj(i -> Order.builder().id(i).createUserId(random.nextInt(50)).build())
				.collect(Collectors.toList());
		System.out.println(orders);

		Filler.fill(
				() -> orders,                        // 数据列表
				userIds -> queryMapByIds(userIds),    // 根据 userIds 查询 userName 的函数
				Order::getCreateUserId, Order::setCreateUserName // 从查询出的 Map{userId : userName} 中 取出 Order.createUserId 对应的 userId 把 userName 赋值给 Order 对象的 createUserName
		);
		System.out.println(orders);

		Assert.assertNotNull(orders.get(orders.size() / 2).getCreateUserName());
	}


	/**
	 * 填充前
	 * <p>
	 * Order [{id = 1, createUserId = x1, createUserName = null, updateUserId = x2, updateUserName = null} ...]
	 * <p>
	 * 填充后
	 * <p>
	 * Order [{id = 1, createUserId = x1, createUserName = 用户x1号, updateUserId = x2, updateUserName = 用户x2号} ...]
	 */
	@Test
	public void fill2() {
		List<Order> orders = IntStream.rangeClosed(1, 5)
				.mapToObj(i -> Order.builder().id(i).createUserId(random.nextInt(50)).updateUserId(random.nextInt(50)).build())
				.collect(Collectors.toList());
		System.out.println(orders);

		Filler.fill(
				() -> orders,                            // 数据列表
				userIds -> queryMapByIds(userIds),    // 根据 userIds 查询 userName 的函数
				Order::getCreateUserId, Order::setCreateUserName, // 从查询出的 Map{userId : userName} 中 取出 Order.createUserId 对应的 userId 把 userName 赋值给 Order 对象的 createUserName
				Order::getUpdateUserId, Order::setUpdateUserName  // 同上
		);
		System.out.println(orders);

		Assert.assertNotNull(orders.get(orders.size() / 2).getCreateUserName());
		Assert.assertNotNull(orders.get(orders.size() / 2).getUpdateUserName());
	}

	/**
	 * 模拟 feignClient 或者 dao 根据 用户idcard列表 查询 IdCard
	 *
	 * @param idCardIds 用户idcard列表
	 * @return 返回 Map{idcardId : IdCard}
	 */
	private Map<Integer, IdCard> queryUserIdCardByIds(Collection<Integer> idCardIds) {
		return idCardIds.stream()
				.map(i -> IdCard.builder().id(i).username("名字" + i + "号").cardNo("身份证" + i + "号").build())
				.collect(Collectors.toMap(IdCard::getId, Function.identity()));
	}

	/**
	 * 填充前
	 * <p>
	 * User [{id = 1, createUserId = x, idCardName = null, idCardNo = null},{id = 2, idCardId = x, idCardName = null, idCardNo = null}...]
	 * <p>
	 * 填充后
	 * <p>
	 * User [{id = 1, idCardId = x, idCardName = 名字x号, idCardNo = 身份证x号},{id = 2, idCardId = x1, idCardName = 名字x1号, idCardNo = 身份证x1号}...]
	 */
	@Test
	public void fillMultiValue() {
		List<User> userList = IntStream.rangeClosed(1, 5)
				.mapToObj(i -> User.builder().id(i).idCardId(random.nextInt(50)).build())
				.collect(Collectors.toList());
		System.out.println(userList);

		/*
			Filler.<User, Integer, IdCard, String>fillMultiValue(
					() -> userList,                                          // 数据列表
					User::getIdCardId,                                       // 取出列表的每个idCardId
					userIds -> queryUserIdCardByIds(userIds),                // 取出的idCardList 去做查询
					ImmutableMap.of(
							IdCard::getUsername, User::setIdCardName,    	// 从查询出的IdCard中 取出 IdCard.username 赋值给 User 对象的 idCardName
							IdCard::getCardNo, User::setIdCardNo            // 从查询出的IdCard中 取出 IdCard.cardNo 赋值给 User 对象的 idCardNo
					)
			);
		*/
		Filler.<User, Integer, IdCard, String>fillMultiValue(
				() -> userList,                                          // 数据列表
				User::getIdCardId,                                       // 取出列表的每个idCardId
				userIds -> queryUserIdCardByIds(userIds),                // 取出的idCardList 去做查询
				new HashMap<Function<IdCard, String>, BiConsumer<User, String>>() {{
					put(IdCard::getUsername, User::setIdCardName);        // 从查询出的IdCard中 取出 IdCard.username 赋值给 User 对象的 idCardName
					put(IdCard::getCardNo, User::setIdCardNo);            // 从查询出的IdCard中 取出 IdCard.cardNo 赋值给 User 对象的 idCardNo
				}}
		);
		System.out.println(userList);
		Assert.assertNotNull(userList.get(userList.size() / 2).getIdCardNo());
		Assert.assertNotNull(userList.get(userList.size() / 2).getIdCardName());
	}

	@Data
	@AllArgsConstructor(staticName = "of")
	@Builder
	@NoArgsConstructor
	public static class User {
		private Integer id;
		private String userName;
		private Integer idCardId;
		private String idCardName;
		private String idCardNo;
	}


	@Data
	@AllArgsConstructor(staticName = "of")
	@Builder
	@NoArgsConstructor
	public static class Order {
		private Integer id;
		private Integer createUserId;
		private String createUserName;
		private Integer updateUserId;
		private String updateUserName;
	}


	@Data
	@AllArgsConstructor(staticName = "of")
	@Builder
	@NoArgsConstructor
	public static class IdCard {
		private Integer id;
		private String username;
		private String cardNo;
	}
}

util

java
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 填充工具类
 * 多服务调用或者多库查询时候,聚合数据使用
 * 比如订单服务查询时,订单的用户相关参数需要填充,通过工具类可简化中间步骤
 *
 * @author pursue-wind
 */
public class Filler {

	/**
	 * @param collectionSupplier    原集合
	 * @param collectionIdQueryData 根据 getter1 getter2 的值查询其他属性的函数
	 * @param getter                原集合属性取出函数
	 * @param setter                原集合属性赋值函数
	 * @param <IN>                  原集合泛型
	 * @param <ID>                  原集合属性取出类型
	 * @param <R>                   collectionIdQueryData 根据取出类型查询返回类型
	 */
	public static <IN, ID, R> void fill(Supplier<Collection<IN>> collectionSupplier,
										Function<Collection<ID>, Map<ID, R>> collectionIdQueryData,
										Function<IN, ID> getter,
										BiConsumer<IN, R> setter) {
		Collection<IN> collection = collectionSupplier.get();
		Collection<ID> ids = collection.stream().map(getter).filter(Objects::nonNull).collect(Collectors.toSet());
		Map<ID, R> map = collectionIdQueryData.apply(ids);
		collection.forEach(in ->
				Optional.ofNullable(map.get(getter.apply(in)))
						.ifPresent(r -> setter.accept(in, r)));
	}


	/**
	 * @param collectionSupplier    原集合
	 * @param collectionIdQueryData 根据 getter1 getter2 的值查询其他属性的函数
	 * @param getter1               原集合属性取出函数1
	 * @param setter1               原集合属性赋值函数1
	 * @param getter2               原集合属性取出函数2
	 * @param setter2               原集合属性赋值函数2
	 * @param <IN>                  原集合泛型
	 * @param <ID>                  原集合属性取出类型
	 * @param <R>                   collectionIdQueryData 根据取出类型查询返回类型
	 */
	public static <IN, ID, R> void fill(
			Supplier<Collection<IN>> collectionSupplier,
			Function<Collection<ID>, Map<ID, R>> collectionIdQueryData,
			Function<IN, ID> getter1, BiConsumer<IN, R> setter1,
			Function<IN, ID> getter2, BiConsumer<IN, R> setter2
	) {
		Map<Function<IN, ID>, BiConsumer<IN, R>> getterSetterMap = new HashMap<Function<IN, ID>, BiConsumer<IN, R>>() {{
			put(getter1, setter1);
			put(getter2, setter2);
		}};
		fill(collectionSupplier, getterSetterMap, collectionIdQueryData);
	}

	/**
	 * @param collectionSupplier    原集合
	 * @param collectionIdQueryData 根据 getter1 getter2 的值查询其他属性的函数
	 * @param getter1               原集合属性取出函数1
	 * @param setter1               原集合属性赋值函数1
	 * @param getter2               原集合属性取出函数2
	 * @param setter2               原集合属性赋值函数2
	 * @param getter2               原集合属性取出函数3
	 * @param setter2               原集合属性赋值函数3
	 * @param <IN>                  原集合泛型
	 * @param <ID>                  原集合属性取出类型
	 * @param <R>                   collectionIdQueryData 根据取出类型查询返回类型
	 */
	public static <IN, ID, R> void fill(Supplier<Collection<IN>> collectionSupplier,
										Function<Collection<ID>, Map<ID, R>> collectionIdQueryData,
										Function<IN, ID> getter1, BiConsumer<IN, R> setter1,
										Function<IN, ID> getter2, BiConsumer<IN, R> setter2,
										Function<IN, ID> getter3, BiConsumer<IN, R> setter3
	) {
		Map<Function<IN, ID>, BiConsumer<IN, R>> getterSetterMap = new HashMap<Function<IN, ID>, BiConsumer<IN, R>>() {{
			put(getter1, setter1);
			put(getter2, setter2);
			put(getter3, setter3);
		}};

		fill(collectionSupplier, getterSetterMap, collectionIdQueryData);
	}

	/**
	 * @param collectionSupplier    原集合
	 * @param collectionIdQueryData 根据 getter1 getter2 的值查询其他属性的函数
	 * @param getterSetterMap       {原集合属性取出函数:原集合属性赋值函数}
	 * @param <IN>                  原集合泛型
	 * @param <ID>                  原集合属性取出类型
	 * @param <R>                   collectionIdQueryData 根据取出类型查询返回类型
	 */
	public static <IN, ID, R> void fill(Supplier<Collection<IN>> collectionSupplier,
										Map<Function<IN, ID>, BiConsumer<IN, R>> getterSetterMap,
										Function<Collection<ID>, Map<ID, R>> collectionIdQueryData) {
		Collection<IN> collection = collectionSupplier.get();
		if (null == collection || collection.isEmpty()) {
			return;
		}
		Collection<ID> ids = getterSetterMap.keySet().stream()
				.map(getter -> collection.stream().map(getter).filter(Objects::nonNull))
				.flatMap(Stream::distinct)
				.collect(Collectors.toSet());
		Map<ID, R> map = collectionIdQueryData.apply(ids);
		getterSetterMap.forEach((getter, setter) ->
				collection.forEach(in ->
						Optional.ofNullable(map.get(getter.apply(in)))
								.ifPresent(r -> setter.accept(in, r))));
	}

	public static <IN, ID, R> void fillMultiValueString(Supplier<Collection<IN>> collectionSupplier,
														Function<IN, ID> collectionIdGetter,
														Function<Collection<ID>, Map<ID, R>> collectionIdQueryData,
														Map<Function<R, String>, BiConsumer<IN, String>> queryDataGetterAndCollectionSetter
	) {
		fillMultiValue(collectionSupplier, collectionIdGetter, collectionIdQueryData, queryDataGetterAndCollectionSetter);
	}

	/**
	 * 填充多个值
	 *
	 * @param collectionSupplier                 原集合
	 * @param collectionIdGetter                 集合映射原属性
	 * @param collectionIdQueryData              根据 collectionIdGetter 的值查询其他属性的函数
	 * @param queryDataGetterAndCollectionSetter collectionIdQueryData为对象或需要转换时,取值和设值函数映射
	 * @param <IN>                               原集合泛型
	 * @param <ID>                               原集合属性取出类型
	 * @param <R>                                collectionIdQueryData 根据取出类型查询返回类型
	 * @param <S>                                将 collectionIdQueryData 的 R 类型进行转换为赋值给原集合的类型
	 */
	public static <IN, ID, R, S> void fillMultiValue(Supplier<Collection<IN>> collectionSupplier,
													 Function<IN, ID> collectionIdGetter,
													 Function<Collection<ID>, Map<ID, R>> collectionIdQueryData,
													 Map<Function<R, S>, BiConsumer<IN, S>> queryDataGetterAndCollectionSetter
	) {
		Collection<IN> collection = collectionSupplier.get();
		if (null == collection || collection.isEmpty()) {
			return;
		}

		Collection<ID> ids = collection.stream().map(collectionIdGetter).filter(Objects::nonNull).collect(Collectors.toSet());

		Map<ID, R> map = collectionIdQueryData.apply(ids);
		queryDataGetterAndCollectionSetter
				.forEach((queryDataGetter, collectionSetter) ->
						collection.forEach(in ->
								Optional.ofNullable(map.get(collectionIdGetter.apply(in)))
										.ifPresent(r -> collectionSetter.accept(in, queryDataGetter.apply(r)))));
	}
}

kotlin 版本

kotlin
/**
 * 填充工具类
 *
 * @author pursue
 */
object FillerKt {

    /**
     * @param collection
     * @param getter   DATA_getter
     * @param setter   DATA_setter
     * @param f DATA_provider
     */
    fun <IN, R, ID> fill(
        collection: Collection<IN>,
        getter: (IN) -> ID,
        setter: (IN, R) -> Unit,
        f: (Collection<ID>) -> Map<ID, R>
    ) {
        val ids = collection.map(getter).toSet()
        val map = f.invoke(ids)
        collection.forEach { map[getter.invoke(it)]?.let { r -> setter.invoke(it, r) } }
    }

    fun <IN, R, ID> fill(
        collection: Collection<IN>,
        p: Pair<(IN) -> ID, (IN, R) -> Unit>,
        f: (Collection<ID>) -> Map<ID, R>
    ) {
        val ids = collection.map(p.first).toSet()
        val map = f.invoke(ids)
        collection.forEach { map[p.first.invoke(it)]?.let { r -> p.second.invoke(it, r) } }
    }

    /**
     * @param collection
     * @param transformMap   DATA_getter: DATA_setter
     * @param dataProviderMap DATA_provider
     */
    fun <IN, R, ID> fill(
        collection: Collection<IN>,
        transformMap: Map<(IN) -> ID, (IN, R) -> Unit>,
        dataProviderMap: (Collection<ID>) -> Map<ID, R>
    ) {
        val ids = transformMap.keys
            .map { collection.map(it) }
            .flatMap { it.distinct() }

        val fMap = dataProviderMap.invoke(ids)

        transformMap.forEach { (getter, setter) ->
            collection.forEach { item ->
                fMap[getter.invoke(item)]?.let { r -> setter.invoke(item, r) }
            }
        }
    }

    fun <IN, R, ID, R2> fill(
        collection: Collection<IN>,
        transformMap: Map<(IN) -> ID, (IN, R) -> Unit>,
        dataProvider: (Collection<ID>) -> Collection<R2>,
        dataProviderKeySelector: (R2) -> ID,
        dataProviderValueTransform: (R2) -> R,
    ) {
        val ids = transformMap.keys
            .map { collection.map(it) }
            .flatMap { it.distinct() }

        val fMap = dataProvider.invoke(ids)
            .associateBy(dataProviderKeySelector, dataProviderValueTransform)

        transformMap.forEach { (getter, setter) ->
            collection.forEach { item ->
                fMap[getter.invoke(item)]?.let { r -> setter.invoke(item, r) }
            }
        }
    }

    fun <IN, R, ID, R2> fill(
        collection: Collection<IN>,
        pairTransform: Pair<(IN) -> ID, (IN, R) -> Unit>,
        dataProvider: (Collection<ID>) -> Collection<R2>,
        pair: Pair<(R2) -> ID, (R2) -> R>
    ) {
        val ids = collection.map(pairTransform.first)
        val fMap = dataProvider.invoke(ids)
            .associateBy(pair.first, pair.second)

        collection.forEach { item ->
            fMap[pairTransform.first.invoke(item)]?.let { r -> pairTransform.second.invoke(item, r) }
        }
    }

    fun <IN, R, ID, R2> fill(
        collection: Collection<IN>,
        pairTransforms: Collection<Pair<(IN) -> ID, (IN, R) -> Unit>>,
        dataProvider: (Collection<ID>) -> Collection<R2>,
        pair: Pair<(R2) -> ID, (R2) -> R>
    ) {
        val ids = pairTransforms.map { it.first }
            .map { collection.map(it) }
            .flatMap { it.distinct() }

        val fMap = dataProvider.invoke(ids)
            .associateBy(pair.first, pair.second)
        pairTransforms.forEach { (f, s) ->
            collection.forEach { item ->
                fMap[f.invoke(item)]?.let { r -> s.invoke(item, r) }
            }
        }
    }

}

golang 版本

go
func filler[T any, PK comparable, R any](
	arr []T,
	get func(item *T) PK,
	set func(item *T, index R),
	supply func(arrIds []PK) map[PK]R,
) {

	pks := make([]PK, len(arr))
	for i, item := range arr {
		pks[i] = get(&item)
	}

	pksMap := supply(pks)
	for i := range arr {
		pk := get(&arr[i])
		r := pksMap[pk]
		set(&arr[i], r)
	}
}

使用

go
package main

import (
	"fmt"
	"strconv"
)

type User struct {
	ID   int
	Name string
}

func daoGetNameByUserIds(ids []int) map[int]string {
	map1 := make(map[int]string)
	for i := range ids {
		map1[ids[i]] = "userid" + strconv.Itoa(ids[i])
	}
	return map1
}


func main() {
    // 创建一个包含用户信息的切片
    users := []User{
        {ID: 1, Name: "Alice"},
        {ID: 2, Name: "Bob"},
        {ID: 3, Name: "Charlie"},
    }
    
    filler(
        users,
        func(item *User) int { return item.ID },
        func(item *User, name string) { item.Name = name },
        func(collection []int) map[int]string { return daoGetNameByUserIds(collection) },
    )
    
    // 打印结果
    for _, user := range users {
        fmt.Printf("Name: %s, ID: %d\n", user.Name, user.ID)
    }
}

typescript 版本

typescript
function withValue<T, R>(
  value: T | undefined | null,
  fn: (val: T) => R,
): R | undefined {
  return value ? fn(value) : undefined;
}

export async function fillCasual<IN extends Object, ID>(
  supplier: () => IN[],
  getter: (input: IN) => ID,
  idsMap: (ids: ID[]) => Promise<Map<ID, any>>,
) {
  const collection = supplier();
  const ids = new Set<ID>(
    collection.map(getter).filter((id): id is ID => id !== undefined),
  );
  const map = await idsMap(Array.from(ids));

  collection.forEach((item) => {
    const objFromMap = withValue(
      getter(item),
      (id) => map.get(id) ?? undefined,
    );

    Object.assign(item, objFromMap);
  });
}

使用

typescript
// 查询数据
const nftList = await c.db
  .select()
  .from(Nft)
  .where(and(...conditions))
  .limit(ps)
  .offset(ps * (pn - 1));
// 填充
await fillCasual(
  () => nftList,
  nft => nft.tokenId,
  tokenIds => getMetadataByTokenIDs(tokenIds),
);

// 取填充数据的方法
const getMetadataByTokenIDs = async (tokenIds: string[]): Promise<Map<string, any>> => {
  const query: string = tokenIds.map(id => `tokenIds=${id}`).join('&');

  const resp: any = await fetchWithQuery(METADATA_API_URL, query);
  ...
  return new Map(resp.data.map((tm: { tokenId: string }) => {
    return [tm.tokenId, tm];
  }));
};

文章来源于自己总结和网络转载,内容如有任何问题,请大佬斧正!联系我