【内含代码】MyBatis延迟加载的实现

原理

其实延迟加载的实现原理很简单,其实就是对方法进行拦截

假设现在有两个表格kfwallpaper,这两个表存在关联关系(这里是我强行绑定的),通过注解@Collection来进行关系的标注

kf 表格对应的 java 对象

@Table(name = "kf")
public class Kf {

    @Id(name = "kf_id")
    @Column(name = "kf_id")
    private int id;
    @Column(name = "kf_name")
    private String kfName;
    @Column(name = "kf_account")
    private String kfAccount;
    @Column(name = "kf_qcard")
    private String kfQcard;
    @Collection(sub = WallPaper.class)
    private List<WallPaper> wallPapers;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getKfName() {
        return kfName;
    }

    public void setKfName(String kfName) {
        this.kfName = kfName;
    }

    public String getKfAccount() {
        return kfAccount;
    }

    public void setKfAccount(String kfAccount) {
        this.kfAccount = kfAccount;
    }

    public String getKfQcard() {
        return kfQcard;
    }

    public void setKfQcard(String kfQcard) {
        this.kfQcard = kfQcard;
    }

    public List<WallPaper> getWallPapers() {
        return wallPapers;
    }

    public void setWallPapers(List<WallPaper> wallPapers) {
        this.wallPapers = wallPapers;
    }

    @Override
    public String toString() {
        return "Kf{" +
                "id=" + id +
                ", kf + kfName + '\'' +
                ", kfAccount='" + kfAccount + '\'' +
                ", kfQcard='" + kfQcard + '\'' +
                ", wallPapers=" + wallPapers +
                '}';
    }
}

wallpaper 表格对应的 java 对象

@Table(name = "wallpaper")
public class WallPaper {

    @Id(name = "id")
    @Column(name = "id")
    private int id;
    @Column(name = "back_image_url")
    private String backImageUrl;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getBackImageUrl() {
        return backImageUrl;
    }

    public void setBackImageUrl(String backImageUrl) {
        this.backImageUrl = backImageUrl;
    }

    @Override
    public String toString() {
        return "WallPaper{" +
                "id=" + id +
                ", backImageUrl='" + backImageUrl + '\'' +
                '}';
    }
}

然后想在查询 kf 的时候把对应的 wallpaper 查询出来,这个时候,就需要做一些小小的操作,我们将数据库查询出来的kf表格的数据,不再直接简单的分装成Kf对象,而是采用 CGLib 字节码库,对Kf对象进行代理,生成一个Kf的代理对象返回给用户,这样,当用户需要执行kfgetWallPapers时,会被拦截,这个时候我们就可以在这里继续加载被关联表格的数据,然后将查询出来的数据装入 obj 中,

@SuppressWarnings("unchecked")
public T newInstance(Map<String, Object> values) {
    final Map<String, Field> columnFieldMapper = this.columnFieldMapper;
    T t = null;
    try {
        // 判断时候存在@Collection注解或者@Association注解,如果存在,返回一个代理对象,进行延迟加载
        if (hasCascadeRelations()) {
            t = (T) CglibProxy.getProxy(tableCls);
            for (Map.Entry<Field, CascadeRelations> entry : cascadeMapper.entrySet()) {
                Field field = entry.getKey();
                field.setAccessible(true);
                field.set(t, CglibProxy.getProxy(field.getType()));
            }
        } else {
            t = tableCls.newInstance();
        }
        for (Map.Entry<String, Object> entry : values.entrySet()) {
            Field field = columnFieldMapper.get(entry.getKey());
            field.setAccessible(true);
            field.set(t, entry.getValue());
        }
    } catch (InstantiationException | IllegalAccessException e) {
        e.printStackTrace();
    }
    return t;
}

延迟加载实现

对方法进行拦截,当拦截到 get 方法时,通过方法获取对应的 Field 信息,对 Field 进行判断,如果 Field 是另一个 Table 的话,就进行数据查询

@SuppressWarnings("unchecked")
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    Object invokeResult = proxy.invokeSuper(obj, args);
    String methodName = method.getName();
    if (invokeResult == null) {
        return null;
    } else if (methodName.startsWith("get")) {
        Class<?> invokeResultClass = invokeResult.getClass();
        Class<?> superClass = invokeResultClass.getSuperclass();
        if (invokeResultClass.equals(superClass)) {
            return invokeResult;
        }
        Field field = getFieldBy(method);
        field.setAccessible(true);
        CascadeRelations cascadeRelations = CascadeRelations.type(field);
        // 判断是否被相关注解所注解,没有的话直接返回原方法执行结果
        if (cascadeRelations == null) {
            return invokeResult;
        }
        Class<?> relationClass;
        if (cascadeRelations == CascadeRelations.ASSOCIATION) {
            relationClass = field.getAnnotation(Association.class).sub();
        } else {
            relationClass = field.getAnnotation(Collection.class).sub();
        }
        // 根据注解的值找到对应的Table信息进行数据库查询
        Sql sql = new Select().getSql(relationClass);
        // 数据库信息查询出来后根据sql对象以及期望的包装类型`Class<Wrap>`对数据结果进行对象话(这里直接采用了Map容器)
        List list = SqlSessionFactory.getInstance().openSession().getJdbcTemplate().selectByRelation(sql, Map.class);
        field.set(obj, list);
        return list;
    }
    return invokeResult;
}

不足

目前该代码的实现很粗糙,其实只是探究了 MyBatis 延迟加载的原理而已,还有很多数据库细节还没有去完整的考虑


来源链接:

https://www.liaochuntao.cn/2019/03/03/java-web-26/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值