前言

最近在做一个项目,苦于每次改数据库结构都得改一遍Mybatis的xml文件,实在是太繁琐,所以就用了MybatisPlus,但是又想用associationcollection没办法只能手动写一个了

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自MybatisPlusServiceImpl换为我们自己包装的类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);
    }
}