Mybatis plus query sql condition: deleted = 0

This article introduces the integration of spring boot and mybatis plus in the project. When writing query methods, lambda is used to represent query data. When actually executing sql, the where condition will be spliced with deleted = 0.

Reason: 1. Delete field in global configuration. Default delete value = 1 Undeleted value = 0. The configuration method is as follows:

mybatis-plus.global-config.db-config.logic-delete-field = deleted

When using global configuration, if there is a deleted field in the entity class, it will splice deleted = 0

2. If there is no global configuration, use the code separately in the entity class

@Data
@Accessors(chain = true)
@TableName("order")
public class Order {

    @ApiModelProperty(value = "Primary key ID")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    @TableLogic(value = "0",delval = "1")
    @ApiModelProperty(value = "Deleted, 1:yes, 0:no")
    private Integer deleted;

}

This can also take effect. If you want to use deleted = 0 flexibly, you can decide whether to splice deleted = 0 in the query. Then you cannot use global configuration. When you query, if you delete fields logically in the entity class, they will be spliced. If not, they will not be spliced. When you delete, you also need to set the deleted value yourself.

Source code analysis:

Code 1

public class TableInfoHelper {
//  Omit the code. Here are private methods. You can look up and see the functions of each method
 /**
     * <p>
     * Initialize table primary key, table field
     * </p>
     *
     * @param clazz        Entity class
     * @param globalConfig Global configuration
     * @param tableInfo    Database table reflection information
     */
    private static void initTableFields(Class<?> clazz, GlobalConfig globalConfig, TableInfo tableInfo, List<String> excludeProperty) {
        /* Database global configuration */
        GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();
        Reflector reflector = tableInfo.getReflector();
        List<Field> list = getAllFields(clazz);
        // Mark whether the primary key is read
        boolean isReadPK = false;
        // Does @ TableId annotation exist
        boolean existTableId = isExistTableId(list);
        // Is there a @ TableLogic annotation
        boolean existTableLogic = isExistTableLogic(list);

        List<TableFieldInfo> fieldList = new ArrayList<>(list.size());
        for (Field field : list) {
            if (excludeProperty.contains(field.getName())) {
                continue;
            }
// Whether this field is a primary key is FALSE by default. The following assignment is made
            boolean isPK = false;
            boolean isOrderBy = field.getAnnotation(OrderBy.class) != null;

            /* Primary key ID initialization */
            if (existTableId) {
                TableId tableId = field.getAnnotation(TableId.class);
                if (tableId != null) {
                    if (isReadPK) {
                        throw ExceptionUtils.mpe("@TableId can't more than one in Class: \"%s\".", clazz.getName());
                    }
                    // There is a primary key. Set the primary key generation method and primary key name
                    initTableIdWithAnnotation(dbConfig, tableInfo, field, tableId);
//  It's just that the primary key is true
                    isPK = isReadPK = true;
                }
            } else if (!isReadPK) {
                isPK = isReadPK = initTableIdWithoutAnnotation(dbConfig, tableInfo, field);

            }

            if (isPK) {
                if (isOrderBy) {
                    tableInfo.getOrderByFields().add(new TableFieldInfo(dbConfig, tableInfo, field, reflector, existTableLogic, true));
                }
                continue;
            }

            final TableField tableField = field.getAnnotation(TableField.class);

            /* Field initialization with @ TableField annotation */
            if (tableField != null) {
//  Pay attention to the construction method of new TableFieldInfo (). When executing, it will judge whether the field has @ TableLogic annotation. If not, look at the global configuration,
//  The parameter existTableLogic is whether there is @ TableLogic annotation in the whole entity class
                fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field, tableField, reflector, existTableLogic, isOrderBy));
                continue;
            }

            /* Field initialization without @ TableField annotation */
            fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field, reflector, existTableLogic, isOrderBy));
        }

        /* Field list */
        tableInfo.setFieldList(fieldList);

        /* If no primary key annotation is found, a warning message will be prompted */
        if (!isReadPK) {
            logger.warn(String.format("Can not find table primary key in Class: \"%s\".", clazz.getName()));
        }
    }
}

Look at the code

new TableFieldInfo(dbConfig, tableInfo, field, reflector, existTableLogic, isOrderBy)

In this TableFieldInfo constructor, this(...) will be called, In call

this.initLogicDelete(dbConfig, field, existTableLogic);

Code 2

