# 2023年6月4日 - 2.1.1 发布

  • 解决接口方法冲突
  • 优化Fn中的字段处理
  • LogicalMapper 实现和部分测试 ** by ariohe**
  • Fixed typo in common module ** by TinySnow**

# 增加逻辑删除 LogicalMapper

LogicalMapper 是一个逻辑删除的通用 Mapper,通过 @LogicalColumn 注解实现逻辑删除。

public interface LogicalMapper<T, I extends Serializable> extends BaseMapper<T, I>, FnMapper<T> {
  /* BaseMapper +++ */

  @Override
  @Lang(Caching.class)
  @UpdateProvider(type = LogicalProvider.class, method = "updateByPrimaryKeySelectiveWithForceFields")
  int updateByPrimaryKeySelectiveWithForceFields(@Param("entity") T entity, @Param("fns") Fn.Fns<T> forceUpdateFields);

  /* BaseMapper --- */

  /* FnMapper +++ */

  @Override
  @Lang(Caching.class)
  @SelectProvider(type = LogicalProvider.class, method = "selectColumns")
  Optional<T> selectColumnsOne(@Param("entity") T entity, @Param("fns") Fn.Fns<T> selectFields);

  @Override
  @Lang(Caching.class)
  @SelectProvider(type = LogicalProvider.class, method = "selectColumns")
  List<T> selectColumns(@Param("entity") T entity, @Param("fns") Fn.Fns<T> selectFields);

  /* FnMapper --- */

  /* EntityMapper +++ */

  @Override
  @Lang(Caching.class)
  @DeleteProvider(type = LogicalProvider.class, method = "deleteByPrimaryKey")
  int deleteByPrimaryKey(I id);

  @Override
  @Lang(Caching.class)
  @DeleteProvider(type = LogicalProvider.class, method = "delete")
  int delete(T entity);

  @Override
  @Lang(Caching.class)
  @UpdateProvider(type = LogicalProvider.class, method = "updateByPrimaryKey")
  int updateByPrimaryKey(T entity);

  @Override
  @Lang(Caching.class)
  @UpdateProvider(type = LogicalProvider.class, method = "updateByPrimaryKeySelective")
  int updateByPrimaryKeySelective(T entity);

  @Override
  @Lang(Caching.class)
  @SelectProvider(type = LogicalProvider.class, method = "selectByPrimaryKey")
  Optional<T> selectByPrimaryKey(I id);

  @Override
  @Lang(Caching.class)
  @SelectProvider(type = LogicalProvider.class, method = "select")
  Optional<T> selectOne(T entity);

  @Override
  @Lang(Caching.class)
  @SelectProvider(type = LogicalProvider.class, method = "select")
  List<T> selectList(T entity);

  @Override
  @Lang(Caching.class)
  @SelectProvider(type = LogicalProvider.class, method = "selectCount")
  long selectCount(T entity);

  /* EntityMapper --- */

  /* CursorMapper +++ */

  @Override
  @Lang(Caching.class)
  @SelectProvider(type = LogicalProvider.class, method = "select")
  Cursor<T> selectCursor(T entity);

  @Override
  @Lang(Caching.class)
  @SelectProvider(type = LogicalProvider.class, method = "selectByExample")
  Cursor<T> selectCursorByExample(Example<T> example);

  /* CursorMapper --- */

  /* ExampleMapper +++ */
  @Override
  default Example<T> example() {
    return BaseMapper.super.example();
  }

  @Override
  @Lang(Caching.class)
  @DeleteProvider(type = LogicalProvider.class, method = "deleteByExample")
  int deleteByExample(Example<T> example);

  @Override
  @Lang(Caching.class)
  @UpdateProvider(type = LogicalProvider.class, method = "updateByExample")
  int updateByExample(@Param("entity") T entity, @Param("example") Example<T> example);

  @Override
  @Lang(Caching.class)
  @UpdateProvider(type = LogicalProvider.class, method = "updateByExampleSetValues")
  int updateByExampleSetValues(@Param("example") Example<T> example);

  @Override
  @Lang(Caching.class)
  @UpdateProvider(type = LogicalProvider.class, method = "updateByExampleSelective")
  int updateByExampleSelective(@Param("entity") T entity, @Param("example") Example<T> example);

  @Override
  @Lang(Caching.class)
  @SelectProvider(type = LogicalProvider.class, method = "selectByExample")
  List<T> selectByExample(Example<T> example);

  @Override
  @Lang(Caching.class)
  @SelectProvider(type = LogicalProvider.class, method = "selectByExample")
  Optional<T> selectOneByExample(Example<T> example);

  @Override
  @Lang(Caching.class)
  @SelectProvider(type = LogicalProvider.class, method = "countByExample")
  long countByExample(Example<T> example);

  @Override
  List<T> selectByExample(Example<T> example, RowBounds rowBounds);

  /* ExampleMapper --- */
}

示例:

public class User {
  @Entity.Column(id = true, useGeneratedKeys = true)
  private Long    id;
  @Entity.Column("name")
  private String  userName;
  @Entity.Column
  private String  sex;
  
  
  @LogicalColumn(delete = "0")
  @Entity.Column(updatable = false, insertable = false)
  private Boolean status;
}

逻辑删除查询时过滤删除的数据,对应XML代码如下:

<script>
  SELECT
  <choose>
    <when test="fns != null and fns.isNotEmpty()">
      ${fns.baseColumnAsPropertyList()}
    </when>
    <otherwise>
      id,name AS userName,sex,status
    </otherwise>
  </choose>
  FROM user
  <trim prefix="WHERE" prefixOverrides="WHERE |OR |AND " suffixOverrides="" suffix="">
    <if test="_parameter != null">
      <where>
        <if test="entity.id != null">
          AND id = #{entity.id}
        </if>
        <if test="entity.userName != null">
          AND name = #{entity.userName}
        </if>
        <if test="entity.sex != null">
          AND sex = #{entity.sex}
        </if>
        <if test="entity.status != null">
          AND status = #{entity.status}
        </if>
      </where>
    </if>
    AND status != 0
  </trim>
</script>

WHERE 条件中一定包含 status != 0

DELETE 方法变成了 UPDATE:

<script>
  UPDATE user SET status = 0
  ${@io.mybatis.provider.util.Assert@notNull(_parameter, 'Parameter cannot be null')}

  <where>
    <if test="id != null">
      AND id = #{id}
    </if>
    <if test="userName != null">
      AND name = #{userName}
    </if>
    <if test="sex != null">
      AND sex = #{sex}
    </if>
    <if test="status != null">
      AND status = #{status}
    </if>
    AND status != 0

  </where>
</script>