前言
最近在做一个项目,苦于每次改数据库结构都得改一遍Mybatis的xml文件,实在是太繁琐,所以就用了MybatisPlus,但是又想用association
和collection
没办法只能手动写一个了
1.定义工具类
项目结构
RelationType
枚举类 标识是一对多 还是一对一
public enum RelationType {
ONE_TO_ONE,
ONE_TO_MANY
}
RelationConfig
配置类,每个service如果要实现一对一 一对多都需要创建指定的配置
// 泛型T和R,一个是原始表实体类,一个是目标表实体类
@Data
public class RelationConfig<T, R> {
// 一对一 或 一对多
private RelationType relationType;
// 使用哪个字段作为主表的关联字段
// Function函数式编程,需要目标为T类型(原始表实体类),返回值为Serializable
// 对类型为T的对象应用操作,并返回结果。结果是Serializable类型的对象
// 类似重写:Serializable apply(T t)
private Function<T, Serializable> sourceId;
// 使用哪个字段作为主表的注入字段
// BiConsumer函数式编程,接收两个对象但是没有返回
// 类似于:T.setZbbzTest(T)
private BiConsumer<T, R> sourceSetter;
// 一对多数据注入方法
private BiConsumer<T, List<R>> sourceListSetter;
// 副表查询返回
// Function函数式编程,需要目标为Serializable类型,返回值为R
// 类似于:id -> zbbzTestService.getById(id)
private Function<Serializable, R> targetLoader;
// 一对多数据加载器
private Function<Serializable, List<R>> targetListLoader;
}
MybatisPlusBuff
工具类,实现具体的一对一 一对多查询功能
这里要区分单条和多条,单条适用于getOne
,多头适用于list
查询
public class MybatisPlusBuff {
/**
* 注入关联数据 多条 适用于list
*/
public static <T, R> List<T> injectList(List<T> sourceList, List<RelationConfig<T, R>> configs) {
for (RelationConfig<T, R> config : configs) {
for (T source : sourceList) {
// 获取主表的关联 ID
Serializable id = config.getSourceId().apply(source);
if (config.getRelationType() == RelationType.ONE_TO_ONE) {
// 一对一
// 查询副表数据库
R target = config.getTargetLoader().apply(id);
// 把数据注入到主表
config.getSourceSetter().accept(source, target);
} else if (config.getRelationType() == RelationType.ONE_TO_MANY) {
// 一对多
List<R> targets = config.getTargetListLoader().apply(id);
config.getSourceListSetter().accept(source, targets);
}
}
}
return sourceList;
}
/**
* 注入关联数据 单条 适用于getById getOne
*/
public static <T, R> T injectOne(T source, List<RelationConfig<T, R>> configs) {
for (RelationConfig<T, R> config : configs) {
// 获取主表的关联 ID
Serializable id = config.getSourceId().apply(source);
if (config.getRelationType() == RelationType.ONE_TO_ONE) {
// 一对一
// 查询副表数据库
R target = config.getTargetLoader().apply(id);
// 把数据注入到主表
config.getSourceSetter().accept(source, target);
} else if (config.getRelationType() == RelationType.ONE_TO_MANY) {
// 一对多
List<R> targets = config.getTargetListLoader().apply(id);
config.getSourceListSetter().accept(source, targets);
}
}
return source;
}
}
BaseServiceImpl
继承MybatisPlus的公共service父类
,在查询方法返回前注入MybatisPlusBuff
中的一对一一对多查询
重写常用查询方法,注入一对一一对多逻辑
public class BaseServiceImpl<M extends BaseMapper<T>, T> extends ServiceImpl<M, T> {
// 默认所有方法都注入关联数据,子类可重写此方法根据方法名称判断是否为此方法注入
protected boolean shouldInject(String methodName) {
return true;
}
// 获取关联配置(子类需重写此方法)
protected <U, O> List<RelationConfig<U, O>> getRelationConfig() {
return new ArrayList<>();
}
// 根据方法名判断是否注入关联数据
private <R> R processResult(R result, String methodName) {
if (shouldInject(methodName)) {
if (result != null){
if (result instanceof List) {
return (R) MybatisPlusBuff.injectList((List<?>) result, getRelationConfig());
} else {
return (R) MybatisPlusBuff.injectOne(result, getRelationConfig());
}
}
}
return result;
}
//--------------------------------------------------------------
// 重写 MyBatis-Plus 的查询方法,按需注入关联数据
//--------------------------------------------------------------
@Override
public T getById(Serializable id) {
T entity = super.getById(id);
return processResult(entity, "getById");
}
@Override
public List<T> listByIds(Collection<? extends Serializable> idList) {
List<T> list = super.listByIds(idList);
return processResult(list, "listByIds");
}
@Override
public List<T> listByMap(Map<String, Object> columnMap) {
List<T> list = super.listByMap(columnMap);
return processResult(list, "listByMap");
}
@Override
public T getOne(Wrapper<T> queryWrapper) {
T entity = super.getOne(queryWrapper);
return processResult(entity, "getOne");
}
@Override
public T getOne(Wrapper<T> queryWrapper, boolean throwEx) {
T entity = super.getOne(queryWrapper, throwEx);
return processResult(entity, "getOne");
}
@Override
public <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper) {
V obj = super.getObj(queryWrapper, mapper);
return processResult(obj, "getObj");
}
@Override
public List<T> list(Wrapper<T> queryWrapper) {
List<T> list = super.list(queryWrapper);
return processResult(list, "list");
}
@Override
public List<T> list() {
List<T> list = super.list();
return processResult(list, "list");
}
@Override
public <E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper) {
E p = super.page(page, queryWrapper);
if (shouldInject("page")) {
p.setRecords(MybatisPlusBuff.injectList(p.getRecords(), getRelationConfig()));
}
return p;
}
@Override
public <E extends IPage<T>> E page(E page) {
E p = super.page(page);
if (shouldInject("page")) {
p.setRecords(MybatisPlusBuff.injectList(p.getRecords(), getRelationConfig()));
}
return p;
}
}
2.使用
在ServiceImpl
中需要把extends自MybatisPlus
的ServiceImpl
换为我们自己包装的类BaseServiceImpl
还必须重写父类getRelationConfig
方法,配置一对多一对一
// 需要把这里extends换为刚才的自定义包装的BaseServiceImpl类
@Service
public class ZbbzNewsServiceImpl extends BaseServiceImpl<ZbbzNewsMapper, ZbbzNews> implements IZbbzNewsService
{
@Resource
private ZbbzTestServiceImpl zbbzTestService;
// 重写父类getRelationConfig方法,配置一对一 一对多实现
@Override
protected List<RelationConfig<ZbbzNews, ZbbzTest>> getRelationConfig() {
ArrayList<RelationConfig<ZbbzNews, ZbbzTest>> relationConfigs = new ArrayList<>();
// 配置一对一关联
RelationConfig<ZbbzNews, ZbbzTest> userDetailConfig = new RelationConfig<>();
// 设置关系类型 一对一
userDetailConfig.setRelationType(RelationType.ONE_TO_ONE);
// 主表关联字段
userDetailConfig.setSourceId(ZbbzNews::getId);
// 主表注入一对一关系对象的方法
userDetailConfig.setSourceSetter((t, r) -> t.setZbbzTest(r));
// 副表查询方法 这里的id是上面setSourceId()方法返回的 这里自定义副表查询
userDetailConfig.setTargetLoader(id -> zbbzTestService.getById(id));
relationConfigs.add(userDetailConfig);
// 配置一对多关联
RelationConfig<ZbbzNews, ZbbzTest> userDetailConfig2 = new RelationConfig<>();
userDetailConfig2.setRelationType(RelationType.ONE_TO_MANY);
userDetailConfig2.setSourceId(ZbbzNews::getId);
userDetailConfig2.setSourceListSetter((t, r) -> t.setZbbzTests(r));
userDetailConfig2.setTargetListLoader((id -> zbbzTestService.list(new QueryWrapper<ZbbzTest>().eq("id", id))));
relationConfigs.add(userDetailConfig2);
return relationConfigs;
}
@Override
protected boolean shouldInject(String methodName) {
if (methodName.equals("list")){
// 只有list方法不注入
return false;
}
return true;
}
/**
* 查询新闻列表
*
* @param zbbzNews 新闻
* @return 新闻
*/
@Override
public List<ZbbzNews> selectZbbzNewsList(ZbbzNews zbbzNews)
{
QueryWrapper<ZbbzNews> queryWrapper = Wrappers.query();
if (zbbzNews.getTitle() != null && !zbbzNews.getTitle().trim().isEmpty()) {
queryWrapper.eq("title", zbbzNews.getTitle());
}
if (zbbzNews.getContent() != null && !zbbzNews.getContent().trim().isEmpty()) {
queryWrapper.eq("content", zbbzNews.getContent());
}
if (zbbzNews.getTId() != null ) {
queryWrapper.eq("t_id", zbbzNews.getTId());
}
return this.list(queryWrapper);
}
}