跳至主要內容

逻辑删除

xuejmnet大约 7 分钟

介绍

easy-query的逻辑删除可以帮助用户在select的时候过滤逻辑删除字段,update的时候也可以,delete的时候也可以转换对应的update,并且系统提供了大量的默认逻辑删除策略,并且用户也可以自定义实现更多的逻辑删除

方法默认描述
disableLogicDelete禁用逻辑删除不具有跨表达式的传递性也就是子查询逻辑删除独立计算
enableLogicDelete启用逻辑删除不具有跨表达式的传递性也就是子查询逻辑删除独立计算
useLogicDelete传入是否启用值不具有跨表达式的传递性也就是子查询逻辑删除独立计算
tableLogicDelete禁用最近的一张表不具有跨表达式的传递性也就是子查询逻辑删除独立计算,会和全局disableLogicDeleteenableLogicDeleteuseLogicDelete互相作用and
relationLogicDelete禁用启用关联关系表不具有传递性也会和全局作用

说明!!!

判断顺序:都启用的情况下才会去判断是否禁用,如果全局设置禁用那么不会去判断是否启用

demo数据

@Data
@Table("t_logic_del_topic")
public class LogicDelTopic {
    @Column(primaryKey = true)
    private String id;
    private Integer stars;
    private String title;
    @LogicDelete(strategy = LogicDeleteStrategyEnum.BOOLEAN)
    private Boolean deleted;
    private LocalDateTime createTime;
}

LogicDelete

在对应的数据库实体上面添加注解,LogicDelete可以让整个实体以该字段作为逻辑删除字段,其中strategy表示为逻辑删除字段的枚举,除了框架默认提供的框架也支持用户自定义逻辑删除

例子

查询

List<LogicDelTopic> logicDelTopics = easyQuery.queryable(LogicDelTopic.class).toList();
==> Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`deleted`,t.`create_time` FROM t_logic_del_topic t WHERE t.`deleted` = ?
==> Parameters: false(Boolean)
<== Total: 100

按条件删除

long l = easyQuery.deletable(LogicDelTopic.class)
                .whereById("11")
                .executeRows();
==> Preparing: UPDATE t_logic_del_topic SET `deleted` = ? WHERE `deleted` = ? AND `id` = ?
==> Parameters: true(Boolean),false(Boolean),11(String)
<== Total: 1

实体对象删除

 LogicDelTopic logicDelTopic = easyQuery.queryable(LogicDelTopic.class)
                .whereId("11").firstOrNull();
==> Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`deleted`,t.`create_time` FROM t_logic_del_topic t WHERE t.`deleted` = ? AND t.`id` = ? LIMIT 1
==> Parameters: false(Boolean),11(String)
<== Total: 1

long l = easyQuery.deletable(logicDelTopic)
        .executeRows();

==> Preparing: UPDATE t_logic_del_topic SET `deleted` = ? WHERE `deleted` = ? AND `id` = ?
==> Parameters: true(Boolean),false(Boolean),11(String)
<== Total: 1

strategy

枚举默认描述
CUSTOM用户自定义实现LogicDeleteStrategy或者 AbstractLogicDeleteStrategy
BOOLEANBoolean,boolean类型的属性true表示删除,false表示未被删除
DELETE_LONG_TIMESTAMPLong,long类型的属性,0表示未被删除,大于0表示被删除
LOCAL_DATE_TIMELocalDateTime.class null表示未被删除, not null表示被删除
LOCAL_DATELocalDate.class null表示未被删除, not null表示被删除

禁用逻辑删除

通过添加链式方法disableLogicDelete()可以禁用当前表达式的逻辑删除

//查询
List<LogicDelTopic> logicDelTopics = easyQuery.queryable(LogicDelTopic.class).disableLogicDelete().toList();

==> Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`deleted`,t.`create_time` FROM t_logic_del_topic t
<== Total: 100

//删除
long l = easyQuery.deletable(LogicDelTopic.class)
                .disableLogicDelete()
                .whereById("111xx")
                .executeRows();

==> Preparing: DELETE FROM t_logic_del_topic WHERE `id` = ?
==> Parameters: 111xx(String)
<== Total: 0

