目录
一、问题描述
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
*/