ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

我用注解实现了优雅的跨库查询,架构师竖起了大拇指

2021-04-12 11:54:09  阅读:211  来源: 互联网

标签:String 跨库 memberName private 我用 架构师 id 注解 name


背景

在开发过程中,你肯定会遇到这样一个场景:

  • 获取订单列表,需要显示订单id,下单人member_id,下单人姓名member_name
  • 数据库订单表只有member_id字段,member_name字段在用户会员表中。

图片

这时候你肯定会使用连表查询

select o.id,o.member_id,m.name from order o,member m where o.member_id = m.id;

一行SQL搞定,很简单嘛。

但是随着时间推移,系统越来越大,单体服务会拆分为微服务。每个服务有自己的库。

图片

这时候你就不好做跨库查询了吧。

不过你用的Spring Cloud框架,这套框架可以将远程调用变得和本地调用一样简单。

你可能会有如下代码:

图片

其中订单实体Order(作了简化,只保留本次案例用的字段)

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {

    /**
     * 订单id
     */
    private String id;

    /**
     * 下单用户id
     */
    private String memberId;

}

OrderVo为:

@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class OrderVo {

    private String id;

    private String memberId;
    /**
     * 获取订单用户name
     */
    private String memberName;

}

用户会员实体Member为:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Member implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    private String id;

    private String name;
}

测试结果为:

图片

listOrderVo()方法中,我们只有两步:

  1. 从数据库中查询Order
  2. 从用户服务中获取用户名

其中第2步的memberService.getById(memberId)是远程跨库调用。

你发现就算不是以前的连表查询,也不难嘛。

关于上面跨库查询的思考

上面的场景我也经历过,看了代码,我思考:步骤2在这个方法listOrderVo()中是否显得多余呢。我只是需要订单信息,却有大段的代码是如何获取memberName。这里只是订单信息要显示memberName,那么物流信息可能也要显示memberName。还有其他的业务比如:后台要展示用户签到记录也需要显示memberName。都要写步骤2的代码,太麻烦了。

可不可以将步骤2封装呢?

思来想去,发现使用注解可以优雅的解决这个问题。

注解封装

我们先来定义几个注解:

NeedSetValue

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface NeedSetValue {
    Class<?> beanClass();
    String param();
    String method();
    String targetFiled();
}

NeedSetValue注解用于字段上,下面来解释下这几个属性的含义:

  • beanClass:表示获取值的方法在哪个类上
  • param:表示字段值的方法的传入参数
  • method:表示获取字段值的方法
  • targetFiled:表示方法的返回的某个字段值赋给当前字段

NeedSetValueField

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface NeedSetValueField {

}

作用与方法上,表示该方法的返回值会通过注解处理获得。

我们来开发NeedSetValue注解的功能

@Component
@Aspect
public class SetFieldValueAspect {

    @Autowired
    BeanUtil beanUtil;

    @Around("@annotation(com.lvshen.demo.annotation.NeedSetValueField)")
    public Object doSetFieldValue(ProceedingJoinPoint point) throws Throwable {
        Object o = point.proceed();

        if (o instanceof Collection) {
            this.beanUtil.setFieldValueForCol(((Collection) o));
        } else {
            //返回结果不是集合的逻辑
        }
        return o;
    }
}

使用AOP,执行方法后会执行后置处理this.beanUtil.setFieldValueForCol(((Collection) o));这里我只写了方法返回是集合的处理。

setFieldValueForCol方法如下:

图片

由于代码过长,这里我转成图片了。这个方法主要做几件事:

1.获取有NeedSetValue注解的字段

2.反射获取注解上的参数,方法,返回值

3.执行注解上的方法,获取返回值

4.将返回的这个值赋给标注注解的这个字段

如果上面的代码没有看明白,那先看看我下面的使用例子吧

首先OrderVo代码如下:

@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class OrderVo{

    private String id;

    private String memberId;
    /**
     * 获取订单用户name
     */
    @NeedSetValue(beanClass = MemberService.class, param = "memberId", method = "getById", targetFiled = "name")
    private String memberName;

}

上面的注解表示:memberName的值是通过MemberService#getById方法获取的,该方法的参数为字段memberId,并将方法值的name赋给memberName

我们刚才获取订单展示的方法改造如下:

@NeedSetValueField
public List<OrderVo> listOrderVoByAnnotation() {
    //1.从数据库中查询Order
    List<Order> orderList = this.listOrder();
    log.info("数据库中的数据orderList:{}",orderList);
    //list之间的转换
    return OrderConverter.INSTANCE.listentity2Vo(orderList);
}

只需要添加@NeedSetValueField注解即可。省了步骤2。

图片

测试结果如下:

图片

你看,我没有写步骤2,一样获取到了memberName

有的开发认为写注解是多此一举,但是步骤2实际上是违反了设计原则:单一职责原则和开闭原则。

如果想要看源码的可以访问下面地址:

https://github.com/lvshen9/demo/blob/lvshen-dev/src/main/java/com/lvshen/demo/annotation


标签:String,跨库,memberName,private,我用,架构师,id,注解,name
来源: https://blog.51cto.com/u_15127545/2700945

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有