private void initLogicDelete(GlobalConfig.DbConfig dbConfig, Field field, boolean existTableLogic) {
        /* Get annotation attribute, logical processing field */
        TableLogic tableLogic = field.getAnnotation(TableLogic.class);
        if (null != tableLogic) {
            if (StringUtils.isNotBlank(tableLogic.value())) {
                this.logicNotDeleteValue = tableLogic.value();
            } else {
                this.logicNotDeleteValue = dbConfig.getLogicNotDeleteValue();
            }
            if (StringUtils.isNotBlank(tableLogic.delval())) {
                this.logicDeleteValue = tableLogic.delval();
            } else {
                this.logicDeleteValue = dbConfig.getLogicDeleteValue();
            }
            this.logicDelete = true;
        } else if (!existTableLogic) {
// There is no TableLogic annotation marked in the entity class. The value of existTableLogic = false,! existTableLogic=true, this method will be entered
            String deleteField = dbConfig.getLogicDeleteField();
// Get global delete field name above
// Next, determine that the field name is the same as the global definition, and then assign a value
            if (StringUtils.isNotBlank(deleteField) && this.property.equals(deleteField)) {
                this.logicNotDeleteValue = dbConfig.getLogicNotDeleteValue();
                this.logicDeleteValue = dbConfig.getLogicDeleteValue();
                this.logicDelete = true;
            }
        }
    }

In the code 1 method, tableInfo setFieldList(fieldList); This line of code will call the method of tableInfo class,

Code 3

void setFieldList(List<TableFieldInfo> fieldList) {
        this.fieldList = fieldList;
        AtomicInteger logicDeleted = new AtomicInteger();
        AtomicInteger version = new AtomicInteger();
        fieldList.forEach(i -> {
// In code 2 above, isLogicDelete that deletes a field will be assigned a value of true
            if (i.isLogicDelete()) {
// withLogicDelete=true will be set here,
                this.withLogicDelete = true;
// Set delete field
                this.logicDeleteFieldInfo = i;
                logicDeleted.getAndAdd(1);
            }
            if (i.isWithInsertFill()) {
                this.withInsertFill = true;
            }
            if (i.isWithUpdateFill()) {
                this.withUpdateFill = true;
            }
            if (i.isOrderBy()) {
                if (null == this.orderByFields) {
                    this.orderByFields = new LinkedList<>();
                }
                this.orderByFields.add(i);
            }
            if (i.isVersion()) {
                this.withVersion = true;
                this.versionFieldInfo = i;
                version.getAndAdd(1);
            }
        });
        /* Verify field validity */
        Assert.isTrue(logicDeleted.get() <= 1, "@TableLogic not support more than one in Class: \"%s\"", entityType.getName());
        Assert.isTrue(version.get() <= 1, "@Version not support more than one in Class: \"%s\"", entityType.getName());
    }

When the query method is called, it will be called when splicing sql

public class TableInfo{
 /**
     * Get sql script for logically deleted fields
     *
     * @param startWithAnd Start with and
     * @param isWhere      Is a logical deletion value required
     * @return sql script
     */
    public String getLogicDeleteSql(boolean startWithAnd, boolean isWhere) {
// If there is a delete field in the class, the @ TableLogic annotation is marked, or the field is the same as the global, withLogicDelete has been deleted in code 3
// If the assignment = true, the following spliced deleted = 0 method will be entered
        if (withLogicDelete) {
            String logicDeleteSql = formatLogicDeleteSql(isWhere);
            if (startWithAnd) {
                logicDeleteSql = " AND " + logicDeleteSql;
            }
            return logicDeleteSql;
        }
        return EMPTY;
    }

    /**
     * format logic delete SQL, can be overrided by subclass
     * github #1386
     *
     * @param isWhere true: logicDeleteValue, false: logicNotDeleteValue
     * @return sql
     */
// Real splicing method
    protected String formatLogicDeleteSql(boolean isWhere) {
        final String value = isWhere ? logicDeleteFieldInfo.getLogicNotDeleteValue() : logicDeleteFieldInfo.getLogicDeleteValue();
        if (isWhere) {
            if (NULL.equalsIgnoreCase(value)) {
                return logicDeleteFieldInfo.getColumn() + " IS NULL";
            } else {
                return logicDeleteFieldInfo.getColumn() + EQUALS + String.format(logicDeleteFieldInfo.isCharSequence() ? "'%s'" : "%s", value);
            }
        }
        final String targetStr = logicDeleteFieldInfo.getColumn() + EQUALS;
        if (NULL.equalsIgnoreCase(value)) {
            return targetStr + NULL;
        } else {
            return targetStr + String.format(logicDeleteFieldInfo.isCharSequence() ? "'%s'" : "%s", value);
        }
    }
}

If you don't want to splice deleted = 0 in the sql query, the solution is:

1. Do not set the global logical deletion field. If there is a logical deletion, define the deletion field in the code and mark the @ TableLogic annotation, where you can write the deleted value and the value that will not be deleted. If the global delete field is set, it is applicable to those with fewer deletion scenarios and more queries. And logical deletion is adopted in the project.

2. If the global delete field is set, the delete field defined in the entity class is different from the global. If you add @ TableLogic annotation to that field, the code query will splice the delete = 0 condition. If you do not add annotation, you can set it yourself.

3. If there are many item queries, they all use logical deletion. If you need to find the deleted data during query, you need to write your own xml sql statement. For queries of other lambda expressions, the deleted = 0 condition is automatically added by mybatis.

Keywords: Java Spring Boot SQL

Added by kentish on Thu, 06 Jan 2022 04:25:40 +0200