All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.mybatis.provider.EntityTable Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2020-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.mybatis.provider;

import io.mybatis.provider.defaults.GenericTypeResolver;
import io.mybatis.provider.util.Utils;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.apache.ibatis.annotations.SelectProvider;
import org.apache.ibatis.builder.annotation.ProviderContext;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultFlag;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeException;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.UnknownTypeHandler;

import java.lang.reflect.Constructor;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 实体表接口,记录实体和表的关系
 *
 * @author liuzh
 */
@Accessors(fluent = true)
public class EntityTable extends EntityProps {
  public static final Pattern            DELIMITER         = Pattern.compile("^[`\\[\"]?(.*?)[`\\]\"]?$");
  public static final String             RESULT_MAP_NAME   = "BaseProviderResultMap";
  /**
   * 原始表名,在拼 SQL 中,使用 {@link #tableName()} 方法,这个方法可能会返回代理方法加工后的值
   */
  @Getter
  @Setter
  protected           String             table;
  /**
   * catalog 名称,配置后,会在表名前面加上 catalog 名称,规则为:catalog.schema.tableName,支持全局 mybatis.provider.catalog 配置
   */
  @Getter
  @Setter
  protected           String             catalog;
  /**
   * schema 名称,配置后,会在表名前面加上 schema 名称,规则为:catalog.schema.tableName,支持全局 mybatis.provider.schema 配置
   */
  @Getter
  @Setter
  protected           String             schema;
  /**
   * 实体类和字段转表名和字段名方式
   */
  @Getter
  @Setter
  protected           String             style;
  /**
   * 实体类
   */
  @Getter
  @Setter
  protected           Class           entityClass;
  /**
   * 字段信息
   */
  @Setter
  protected           List columns;
  /**
   * 初始化完成,可以使用
   */
  @Getter
  @Setter
  protected           boolean            ready;
  /**
   * 使用指定的 <resultMap>
   */
  @Getter
  @Setter
  protected           String             resultMap;
  /**
   * 自动根据字段生成 <resultMap>
   */
  @Getter
  @Setter
  protected           boolean            autoResultMap;
  /**
   * 已初始化自动ResultMap
   */
  protected           List    resultMaps;
  /**
   * 排除指定父类的所有字段
   */
  @Getter
  @Setter
  protected           Class[]         excludeSuperClasses;
  /**
   * 排除指定类型的字段
   */
  @Getter
  @Setter
  protected           Class[]         excludeFieldTypes;
  /**
   * 排除指定字段名的字段
   */
  @Getter
  @Setter
  protected           String[]           excludeFields;
  /**
   * 已经初始化的配置
   */
  protected           Set initConfiguration = new HashSet<>();
  //

  protected EntityTable(Class entityClass) {
    this.entityClass = entityClass;
  }

  public static EntityTable of(Class entityClass) {
    return new EntityTable(entityClass);
  }

  /**
   * 获取 SQL 语句中使用的表名
   */
  public String tableName() {
    return Stream.of(catalog(), schema(), table())
        .filter(s -> s != null && !s.isEmpty())
        .collect(Collectors.joining("."));
  }

  /**
   * 返回所有列
   *
   * @return 所有列信息
   */
  public List columns() {
    if (this.columns == null) {
      this.columns = new ArrayList<>();
    }
    return columns;
  }

  /**
   * 返回所有字段
   *
   * @return 所有字段
   */
  public List fields() {
    return columns().stream().map(EntityColumn::field).collect(Collectors.toList());
  }

  /**
   * 返回所有列名
   *
   * @return 所有列名
   */
  public List columnNames() {
    return columns().stream().map(EntityColumn::column).collect(Collectors.toList());
  }

  /**
   * 返回所有属性名
   *
   * @return 所有属性名
   */
  public List fieldNames() {
    return columns().stream().map(EntityColumn::property).collect(Collectors.toList());
  }

  /**
   * 添加列
   */
  public void addColumn(EntityColumn column) {
    //不重复添加同名的列
    if (!columns().contains(column)) {
      if (column.field().getDeclaringClass() != entityClass()) {
        columns().add(0, column);
      } else {
        columns().add(column);
      }
      column.entityTable(this);
    } else {
      //同名列在父类存在时,说明是子类覆盖的,字段的顺序应该更靠前
      EntityColumn existsColumn = columns().remove(columns().indexOf(column));
      columns().add(0, existsColumn);
    }
  }

