# jforum.toro **Repository Path**: subpu/jforum.toro ## Basic Information - **Project Name**: jforum.toro - **Description**: 对象聚合的映射工具 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-07-10 - **Last Updated**: 2022-10-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: Java, 关联关系, 映射 ## README # jforum.toro #### 介绍 对象聚合的映射工具 从实体类到值对象的映射。支持代理(值对象是实例对象的子类), 模板(值对象包含实例对象的属性), ASF的元组(Pair, Triple)及其集合 :scream: 1: 支持: 目标对象(起始对象实例)与目标关联对象的一对一,一对多, 多对多。工具通过描述其关联关系减少日常的Set. 内置一些实现策略同时提供策略的自定义实现 :+1: > 例如: 通过订单获取其关联的购买者,配送地址; 2: 支持: 自定义BoolExpress表达式,传入表达式后通过decide方法对传入对象参数完成判断 > 例如: 用户的角色=x 并且 积分 > y. 此时传入用户对象即可计算当前传入的用户是否满足表达式。对于表达式由程序使用者来构建是实用的 3: 支持: 关联关系的表达式描述,减小构造关联关系的书写时间。工具对于表达式的格式是内置式的。当然您也可以重新定义并重新编译。工具内置有:BeanParser来进行表达式的解析 :fire: >BeanParser 通过自定义表达式完成对象的实例化(parse)或转成Map结构(toMap),也可以使用自定义表达式来对实例进行格式化(format) #### 分支路线图 ##### league 分支: 增加统一的API格式。统一代理,模板和ASF元组的调用格式 :scream: 完整示例: ``` // 1) 创建通用映射 ToroMapper mapper = new ToroMapper<>(OrdersVo.class); // 2.1) 通过方法创建连接关系描述( // link=VO.product, // use=orders.productId, // eq=Product.id, // type=orders.productId和Product.id的类型) LinkRelation mlr = mapper .link(设置的属性名) .use(选择对象中的查询属性名) .eq(匹配选择对像中的属性名) .type(use/eq中的类型) .tag(标签名:可选) .build(); // 2.2) 通过描述表达式创建连接关系描述 LinkRelation mlr = mapper.link(() -> "product=select(productId)&eq(id:long)"); // 3) 执行 结果 = mapper .many(起始对象|起始对象供应函数)|one|any .pair(右.mlr)|triple(中.mlr, 右.mlr)|proxy(mlr,...)|template(mlr,...) .transfer()|transfer(消费函数)|transfer(映射函数)|transfer(消费函数&映射函数) .parallel(是否并行:可选) .apply() .compose(关联关系)|compose(标签名, 关联关系), ... .strategy(自定义策略:可选) .cache(缓存关联关系图:可选) .get(); ``` ##### terse 分支: 在league 分支的基础上对非统一API进行简减,只使用统一的API格式。 目前尚未展开 :neckbeard: #### 项目需求 Java JDK11+ ASF commons lang3 V3.12.0 BULL Bean Utils V2.1.1-jdk11 #### 使用示例 :speech_balloon: 示例使用的对象在test中均可找到.不在此说明, 同时在test下有更详细的使用示例. 以下仅作部分描述 ##### :sparkles: 代理: ProxyByOneTest 值对象是实例对象的子类; 例:OrdersVo继承自Orders。 ``` // 1)一对一聚合 // R: 一张订单的购买者和商品的聚合 ProxyMapper mab = ProxyMapper.proxy(OrdersVo.class); MapperAttrStatements mae = mab .one() .attr("members") .used("memberId") .usedType("long") .select(memberSelectFun) .attr("product") .used("productId") .usedType(TypeEnum.BLong) .select(productSelectFun) .attr("coupons") .collection("java.util.HashSet") .used("id") .usedType(TypeEnum.BLong) .select(orderCouponFun); Optional ovo = mab .by(o3,true) .get(mae); // 1.1 if(ovo.isPresent()){ Members members = ovo.get().getMembers(); Product product = ovo.get().getProduct(); String css = ovo.get().getCoupons().stream().map(Coupon::getNames).collect(Collectors.joining(",")); System.out.println(String.format("%s by %s payed %f used coupon list: %s", members.getNames(), product.getTitle(), ovo.get().getAmout(), css)); }else { System.out.println("map fail"); } ``` ##### 代理的表达式示例: ProxyByOneExpTest ``` // 1)一对一聚合 // R: 一张订单的购买者和商品的聚合 ProxyMapper mab = ProxyMapper.proxy(OrdersVo.class); MapperAttrStatements mae = mab .one() .expr("members=select(memberId)") .select(memberSelectFun) .expr("product=select(productId)") .select(productSelectFun) .expr("coupons:java.util.HashSet=select(id)") .select(orderCouponFun); /* 无处理操作 Optional ovo = mab .by(o3, true) .get(mae);*/ // 对结果进行前置处理 Optional ovo = mab .by(o3, true) .consume(System.out::println) .get(mae); // 1.1 if(ovo.isPresent()){ OrdersVo ov = ovo.get(); Members members = ov.getMembers(); Product product = ov.getProduct(); String css = ov.getCoupons().stream().map(Coupon::getNames).collect(Collectors.joining(",")); System.out.println(String.format("%s by %s payed %f used coupon list: %s", members.getNames(), product.getTitle(), ov.getAmout(), css)); }else { System.out.println("map fail"); } ``` ##### :boom: 模板: TemplateByBatchTest 值对象中含有关联对象的属性; 例:OrdersTVo 中属性由订单(Orders),商品(Product),购买者(Members)的部分属性组成。 ``` TemplateMapper mab = TemplateMapper.template(OrdersTVo.class); TemplateStatements maeMember = mab .many("Member", "memberId", "id") .field(TemplateExpression.Builder.newInstance().target("buyer").used("id").usedType(TypeEnum.BLong).build()) .field(TemplateExpression.Builder.newInstance().target("buyerNames").used("names").usedType(TypeEnum.Str).build()); TemplateStatements maeProduct = mab .many("Product", "productId", "id") .field(TemplateExpression.Builder.newInstance().target("product").used("id").usedType(TypeEnum.BLong).build()) .field(TemplateExpression.Builder.newInstance().target("productTitle").used("title").usedType(TypeEnum.Str).build()); BatchQueryFace mb = (Collection memberIds) -> queryMemberFace(memberIds); BatchQueryFace pb = (Collection productIds)->queryProductFace(productIds); Collection rs = mab .by(Arrays.asList(o3, o4, o5), false) .batch() .compose("Member", mb) .compose("Product", pb) .get(Arrays.asList(maeMember, maeProduct)); rs.stream().forEach(ele->{ System.out.println(ele); }); ``` ##### 模板的表达式示例: TemplateByBatchExpTest ``` TemplateMapper mab = TemplateMapper.template(OrdersTVo.class); TemplateStatements maeMember = mab .many("Member", "memberId", "id") .expr("buyer=select(id:long)") .expr("buyerNames=select(names:java.lang.String)"); TemplateStatements maeProduct = mab .many("Product", "productId", "id") .expr("product=select(id:long)") .expr("productTitle=select(title:java.lang.String)"); BatchQueryFace mb = (Collection memberIds) -> queryMemberFace(memberIds); BatchQueryFace pb = (Collection productIds)->queryProductFace(productIds); Collection rs = mab .by(Arrays.asList(o3, o4, o5), false) .batch() .compose("Member", mb) .compose("Product", pb) .get(Arrays.asList(maeMember, maeProduct)); rs.stream().forEach(ele->{ System.out.println(ele); }); ``` ##### 模板的前缀匹配示例: TemplateByBasicAffixTest 若VO对象中存在某个关联对象的多个属性并且以某个字符串开头(affix). 以下示例会是更好的选择 ``` TemplateMapper mab = TemplateMapper.template(ArticleVo.class); TemplateAffixStatements autAffix = mab .one("Author", "authorId").affix("aut"); TemplateAffixStatements secAffix = mab .one("ArticleSection", "sectionId").affix("sec"); Optional data = mab .by(ar5, false) .affix() .basis() .compose("Author", authorSelectFun) .compose("ArticleSection", sectionSelectFun) .get(Arrays.asList(autAffix, secAffix)); if (data.isPresent()){ System.out.println(data.get()); } else { System.out.println("ArticleVo build fail"); } ``` ##### :revolving_hearts: ASF元组 ASF Pair的表达式示例: PairMapperTest ``` Function leftFun = (orderId) -> ohm.get(orderId); Function rightFun = (productId) -> phm.get(productId+""); /* 查看指定的订单*/ Pair pair= TupleMapper .pair .one() .expr("R(productId:long)") .supply(()->leftFun.apply(1001L)) .get(rightFun); System.out.println("left-right object:"); System.out.println(pair.getLeft()); System.out.println(pair.getRight()); ``` ASF Triple的表达式示例: ManyTripleMapperTest ``` Function, Collection> leftFun = (orderIdSet) -> queryOrdersById(orderIdSet); Function, Collection> middleFun = (productIdSet) -> queryProductById(productIdSet); Function, Collection> rightFun = (memberIdSet) -> queryMemberById(memberIdSet); /* 查看指定的订单*/ Collection> rs = TupleMapper .triple .many() .expr("M(productId:long)#R(memberId:long)&ML(id:long)#RJ(id:long)") .by(leftFun.apply(Arrays.asList(1000L, 1001L, 1002L))) .get(middleFun, rightFun); rs.stream().forEach(ele->{ System.out.println("------------------left-middle-right object:------------------"); System.out.println(ele.getLeft()); System.out.println(ele.getMiddle()); System.out.println(ele.getRight()); }); ``` ##### :muscle: 自定义表达匹配示例: BoolExpressTest ``` boolean rs = false; try { rs = BoolExpress .getInstance() .expression("GE:int(score,300)|EQ:int(level,2)") .decide(new MemberInfo(100, 2)); }catch (Exception e){ e.printStackTrace(); } System.out.println(rs); ```