//修改
long l = easyQuery.updatable(LogicDelTopic.class)
                .disableLogicDelete()
                .set(LogicDelTopic::getTitle, logicDelTopic.getTitle())
                .whereId(logicDelTopic.getId())
                .executeRows();

==> Preparing: UPDATE t_logic_del_topic SET `title` = ? WHERE `id` = ?
==> Parameters: 标题0(String),0(String)
<== Total: 1

禁用部分逻辑删除

//同一个表达式内from的表然后加join的表所以最近的一张表是join的表
//正常的sql不进行部分禁用
List<BlogEntity> list2 = easyEntityQuery.queryable(BlogEntity.class)
        .leftJoin(BlogEntity.class, (b, b2) -> b.id().eq(b2.id()))
        .where((b1, b2) -> b1.title().like("123"))
        .toList();

==> Preparing: SELECT t.`id`,t.`create_time`,t.`update_time`,t.`create_by`,t.`update_by`,t.`deleted`,t.`title`,t.`content`,t.`url`,t.`star`,t.`publish_time`,t.`score`,t.`status`,t.`order`,t.`is_top`,t.`top` FROM `t_blog` t LEFT JOIN `t_blog` t1 ON t1.`deleted` = ? AND t.`id` = t1.`id` WHERE t.`deleted` = ? AND t.`title` LIKE ?
==> Parameters: false(Boolean),false(Boolean),%123%(String)



//禁用部分逻辑删除join的表禁用from的表不禁用
List<BlogEntity> list1 = easyEntityQuery.queryable(BlogEntity.class)
        .leftJoin(BlogEntity.class, (b, b2) -> b.id().eq(b2.id()))
        .tableLogicDelete(() -> false)
        .where((b1, b2) -> b1.title().like("123"))
        .toList();


==> Preparing: SELECT t.`id`,t.`create_time`,t.`update_time`,t.`create_by`,t.`update_by`,t.`deleted`,t.`title`,t.`content`,t.`url`,t.`star`,t.`publish_time`,t.`score`,t.`status`,t.`order`,t.`is_top`,t.`top` FROM `t_blog` t LEFT JOIN `t_blog` t1 ON t.`id` = t1.`id` WHERE t.`deleted` = ? AND t.`title` LIKE ?
==> Parameters: false(Boolean),%123%(String)

自定义逻辑删除

很多用户可能对现有的很多系统拥有的逻辑删除都表示非常弱鸡,甚至只支持单字段的逻辑删除,easy-query提供了高级抽象可以让用户自行实现逻辑删除

自定义逻辑删除数据

@Data
@Table("t_logic_del_topic_custom")
public class LogicDelTopicCustom {
    @Column(primaryKey = true)
    private String id;
    private Integer stars;
    private String title;
    private LocalDateTime deletedAt;
    private String deletedUser;
    private LocalDateTime createTime;
}

实现自定义逻辑删除策略

easy-query默认提供了一个接口和一个抽象来实现逻辑删除,默认用户可以选择抽象(简单)AbstractLogicDeleteStrategy,或者接口LogicDeleteStrategy
这次我们采用抽象来实现

新建一个静态帮助类来模拟当前用户因为我们这次自定义需要实现两个甚至多个字段逻辑删除处理

public class CurrentUserHelper {
    private static String userId;
    public static void setUserId(String userId){
        CurrentUserHelper.userId=userId;
    }
    public static String getUserId(){
        return userId;
    }
}

//@Component //如果是spring
//非spring或者spring且自行构建build的Qu二有RuntimeContext那么就需要调用
//configuration.applyLogicDeleteStrategy(new MyLogicDelStrategy());
//其中configuration通过以下代码来获取
QueryRuntimeContext runtimeContext = easyQuery.getRuntimeContext();
QueryConfiguration configuration = runtimeContext.getQueryConfiguration();
configuration.applyLogicDeleteStrategy(new MyLogicDelStrategy());

public class MyLogicDelStrategy extends AbstractLogicDeleteStrategy {
    /**
     * 允许datetime类型的属性
     */
    private static final Set<Class<?>> allowTypes=new HashSet<>(Arrays.asList(LocalDateTime.class));
    @Override
    protected SQLExpression1<WherePredicate<Object>> getPredicateFilterExpression(LogicDeleteBuilder builder,String propertyName) {
        //如果需要唯一索引请自行选择数据库是否支持null的唯一索引
        //如果不支持可以选择小于1900年或者一个固定年份来作为被删除
        return o->o.isNull(propertyName);
    }