  /**
   * 是否使用 resultMaps
   *
   * @param providerContext 当前方法信息
   * @param cacheKey        缓存 key,每个方法唯一,默认和 msId 一样
   * @return true 是,false 否
   */
  protected boolean canUseResultMaps(ProviderContext providerContext, String cacheKey) {
    if (resultMaps != null && !resultMaps.isEmpty()
        && providerContext.getMapperMethod().isAnnotationPresent(SelectProvider.class)) {
      Class resultType = resultMaps.get(0).getType();
      //类型相同时直接返回
      if (resultType == providerContext.getMapperMethod().getReturnType()) {
        return true;
      }
      //可能存在泛型的情况,如 List, Optional, 还有 MyBatis 包含的一些注解
      Class returnType = GenericTypeResolver.getReturnType(
          providerContext.getMapperMethod(), providerContext.getMapperType());
      return resultType == returnType;
    }
    return false;
  }

  /**
   * 当前实体类是否使用 resultMap
   *
   * @return
   */
  public boolean useResultMaps() {
    return resultMaps != null || autoResultMap || Utils.isNotEmpty(resultMap);
  }

  /**
   * 是否已经替换 resultMap
   *
   * @param configuration MyBatis 配置类,慎重操作
   * @param cacheKey      缓存 key,每个方法唯一,默认和 msId 一样
   * @return
   */
  protected boolean hasBeenReplaced(Configuration configuration, String cacheKey) {
    MappedStatement mappedStatement = configuration.getMappedStatement(cacheKey);
    if (mappedStatement.getResultMaps() != null && mappedStatement.getResultMaps().size() > 0) {
      return mappedStatement.getResultMaps().get(0) == resultMaps.get(0);
    }
    return false;
  }

  /**
   * 设置运行时信息,不同方法分别执行一次,需要保证幂等
   *
   * @param configuration   MyBatis 配置类,慎重操作,多数据源或多个配置时,需要区分 Configuration 执行
   * @param providerContext 当前方法信息
   * @param cacheKey        缓存 key,每个方法唯一,默认和 msId 一样
   */
  public void initRuntimeContext(Configuration configuration, ProviderContext providerContext, String cacheKey) {
    //初始化一次,后续不会重复初始化
    if (!initConfiguration.contains(configuration)) {
      initResultMap(configuration, providerContext, cacheKey);
      initConfiguration.add(configuration);
    }
    if (canUseResultMaps(providerContext, cacheKey)) {
      synchronized (cacheKey) {
        if (!hasBeenReplaced(configuration, cacheKey)) {
          MetaObject metaObject = configuration.newMetaObject(configuration.getMappedStatement(cacheKey));
          metaObject.setValue("resultMaps", Collections.unmodifiableList(resultMaps));
        }
      }
    }
  }

  protected void initResultMap(Configuration configuration, ProviderContext providerContext, String cacheKey) {
    //使用指定的 resultMap
    if (Utils.isNotEmpty(resultMap)) {
      synchronized (this) {
        if (resultMaps == null) {
          resultMaps = new ArrayList<>();
          String resultMapId = generateResultMapId(providerContext, resultMap);
          if (configuration.hasResultMap(resultMapId)) {
            resultMaps.add(configuration.getResultMap(resultMapId));
          } else if (configuration.hasResultMap(resultMap)) {
            resultMaps.add(configuration.getResultMap(resultMap));
          } else {
            throw new RuntimeException(entityClass().getName() + " configured resultMap: " + resultMap + " not found");
          }
        }
      }
    }
    //自动生成 resultMap
    else if (autoResultMap) {
      synchronized (this) {
        if (resultMaps == null) {
          resultMaps = new ArrayList<>();
          ResultMap resultMap = genResultMap(configuration, providerContext, cacheKey);
          resultMaps.add(resultMap);
          configuration.addResultMap(resultMap);
        }
      }
    }
  }

  protected String generateResultMapId(ProviderContext providerContext, String resultMapId) {
    if (resultMapId.indexOf(".") > 0) {
      return resultMapId;
    }
    return providerContext.getMapperType().getName() + "." + resultMapId;
  }

