MyBatis-Plus:代码增强-抽取公共字段、Lambda获取字段名

x33g5p2x  于2022-06-27 转载在 其他  
字(8.3k)|赞(0)|评价(0)|浏览(325)

1. 增强

1.1 抽取公共字段(Model)

@Getter
@Setter
@ToString
@Accessors(chain = true)
public class BaseDB<T extends BaseDB<?>> extends Model<T> {

    private static final long serialVersionUID = 3253505422347170166L;

    /** 主键 - 记录插入自动填充主键处理{@path application.yml} */
    @TableId
    private String id;

    @TableField(fill = FieldFill.INSERT)
    private String createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateTime;

    /** 记录是否被逻辑删除:0未删除 1逻辑删除 - 逻辑处理值定义{@path application.yml}*/
    @TableField
    @TableLogic
    private Integer isDel;

    @TableField("is_del")
    private IsDelEnum isDelEnum;

    /**
     * 乐观锁(记录的被修改数)
     *
     * 很鸡肋,需要通过代码查出记录然后手动取此对象的version给准备update的对象,或者直接在查出记录上进行update
     */
    @TableField
    @Version
    private Integer version;

}

1.2 物理删除、逻辑删除同时支持(BaseMapper增强)

MyBatisPlusConfig.java

@Configuration
public class MyBatisPlusConfig {

    /**
     * SQL注入器
     * @return
     */
    @Bean
    public ISqlInjector enhanceISqlInjector() {
        return new DefaultSqlInjector() {
            @Override
            public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
                List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);

                methodList.add(new DeleteByIdPhysical());
                methodList.add(new DeleteByIdLogic());

                return methodList;
            }
        };
    }

}

DeleteByIdLogic.java

/**
 * 作用:逻辑删除
 *  照抄官方{@link DeleteById}
 * @author LinRuChang
 * @version 1.0
 * @date 2022/06/14
 * @since 1.8
 **/
public class DeleteByIdLogic extends AbstractMethod {
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {

        boolean withLogicDelete = tableInfo.isWithLogicDelete();

        //当前表支持逻辑删除,不支持则不注入
        if(withLogicDelete) {
            String sql;
            SqlMethod sqlMethod = SqlMethod.LOGIC_DELETE_BY_ID;
            List<TableFieldInfo> fieldInfos = tableInfo.getFieldList().stream()
                    .filter(TableFieldInfo::isWithUpdateFill)
                    .filter(f -> !f.isLogicDelete())
                    .collect(toList());
            if (CollectionUtils.isNotEmpty(fieldInfos)) {
                String sqlSet = "SET " + SqlScriptUtils.convertIf(fieldInfos.stream()
                        .map(i -> i.getSqlSet(EMPTY)).collect(joining(EMPTY)), "!@org.apache.ibatis.type.SimpleTypeRegistry@isSimpleType(_parameter.getClass())", true)
                        + tableInfo.getLogicDeleteSql(false, false);
                sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), sqlSet, tableInfo.getKeyColumn(),
                        tableInfo.getKeyProperty(), tableInfo.getLogicDeleteSql(true, true));
            } else {
                sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), sqlLogicSet(tableInfo),
                        tableInfo.getKeyColumn(), tableInfo.getKeyProperty(),
                        tableInfo.getLogicDeleteSql(true, true));
            }
            SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, Object.class);
            //return addUpdateMappedStatement(mapperClass, modelClass, getMethod(sqlMethod), sqlSource);
            return addUpdateMappedStatement(mapperClass, modelClass, "deleteByIdLogic", sqlSource);
        }

        //无逻辑删除字段,则不注入上述SQL,当调用work.linruchang.mybatisplussamplestest.dao.MyBaseMapper.deleteByIdLogic(java.io.Serializable)会报异常
        return null;
    }
}

DeleteByIdPhysical.java

/**
 * 作用:物理删除
 *  照抄{@link DeleteById}
 * @author LinRuChang
 * @version 1.0
 * @date 2022/06/14
 * @since 1.8
 **/
public class DeleteByIdPhysical extends AbstractMethod {
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        String sql;
        SqlMethod sqlMethod  = SqlMethod.DELETE_BY_ID;
        sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), tableInfo.getKeyColumn(),
                tableInfo.getKeyProperty());
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, Object.class);
        //return this.addDeleteMappedStatement(mapperClass, getMethod(sqlMethod), sqlSource);
        return this.addDeleteMappedStatement(mapperClass, "deleteByIdPhysical", sqlSource);
    }
}


MyBaseMapper.java

public interface MyBaseMapper<T> extends BaseMapper<T> {

    /**
     * 物理删除
     * @param id
     * @return
     */
    int deleteByIdPhysical(Serializable id);

    /**
     * 物理删除
     * @param entity
     * @return
     */
    int deleteByIdPhysical(T entity);

    /**
     * 逻辑删除
     *
     * 打算调这个方法,如果发现实体没有@TableLogic就会报异常出来提示用户
     * @param id
     * @return
     */
    int deleteByIdLogic(Serializable id);

    /**
     * 逻辑删除
     * @param entity
     * @return
     */
    int deleteByIdLogic(T entity);

    /**
     * 删除
     * @param id 主键ID
     * @param logicDeleteFlag null老逻辑  true逻辑删除 false物理删除
     * @return
     */
    default int deleteById(Serializable id, Boolean logicDeleteFlag) {

        //无指定则根据实体是否有@TableLogic进行评判(原逻辑)
        if(logicDeleteFlag == null) {return deleteById(id);}

        //指定逻辑删除、物理删除
        if(BooleanUtil.isTrue(logicDeleteFlag)) {
            Class currentMapperClazz = (Class) ReflectUtil.getFieldValue(ReflectUtil.getFieldValue(AopUtils.getTargetObject(this),"h"),"mapperInterface");
            Type typeArgument = TypeUtil.getTypeArgument(currentMapperClazz, 0);
            TableInfo tableInfo = TableInfoHelper.getTableInfo((Class<?>) typeArgument);
            if(tableInfo.isWithLogicDelete()) {
                return deleteByIdLogic(id);
            }
            throw new RuntimeException(StrUtil.format("当前表【{}】:不支持逻辑删除,请检查是否有配置逻辑删除字段", tableInfo.getTableName()));
        }else {
            return deleteByIdPhysical(id);
        }
    }

}

SysUserDao.java

@Mapper
public interface SysUserDao extends MyBaseMapper<SysUser> {
}

SysUserServiceTest.java

@SpringBootTest
class SysUserServiceTest {

    @Autowired
    SysUserDao sysUserDao;

    /**
     * 物理删除支持
     */
    @Test
    public void test9() {
        int insertFlag = sysUserDao.deleteById("dde66e90c44bcc535cce872541dd8b36", false);
        Console.log("删除情况:{}",insertFlag);
    }

    /**
     * 逻辑删除
     */
    @Test
    public void test10() {
        int insertFlag = sysUserDao.deleteById("dde66e90c44bcc535cce872541dd8b36", true);
        Console.log("逻辑删除情况:{}",insertFlag);
    }
}

2. 工具类

2.1 Lambda获取字段名

代码
/**
 * 作用:mybatisPlus工具类
 *
 * @author LinRuChang
 * @version 1.0
 * @date 2021/03/09
 * @since 1.8
 **/
public class MyBatisPlusUtil {

    /**
     * Collection<SFunction> 转成 Array<SFunction>
     * 容器类型转换
     * @param beanFields
     * @param <T>
     * @return
     */
    public static <T> SFunction<T,?>[] sFunctionCollsToArray(Collection<SFunction<T, ?>> beanFields) {
        return Optional.ofNullable(beanFields)
                .map(bfs -> {
                    SFunction<T,?>[] result = new SFunction[beanFields.size()];
                    return bfs.toArray(result);
                })
                .orElse(new SFunction[0]);
    }

    /**
     * Bean字段转select的数据库列名【即Bean变量名【驼峰变量名】转成下划线形式】
     *      过程:sfunction =》Bean变量名 =》驼峰转下划线字符串
     *      注意:仅仅只是基于getxx进行解析,对于@TableFiled上的真正的注解列名不生效
     * @param beanFields
     * @param <T>
     * @return 同一个表的多个表列名
     */
    public static <T> Set<String> sFunctionConvertToDataBaseFields(SFunction<T, ?>... beanFields) {
        return sFunctionConvertToDataBaseFields(beanFields == null ? Collections.emptySet():Arrays.asList(beanFields));
    }

    /**
     * Bean字段转select的数据库列名【即Bean变量名【驼峰变量名】转成下划线形式】
     *      过程:sfunction =》Bean变量名 =》驼峰转下划线字符串
     *      注意:仅仅只是基于getxx进行解析,对于@TableFiled上的真正的注解列名不生效
     * @param beanFields 多个属性Bean变量
     * @param <T>
     * @return 同一个表的多个表列名
     */
    public static <T> Set<String> sFunctionConvertToDataBaseFields(Collection<SFunction<T, ?>> beanFields) {
        return Optional.ofNullable(beanFields).map(columnNames -> {
            return beanFields.stream()
                    .map(MyBatisPlusUtil::sFunctionConverToDataBaseFieldFieldName)
                    .collect(Collectors.toSet());
        }).orElse(Collections.emptySet());
    }

    /**
     * 将Bean变量名转成数据库列名【约定俗成的形式】 == 驼峰转下划线
     * 注意:仅仅只是基于getxx进行解析,对于@TableFiled上的真正的注解列名不生效
     * @param beanFileldName 属性Bean变量
     * @param <T>
     * @return 数据库列名【下划线】
     */
    public static <T> String sFunctionConverToDataBaseFieldFieldName(SFunction<T, ?> beanFileldName) {
        return Optional.ofNullable(sFunctionConverToBeanFieldName(beanFileldName))
                .map(com.baomidou.mybatisplus.core.toolkit.StringUtils::camelToUnderline)
                .orElse(null);
    }

    /**
     * 获取Bean的变量名 == 一般都是驼峰变量名
     * 注意:仅仅只是基于getxx进行解析,对于@TableFiled上的真正的注解列名不生效
     * @param beanFileldName 属性Bean变量
     * @param <T>
     * @return Bean属性名
     */
    public static <T> String sFunctionConverToBeanFieldName(SFunction<T, ?> beanFileldName) {
        return Optional.ofNullable(beanFileldName).map(bfe -> {
            SerializedLambda resolve = LambdaUtils.resolve(bfe);
            return PropertyNamer.methodToProperty(resolve.getImplMethodName());
        }).orElse(null);
    }

}
使用
@Service
@Slf4j
public class RoleService extends ServiceImpl<RoleDao, Role> {    
    public List<Role> getList(SFunction<Role, ?>... fieldName) {
        return this.getBaseMapper().getList(MyBatisPlusUtil.sFunctionConvertToDataBaseFields(fieldName));
    }
}

@Mapper
public interface RoleDao extends BaseMapper<Role> {
    public List<Role> getList(@Param("selectTableColumnNames") Set<String> selectTableColumnNames);
}
<!--注意列名拼接必须使用${变量},使用#{变量}的话,最后语句变成  select '列名1', '列名2' from 表名 这句是错的,应该变成 select 列名1, 列名2 from 表名 -->
    <select id="getList" resultType="top.linruchang.springdemo.db.Role">
        SELECT
            <choose>
                <when test="selectTableColumnNames != null and selectTableColumnNames.size() > 0">
                    <foreach collection="selectTableColumnNames" separator="," item="columnName">
                        ${columnName}
                    </foreach>
                </when>
                <otherwise>*</otherwise>
            </choose>
        FROM role
    </select>

相关文章