主题:使用BeetlSQL自定义扩展为BeetlSQL添加逻辑删除功能

darren 2019年08月30日 251

BeetlSQL的自由度很高,支持自定义BaseMapper,也支持自己扩展基础方法。因此可以通过自定义ami的方式来简单的支持逻辑删除。

话不多说,贴代码:

先定义一个注解,用于标注在逻辑删除时需要更新的字段上,常见的可能定义成Boolean类型,也有定义成Integer类型的,所以一个通用注解,默认设定为Boolean类型,当不是Boolean类型时需要自己指定,以及逻辑删除时需要update的值如果不是默认的值也需要手动设定

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogicDelete {
    //当前被注解的字段类型
    LogicDeleteType value() default LogicDeleteType.BOOL;

    //当字段为Boolean类型时 逻辑删除的取值
    boolean boolValue() default false;

    //当字段为数字类型时的 逻辑删除的取值
    int numValue() default 0;

    //当字段为字符串类型时的 逻辑删除的取值
    String strValue() default "DELETE";
}

//上面用到的枚举类,暂时只定义三种类型,如有需要自行扩展
public enum LogicDeleteType {
    BOOL, INT, STR;
}

接下来就是重头戏,自定义的ami类

@SuppressWarnings("unchecked")
public class LogicDeleteAmi implements MapperInvoke {

    @Override
    public Object call(SQLManager sm, Class entityClass, String sqlId, Method m, Object[] args) {
        //获取所有的字段
        List allFields = fields(entityClass);
        //筛选出 注解了 @LogicDelete 的字段
        List logicDeleteFields = allFields
                .stream()
                .filter(field -> field.isAnnotationPresent(LogicDelete.class))
                .collect(Collectors.toList());
        //未找到 @LogicDelete 注解的字段则抛出异常
        if (logicDeleteFields.size() == 0) {
            throw new BeetlSQLException(BeetlSQLException.SQL_SCRIPT_ERROR, "实体 " + entityClass.getName() + " 缺少 @LogicDelete 注解的字段");
        }
        Object instance;
        //当前参数 如果不是当前类型(认为是主键),则实例化当前对象,并将当前参数设置到主键字段上
        if (!entityClass.isAssignableFrom(args[0].getClass())) {
            Field idField = allFields.stream()
                    .filter(field -> Objects.equals(field.getName(), "id")
                            || field.isAnnotationPresent(AutoID.class)
                            || field.isAnnotationPresent(AssignID.class)
                            || field.isAnnotationPresent(SeqID.class))
                    .findFirst()
                    .orElseThrow(() -> new BeetlSQLException(BeetlSQLException.ID_NOT_FOUND, "实体 " + entityClass.getName() + " 没有找到主键字段"));
            instance = BeanKit.newInstance(entityClass);
            BeanKit.setBeanProperty(instance, args[0], idField.getName());
        } else {
            //当前参数是当前类型(如联合主键的情况)
            instance = args[0];
        }

        //根据 @LogicDelete 分别设置逻辑删除所标注的 删除属性的值
        logicDeleteFields.forEach(field -> {
            LogicDelete annotation = field.getAnnotation(LogicDelete.class);
            switch (annotation.value()) {
                case INT:
                    BeanKit.setBeanProperty(instance, annotation.numValue(), field.getName());
                    break;
                case BOOL:
                    BeanKit.setBeanProperty(instance, annotation.boolValue(), field.getName());
                    break;
                case STR:
                    BeanKit.setBeanProperty(instance, annotation.strValue(), field.getName());
                    break;
            }
        });
        //调用内置的 模板更新方法 更新当前实体
        return sm.updateTemplateById(instance);
    }

    private List fields(Class cls) {
        List fields = new ArrayList<>();
        while (cls != Object.class) {
            fields.addAll(Arrays.asList(cls.getDeclaredFields()));
            cls = cls.getSuperclass();
        }
        return fields;
    }
}

然后接下来就是定义一个自己的基类Mapper,这里直接继承了BaseMapper,新增了logicDeleteById的方法

public interface CustomerBaseMapper extends BaseMapper {
    int logicDeleteById(Object object);
}

好了,可以开始测试了,我们先定义一个简单的User类(PS:使用了lombok简化代码,所以没有getter/setter)

@Table(name = "tb_user")
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class User   {

    @AutoID
    Integer id;
    String userName;
    String password;
    //....其他属性...

    /**
     * 逻辑删除的字段,敲黑板!这里是重点,因为当前我定义的属性是Boolean,所以都可以用默认值
     */
    @LogicDelete
    Boolean state;

}

最后的最后 测试用例 以及结果:

@RunWith(SpringRunner.class)
@SpringBootTest
public class BeetlsqlLogicDeleteTest {

    @Autowired
    SQLManager sql;

    @Before
    public void init(){
        MapperConfigBuilder builder = sql.setBaseMapper(CustomerBaseMapper.class).getBuilder();
        //安排~  给BeetlSQL注册上我们自定义的logicDeleteById方法,
        builder.addAmi("logicDeleteById", new LogicDeleteAmi());
    }

    @Test
    public void logicDeleteTest(){
        //UserDao是一个继承了自定义CustomerBaseMapper的空接口
        UserDao userDao = sql.getMapper(UserDao.class);
        userDao.logicDeleteById(1);
    }
}

控制台输出如下:

┏━━━━━ Debug [user._gen_updateTemplateById] ━━━
┣ SQL:   update `tb_user` set `state`=? where `id` = ?
┣ 参数:    [false, 1]
┣ 位置:    work.darren.sample.BeetlsqlLogicDeleteTest.logicDeleteTest(BeetlsqlLogicDeleteTest.java:30)
┣ 时间:    42ms
┣ 更新:    [1]
┗━━━━━ Debug [user._gen_updateTemplateById] ━━━
Gavin 2019年09月06日

优秀