  protected ResultMap genResultMap(Configuration configuration, ProviderContext providerContext, String cacheKey) {
    List resultMappings = new ArrayList<>();
    for (EntityColumn entityColumn : selectColumns()) {
      String column = entityColumn.column();
      //去掉可能存在的分隔符,例如:`order`
      Matcher matcher = DELIMITER.matcher(column);
      if (matcher.find()) {
        column = matcher.group(1);
      }
      ResultMapping.Builder builder = new ResultMapping.Builder(configuration, entityColumn.property(), column, entityColumn.javaType());
      if (entityColumn.jdbcType != null && entityColumn.jdbcType != JdbcType.UNDEFINED) {
        builder.jdbcType(entityColumn.jdbcType);
      }
      if (entityColumn.typeHandler != null && entityColumn.typeHandler != UnknownTypeHandler.class) {
        try {
          builder.typeHandler(getTypeHandlerInstance(entityColumn.javaType(), entityColumn.typeHandler));
        } catch (Exception e) {
          throw new RuntimeException(e);
        }
      }
      List flags = new ArrayList<>();
      if (entityColumn.id) {
        flags.add(ResultFlag.ID);
      }
      builder.flags(flags);
      resultMappings.add(builder.build());
    }
    String resultMapId = generateResultMapId(providerContext, RESULT_MAP_NAME);
    ResultMap.Builder builder = new ResultMap.Builder(configuration, resultMapId, entityClass(), resultMappings, true);
    return builder.build();
  }


  /**
   * 实例化TypeHandler
   */
  public TypeHandler getTypeHandlerInstance(Class javaTypeClass, Class typeHandlerClass) {
    if (javaTypeClass != null) {
      try {
        Constructor c = typeHandlerClass.getConstructor(Class.class);
        return (TypeHandler) c.newInstance(javaTypeClass);
      } catch (NoSuchMethodException ignored) {
        // ignored
      } catch (Exception e) {
        throw new TypeException("Failed invoking constructor for handler " + typeHandlerClass, e);
      }
    }
    try {
      Constructor c = typeHandlerClass.getConstructor();
      return (TypeHandler) c.newInstance();
    } catch (Exception e) {
      throw new TypeException("Unable to find a usable constructor for " + typeHandlerClass, e);
    }
  }
  //

  //

  /**
   * 返回主键列,不会为空,当根据主键作为条件时,必须使用当前方法返回的列,没有设置主键时,当前方法返回所有列
   */
  public List idColumns() {
    List idColumns = columns().stream().filter(EntityColumn::id).collect(Collectors.toList());
    if (idColumns.isEmpty()) {
      return columns();
    }
    return idColumns;
  }

  /**
   * 返回普通列,排除主键字段,当根据非主键作为条件时,必须使用当前方法返回的列
   */
  public List normalColumns() {
    return columns().stream().filter(column -> !column.id()).collect(Collectors.toList());
  }

  /**
   * 返回查询列,当获取查询列时,必须使用当前方法返回的列
   */
  public List selectColumns() {
    return columns().stream().filter(EntityColumn::selectable).collect(Collectors.toList());
  }

  /**
   * 返回查询列,默认所有列,当使用查询条件列时,必须使用当前方法返回的列
   */
  public List whereColumns() {
    return columns();
  }

  /**
   * 所有 insert 用到的字段,当插入列时,必须使用当前方法返回的列
   */
  public List insertColumns() {
    return columns().stream().filter(EntityColumn::insertable).collect(Collectors.toList());
  }

  /**
   * 所有 update 用到的字段,当更新列时,必须使用当前方法返回的列
   */
  public List updateColumns() {
    return columns().stream().filter(EntityColumn::updatable).collect(Collectors.toList());
  }

  /**
   * 所有 GROUP BY 到的字段,默认为空,当使用 GROUP BY 列时,必须使用当前方法返回的列
   */
  public Optional> groupByColumns() {
    return Optional.empty();
  }

  /**
   * 所有 HAVING 到的字段,默认为空,当使用 HAVING 列时,必须使用当前方法返回的列
   */
  public Optional> havingColumns() {
    return Optional.empty();
  }

