利用 MyBatis Plus 的 getter 转字段名

Mybatis-Plus结合lambda表达式获取entity的数据库字段名

代码

@Component
public class ColumnResolver<T> extends AbstractLambdaWrapper<T, ColumnResolver<T>> {

  /**
   * 这个不需要,MyBatis Plus 自己使用的
   *
   * @return null
   */
  @Override
  protected ColumnResolver<T> instance() {
    return null;
  }

  // ---- 以下直接调用父类,只是提升了访问级别

  @Override
  public String columnToString(SFunction<T, ?> column) {
    return super.columnToString(column);
  }
}

使用时直接注入即可

private final ColumnResolver<Entity> columnResolver;

原理

自动添加 Mapper 时解析实体信息生成表名及字段名
LambdaUtils 会全局缓存解析后的信息

MybatisMapperRegistry#addMapper
--->
TableInfoHelper#initTableInfo(org.apache.ibatis.session.Configuration, java.lang.String, java.lang.Class<?>)

从 AbstractLambdaWrapper 的逻辑可以看出,上面的 ColumnResolver 最好做成多例/prototype 的,因为实例只会初始化一次且只会保存一个实体对应的字段缓存。

public abstract class AbstractLambdaWrapper {
  protected String columnToString(SFunction<T, ?> column, boolean onlyColumn) {
    // 缓存获取
    ColumnCache cache = getColumnCache(column);
    return onlyColumn ? cache.getColumn() : cache.getColumnSelect();
  }

  protected ColumnCache getColumnCache(SFunction<T, ?> column) {
    LambdaMeta meta = LambdaUtils.extract(column);
    // 字段名
    String fieldName = PropertyNamer.methodToProperty(meta.getImplMethodName());
    // 实体类
    Class<?> instantiatedClass = meta.getInstantiatedClass();
    tryInitCache(instantiatedClass);
    return getColumnCache(fieldName, instantiatedClass);
  }

  private void tryInitCache(Class<?> lambdaClass) {
    // 没有初始化
    if (!initColumnMap) {
      // 忽略
      final Class<T> entityClass = getEntityClass();
      if (entityClass != null) {
        lambdaClass = entityClass;
      }
      // 根据实体类获取全局缓存, 没有则返回 null (不会实时解析)
      columnMap = LambdaUtils.getColumnMap(lambdaClass);
      Assert.notNull(columnMap, "can not find lambda cache for this entity [%s]", lambdaClass.getName());
      // 当前实例初始化过一次就不会再次解析
      initColumnMap = true;
    }
  }

  private ColumnCache getColumnCache(String fieldName, Class<?> lambdaClass) {
    // 根据字段名获取相应信息, 没有则抛出异常
    ColumnCache columnCache = columnMap.get(LambdaUtils.formatKey(fieldName));
    Assert.notNull(columnCache, "can not find lambda cache for this property [%s] of entity [%s]",
      fieldName, lambdaClass.getName());
    return columnCache;
  }
}

多实体适配

如果要使用多实体,那么就需要进行改部分代码进行适配。
首先要实体对应的column,其次要对初始化方法进行加锁同步处理。

public class ColumnResolver<T> extends AbstractLambdaWrapper<T, ColumnResolver<T>> {

  private final ConcurrentMap<Class<?>, Map<String, ColumnCache>> class2ColumnMap = new ConcurrentHashMap<>();

  private final Object initLock = new Object();

  /**
   * 这个不需要,MyBatis Plus 自己使用的
   *
   * @return null
   */
  @Override
  protected ColumnResolver<T> instance() {
    return null;
  }

  // ---- 以下直接调用父类,只是提升了访问级别

  @Override
  public String columnToString(SFunction<T, ?> column) {
    return super.columnToString(column);
  }

  @Override
  protected ColumnCache getColumnCache(SFunction<T, ?> column) {
    LambdaMeta meta = LambdaUtils.extract(column);
    String fieldName = PropertyNamer.methodToProperty(meta.getImplMethodName());
    Class<?> instantiatedClass = meta.getInstantiatedClass();
    tryInitCache(instantiatedClass);
    return getColumnCache(fieldName, instantiatedClass);
  }

  private void tryInitCache(Class<?> lambdaClass) {
    Map<String, ColumnCache> columnMap = class2ColumnMap.get(lambdaClass);
    if (Objects.nonNull(columnMap)) {
      return;
    }

    synchronized (initLock) {
      tryInitCache0(lambdaClass);
    }
  }

  private void tryInitCache0(Class<?> lambdaClass) {
    Map<String, ColumnCache> columnMap = class2ColumnMap.get(lambdaClass);
    if (Objects.nonNull(columnMap)) {
      return;
    }


    final Class<T> entityClass = getEntityClass();
    if (entityClass != null) {
      lambdaClass = entityClass;
    }
    columnMap = LambdaUtils.getColumnMap(lambdaClass);
    Assert.notNull(columnMap, "can not find lambda cache for this entity [%s]", lambdaClass.getName());
    class2ColumnMap.put(lambdaClass, columnMap);
  }


  private ColumnCache getColumnCache(String fieldName, Class<?> lambdaClass) {
    Map<String, ColumnCache> columnMap = class2ColumnMap.get(lambdaClass);
    ColumnCache columnCache = columnMap.get(LambdaUtils.formatKey(fieldName));
    Assert.notNull(columnCache, "can not find lambda cache for this property [%s] of entity [%s]",
        fieldName, lambdaClass.getName());
    return columnCache;
  }
}
posted @ 2024-01-05 10:41  YangDanMua  阅读(177)  评论(0编辑  收藏  举报