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];
}));
};