  /**
   * 所有排序用到的字段
   */
  public Optional> orderByColumns() {
    List orderByColumns = columns().stream()
        .filter(c -> Utils.isNotEmpty(c.orderBy))
        .sorted(Comparator.comparing(EntityColumn::orderByPriority))
        .collect(Collectors.toList());
    if (orderByColumns.size() > 0) {
      return Optional.of(orderByColumns);
    }
    return Optional.empty();
  }

  /**
   * 所有查询列,形如 column1, column2, ...
   */
  public String baseColumnList() {
    return selectColumns().stream().map(EntityColumn::column).collect(Collectors.joining(","));
  }

  /**
   * 所有查询列,形如 column1 AS property1, column2 AS property2, ...
   */
  public String baseColumnAsPropertyList() {
    //当存在 resultMaps 时,查询列不能用别名
    if (useResultMaps()) {
      return baseColumnList();
    }
    return selectColumns().stream().map(EntityColumn::columnAsProperty).collect(Collectors.joining(","));
  }

  /**
   * 所有 insert 列,形如 column1, column2, ...,字段来源 {@link #insertColumns()}
   */
  public String insertColumnList() {
    return insertColumns().stream().map(EntityColumn::column).collect(Collectors.joining(","));
  }

  /**
   * 所有 order by 字段,默认空,字段来源 {@link #groupByColumns()} 参考值: column1, column2, ...
   * 

* 默认重写 {@link #groupByColumns()} 方法即可,当前方法不需要重写 */ public Optional groupByColumnList() { Optional> groupByColumns = groupByColumns(); return groupByColumns.map(entityColumns -> entityColumns.stream().map(EntityColumn::column) .collect(Collectors.joining(","))); } /** * 带上 GROUP BY 前缀的方法,默认空,默认查询列来自 {@link #groupByColumnList()} *

* 默认重写 {@link #groupByColumns()} 方法即可,当前方法不需要重写 */ public Optional groupByColumn() { Optional groupByColumnList = groupByColumnList(); return groupByColumnList.map(s -> " GROUP BY " + s); } /** * 所有 having 字段,默认空,字段来源 {@link #havingColumns()} 参考值: column1, column2, ... */ public Optional havingColumnList() { Optional> havingColumns = havingColumns(); return havingColumns.map(entityColumns -> entityColumns.stream().map(EntityColumn::column) .collect(Collectors.joining(","))); } /** * 带上 HAVING 前缀的方法,默认空,默认查询列来自 {@link #havingColumnList()} */ public Optional havingColumn() { Optional havingColumnList = havingColumnList(); return havingColumnList.map(s -> " HAVING " + s); } /** * 所有 order by 字段,默认空,字段来源 {@link #orderByColumns()} 参考值: column1, column2, ... *

* 默认重写 {@link #orderByColumns()} 方法即可,当前方法不需要重写 */ public Optional orderByColumnList() { Optional> orderByColumns = orderByColumns(); return orderByColumns.map(entityColumns -> entityColumns.stream() .map(column -> column.column() + " " + column.orderBy()) .collect(Collectors.joining(","))); } /** * 带上 ORDER BY 前缀的方法,默认空,默认查询列来自 {@link #orderByColumnList()} *

* 默认重写 {@link #orderByColumns()} 方法即可,当前方法不需要重写 */ public Optional orderByColumn() { Optional orderColumnList = orderByColumnList(); return orderColumnList.map(s -> " ORDER BY " + s); } /** * 是否需要排除父类 * * @param superClass 父类 * @return true - 需要排除,false - 不需要排除 */ public boolean isExcludeSuperClass(Class superClass) { if (excludeSuperClasses != null) { for (Class clazz : excludeSuperClasses) { if (clazz == superClass) { return true; } } } return false; } /** * 是否需要排除指定的字段 * * @param field 字段 * @return true - 需要排除,false - 不需要排除 */ public boolean isExcludeField(EntityField field) { if (excludeFieldTypes != null) { Class fieldType = field.getType(); for (Class clazz : excludeFieldTypes) { if (clazz == fieldType) { return true; } } } if (excludeFields != null) { String fieldName = field.getName(); for (String excludeField : excludeFields) { if (excludeField.equals(fieldName)) { return true; } } } return false; } // @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof EntityTable)) return false; EntityTable entity = (EntityTable) o; return tableName().equals(entity.tableName()); } @Override public int hashCode() { return Objects.hash(tableName()); } @Override public String toString() { return tableName(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy