baomidou / mybatis-plus

An powerful enhanced toolkit of MyBatis for simplify development
https://baomidou.com
Apache License 2.0
16.26k stars 4.29k forks source link

关于逻辑删除的 delete_time 问题,不是issue,只是帮助贴,望海涵。 #1386

Closed pedrogao closed 5 years ago

pedrogao commented 5 years ago

笔者也是从昨天才入手的mp,今天在解决逻辑删除的问题上绕了好久,由于官方文档上的逻辑删除确实只给出了Integer的实现,而很多orm的框架的软删除支持delete_time等在时间上的赋值,为了跟以前的项目保持统一,却又不想重新实现一遍logic的所有方法,因此提供了下面的方法来实现delete_time的赋值问题,希望帮助更多的开发者,如果已经有人也是这样实现的,纯属雷同。

经过我半天的探索和研究,增加delete_time的方式如下:

  1. 添加逻辑删除注解
@Getter
@Setter
@ToString
public class User {

    private Long id;

    private String name;

    private Integer age;

    private String email;

    @TableLogic
    private Date deleteTime;
}
  1. 修改appplication.properties配置
# mybatis.mapper-locations=classpath:mapper/*.xml
# mybatis.type-aliases-package=com.pedro.shirodemo.mapper
mybatis-plus.configuration.map-underscore-to-camel-case=true
# 逻辑删除
mybatis-plus.global-config.db-config.logic-delete-value=NOW()
mybatis-plus.global-config.db-config.logic-not-delete-value=NULL

此处比较取巧,因为delete-value和not-delete-value均为字符串值,sql在拼接的时候也会拼字符串,因此完全可以借助sql自身的 NOW() 函数来实现delete_time的赋值。

希望可以开此issue,帮助更多的开发者,拜谢了,确实又很多人在这个问题上绕了很久。

whh945atsyzx commented 5 years ago

好神奇的方案...这样子逻辑删除不会失效?

pedrogao commented 5 years ago

就目前使用的情况是没有失效的

qmdx commented 5 years ago

Very well modified to closed state

5468sun commented 5 years ago

在我这不管用 MYSQL 删除时执行如下sql: UPDATE wms_mgr_permission SET delete_time=NOW() WHERE guid=1145120443882397698 AND delete_time=NULL

应为 delete_time is NULL

pedrogao commented 5 years ago

抱歉,确实是有问题的,我这边也发现了,后续再找找其它方法吧

pedrogao commented 5 years ago

还是通过黑魔法解决了一下,毕竟为了兼容,推荐新项目就不要这么做了,通过mybatis的拦截器,修改原始的sql语句,如下:

package com.lin.cms.demo.common.mybatis;

import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.Properties;

@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
@Slf4j
public class LogicInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
        //获取到原始sql语句
        String sql = boundSql.getSql();
        if (sql.contains("delete_time=NULL")){
            String mSql = sql.replace("delete_time=NULL","delete_time is NULL");
            //通过反射修改sql语句
            Field field = boundSql.getClass().getDeclaredField("sql");
            field.setAccessible(true);
            field.set(boundSql, mSql);
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    @Override
    public void setProperties(Properties properties) {
    }
}

delete_time=NULL通过拦截器修改为了delete_time is NULL,这样的做法比较hack,不知道会不会有后面的bug,笔者也是因为项目的原因才这样做,慎用。

yuxiaobin commented 5 years ago

别用黑科技了,这个问题已经修复 参考提交

稍后发一个快照可以先用着

pedrogao commented 5 years ago

喜大普奔,发版了就不用了黑魔法了

yuxiaobin commented 5 years ago

快照已发,请配置快照仓库并使用版本3.1.3.1-SNAPSHOT

<repository> <id>snapshot</id> <name>mp-snapshot</name> <url>https://oss.sonatype.org/content/repositories/snapshots/</url> <snapshots> <enabled>true</enabled> </snapshots> </repository>

<groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <version>3.1.3.1-SNAPSHOT</version>

5468sun commented 5 years ago

坐等发版

zonasgao commented 5 years ago

