【沉浸式解决问题】基于泛型递归,Java中实体类基类开启MybatisPlus的ActiveRecord模式

一、问题描述

MybatisPlus的ActiveRecord模式对本地数据处理项目十分的便捷,同时为了更好的管理公共字段,又引入了BaseEntity作为基类,包括创建时间、创建者、更新时间、更新者等字段,之后所有的实体类再继承基类,但是这样实体类就无法再使用MybatisPlus的Model类提供的数据库操作方法了,想到了通过泛型递归来解决。
在这里插入图片描述


二、场景还原

所有类只保留最简单代码

1. 实体类基类 BaseEntity.class

public class BaseEntity extends Model<T> {
}

2. 测试实体类 TestTemplate.class

public class TestTemplate extends BaseEntity {
}

3. 测试类 EntityTests.class

@SpringBootTest
public class EntityTests {
    @Test
    void test1() {
        TestTemplate testTemplate = new TestTemplate();
        List<TestTemplate> testTemplates = testTemplate.selectAll();
    }
}

三、原因分析

因为是BaseEntity继承的Model类,所以相关方法也是绑定在BaseEntity上的,TestTemplate作为BaseEntity的子类调用父类的方法,返回的当然应该是父类的类型。而MybatisPlus提供的方法实际上是在Model中的,所以要想办法把子类送到Model的泛型中。


四、解决方案

这里就需要用到Java的一种语法糖,我把它叫做泛型递归,没查到专业的名称,或者叫泛型的泛型也很合适。
上面说到了要把子类送到Model的泛型中,Java又不支持多继承,我的需求又需要继承BaseEntity,那很自然的就想到给BaseEntity一个泛型,再传到Model的泛型中。

1. 实体类基类 BaseEntity.class

public class BaseEntity<T extends BaseEntity<T>>  extends Model<T> {
}

2. 测试实体类 TestTemplate.class

public class TestTemplate extends BaseEntity<TestTemplate> {
}

3. 测试类 EntityTests.class

@SpringBootTest
public class EntityTests {
    @Test
    void test1() {
        TestTemplate testTemplate = new TestTemplate();
        List<TestTemplate> testTemplates = testTemplate.selectAll();
    }
}

这时候返回类型就正确了
在这里插入图片描述

五、泛型递归

这里再结合BaseEntity<T extends BaseEntity<T>> extends Model<T>详细讲解一下泛型递归这个概念

前面我们决定了要给BaseEntity带一个泛型,那就变成了BaseEntity<T>
什么样的泛型呢,那得是BaseEntity的子类,那就变成了BaseEntity<T extends class<?>
这里引申出第一个问题,继承谁,你可能直接会想到那当然是BaseEntity,那为什么不能是Model呢,语法上是可以的,相当于BaseEntity的泛型允许别的Model的子类代入,比方说其他基类,但实际中我想不到会有这样的极端情况,继承BaseEntity就好,那就变成了BaseEntity<T extends BaseEntity<?>
第二个问题,泛型的要求还有什么,这就是子尖括号里面的问号了,<?>表示任何BaseEntity的子类都可以,而<T>表示只能填当前子类自身。通过刚刚的分析我们可以看出来,<?>无法保证返回的类型,谁知道父类又继承了什么,泛型方法最终返回什么,但<T>可以确保类型一致性,比较适合链式调用、Builder模式、ORM基类等场景,通常也都用这个。

为什么会考虑这个呢,因为Model就是用的<?>

package com.baomidou.mybatisplus.extension.activerecord;

import com.baomidou.mybatisplus.extension.toolkit.SqlRunner;

public abstract class Model<T extends Model<?>> extends AbstractModel<T> {
    public Model() {
    }

    public SqlRunner sql() {
        return new SqlRunner(this.entityClass);
    }
}

再举一个标准的例子,我们常用到的枚举类

public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {
}

喜欢的点个关注吧><!祝你永无bug!

/*
                   _ooOoo_
                  o8888888o
                  88" . "88
                  (| -_- |)
                  O\  =  /O
               ____/`---'\____
             .'  \\|     |//  `.
            /  \\|||  :  |||//  \
           /  _||||| -:- |||||-  \
           |   | \\\  -  /// |   |
           | \_|  ''\---/''  |   |
           \  .-\__  `-`  ___/-. /
         ___`. .'  /--.--\  `. . __
      ."" '<  `.___\_<|>_/___.'  >'"".
     | | :  `- \`.;`\ _ /`;.`/ - ` : | |
     \  \ `-.   \_ __\ /__ _/   .-` /  /
======`-.____`-.___\_____/___.-`____.-'======
                   `=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            佛祖保佑       永无BUG
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值