    @Override
    protected SQLExpression1<ColumnSetter<Object>> getDeletedSQLExpression(LogicDeleteBuilder builder, String propertyName) {
//        LocalDateTime now = LocalDateTime.now();
//        return o->o.set(propertyName,now);
        //上面的是错误用法,将now值获取后那么这个now就是个固定值而不是动态值
        return o->o.set(propertyName,LocalDateTime.now())
                .set("deletedUser",CurrentUserHelper.getUserId());
    }

    @Override
    public String getStrategy() {
        return "MyLogicDelStrategy";
    }

    @Override
    public Set<Class<?>> allowedPropertyTypes() {
        return allowTypes;
    }
}

修改我们的实体对象

@Data
@Table("t_logic_del_topic_custom")
public class LogicDelTopicCustom {
    @Column(primaryKey = true)
    private String id;
    private Integer stars;
    private String title;
    //如果是自定义strategy必须是LogicDeleteStrategyEnum.CUSTOM,并且strategyName不可以为空
    @LogicDelete(strategy = LogicDeleteStrategyEnum.CUSTOM,strategyName = "MyLogicDelStrategy")
    private LocalDateTime deletedAt;
    private String deletedUser;
    private LocalDateTime createTime;
}

测试

List<LogicDelTopicCustom> logicDelTopics = easyQuery.queryable(LogicDelTopicCustom.class).toList();

==> Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`deleted_at`,t.`deleted_user`,t.`create_time` FROM t_logic_del_topic_custom t WHERE t.`deleted_at` IS NULL
<== Total: 100


LogicDelTopicCustom logicDelTopic = easyQuery.queryable(LogicDelTopicCustom.class)
                .where(o->o.eq(LogicDelTopicCustom::getId,"1")).firstOrNull();

==> Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`deleted_at`,t.`deleted_user`,t.`create_time` FROM `t_logic_del_topic_custom` t WHERE t.`deleted_at` IS NULL AND t.`id` = ? LIMIT 1
==> Parameters: 1(String)
<== Time Elapsed: 2(ms)
<== Total: 1


long l = easyQuery.updatable(logicDelTopic).executeRows();
==> Preparing: UPDATE t_logic_del_topic_custom SET `stars` = ?,`title` = ?,`deleted_user` = ?,`create_time` = ? WHERE `deleted_at` IS NULL AND `id` = ?
==> Parameters: 101(Integer),标题1(String),null(null),2023-04-02T23:09:03(LocalDateTime),1(String)
<== Total: 1

//为了测试防止数据被删掉,这边采用不存在的id
logicDelTopic.setId("11xx");
//测试当前人员
CurrentUserHelper.setUserId("easy-query");
long l = easyQuery.deletable(logicDelTopic).executeRows();

==> Preparing: UPDATE t_logic_del_topic_custom SET `deleted_at` = ?,`deleted_user` = ? WHERE `deleted_at` IS NULL AND `id` = ?
==> Parameters: 2023-04-01T23:15:13.944(LocalDateTime),easy-query(String),11xx(String)
<== Total: 0

到这里为止我们就完全实现了逻辑删除自定义并且支持更新多字段

关联对象逻辑删除

ToOne

        List<SysUser> userInHz = easyEntityQuery.queryable(SysUser.class)
                .where(s -> {
                    s.address().relationLogicDelete(()->false);//false表示不使用逻辑删除
                    //隐式子查询会自动join用户表和地址表
                    s.or(() -> {
                        s.address().city().eq("杭州市");
                        s.address().city().eq("绍兴市");
                    });
                }).toList();

ToMany

List<SysMenu> menus = easyEntityQuery.queryable(SysMenu.class)
        .where(s -> {
            //判断菜单下的角色存在角色的用户叫做小明的
            s.roles().configure(x->x.disableLogicDelete()).any(role -> {
                role.users().any(user -> {
                    user.name().eq("小明");
                });
            });
        }).toList();

相关搜索

逻辑删除 软删除 soft delete logic delete

上次编辑于:
贡献者: Hoysing