升版了 3.2.0 之后已经可以这样使用了,首先感谢各位大佬们方案和支持。 同时提出自己遇到的一个小坑给各位参考: 环境:springboot 2.1.0.RELEASE,使用yaml进行配置解析。 问题:在通过yaml使用配置 mybatis-plus.global-config.db-config.logic-not-delete-value=NULL 的时候SQL附带的添加为查询条件为 deleted=,后面就没有了,所以一直报错。 原因:因为使用yaml进行配置文件解析的时候会自动转换成空字符串 解决:将配置更改为 mybatis-plus: global-config: db-config: logic-not-delete-value: 'NULL'

YiuTerran commented 4 years ago

这里用UNIX_TIMESTAMP(),然后default 0不是更简单么,无须null

maybeyj commented 3 years ago

这里用UNIX_TIMESTAMP(),然后default 0不是更简单么,无须null

用UNIX_TIMESTAMP(),如果【批量删除】岂不是会有问题?

liuanxin commented 3 years ago

默认值设置成 0, 删除的时候设置成当前时间戳

mybatis-plus.global-config.db-config:
  logic-delete-field: isDeleted
  logic-delete-value: UNIX_TIMESTAMP()
  logic-not-delete-value: 0

如果不想用上面的全局配置(比如有些表可能不需要用唯一索引, 字段用 0 1 就够了), 就在具体的注解上处理

/** 删除标识: 0.未删除, 非 0.删除 --> is_deleted */
@TableLogic(value = "0", delval = "UNIX_TIMESTAMP()")
private Integer isDeleted;

或者

/** 删除标识: 0.未删除, 1.已删除 --> is_deleted */
@TableLogic
private Boolean isDeleted;

上面的两种分别对应有唯一索引和没有的情况

`is_deleted` INT NOT NULL DEFAULT '0' COMMENT '删除标识: 0.未删除, 非 0.删除',
UNIQUE INDEX `uk_xxx` (`xxx`, `is_deleted`),

或者

`is_deleted` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '删除标识: 0.未删除, 1.已删除',
MccRay-s commented 2 years ago

默认值设置成 0, 删除的时候设置成当前时间戳

mybatis-plus.global-config.db-config:
  logic-delete-field: isDeleted
  logic-delete-value: UNIX_TIMESTAMP()
  logic-not-delete-value: 0

如果不想用上面的全局配置(比如有些表可能不需要用唯一索引, 字段用 0 1 就够了), 就在具体的注解上处理

/** 删除标识: 0.未删除, 非 0.删除 --> is_deleted */
@TableLogic(value = "0", delval = "UNIX_TIMESTAMP()")
private Integer isDeleted;

或者

/** 删除标识: 0.未删除, 1.已删除 --> is_deleted */
@TableLogic
private Boolean isDeleted;

上面的两种分别对应有唯一索引和没有的情况

`is_deleted` INT NOT NULL DEFAULT '0' COMMENT '删除标识: 0.未删除, 非 0.删除',
UNIQUE INDEX `uk_xxx` (`xxx`, `is_deleted`),

或者

`is_deleted` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '删除标识: 0.未删除, 1.已删除',

nice 一直没有尝试,然后就在找方法,看到老哥的办法直接尝试了一下OK了

KANLON commented 1 year ago

一般 delete_time 设置为 0000-00-00 00:00:00 这个合适些? 这样在表有唯一索引的时候,不会因为delete_time 为null,而导致唯一索引不生效

langlichong commented 8 months ago

image 写了个TableInfo的子类覆盖了formatLogicDeleteSql,程序启动后做业务查询压根不会执行到子类覆盖的代码,要做额外的配置吗(spring boot web环境)

Wuaner commented 7 months ago

image 写了个TableInfo的子类覆盖了formatLogicDeleteSql,程序启动后做业务查询压根不会执行到子类覆盖的代码,要做额外的配置吗(spring boot web环境)

用不了。注释估计是瞎写的

leechor commented 2 months ago

mysql, 列采用tinyint(1),默认为1,

      logic-delete-field: is_actual
      logic-delete-value: "NULL"
      logic-not-delete-value: 1

因为mysql把NULL索引当做唯一值,跟他组成联合索引后这样也可以保证被删除的记录不冲突。同时一般都有update_time,可以表示删除时间。