Яма MyBatis, если я действительно наступил на тег~~

Java

Причина события вот в чем.Требование-проверить данные по условиям и потом отобразить их на фронтенде.Когда писал его я думал что это очень просто.Разве не просто использовать динамический SQL MyBatis для запросить данные?

Реальность все еще очень жестока.После того, как я закончил писать UAT, студенты-передовики сказали, что согласноstateПроверенные данные не соответствуют идеальным данным, этоstateВ то время было всего два значения по дизайну:0и1.

/**
 * 数据状态
 */
@Range(min = 0, max = 1, message = "状态只能为0(未处理),1(已处理)")
private Integer state;

В идеале вы можете передать значение, переданное из внешнего интерфейса, а затем сделать sql-запрос:

<if test="req.state != null and req.state != ''">
            AND md.state = #{req.state}
</if>

Вышеупомянутый sql сначала судитstateне пустой ине пустая строка, затем добавьте сравнениеstateполе. Взгляните первымifС решением проблем нет, но я его вынесreq.stateдаIntegerтипа, смотри внимательноreq.state != nullНет проблем, потом узналreq.state != ''использоватьIntegerСравните с пустой строкой.

Если интерфейс не передает его при запросеreq.stateТотreq.state != null Здесь он не будет удовлетворен, но передняя часть проходит0когда ты придешьreq.state != ''фактически вернулсяfalseТо есть в0 в MyBatis, если синтаксис равен пустой строке:

{
	"state": 0
}

В этом сравнении нет ошибки, и оно немного сбивает с толку, нет другого пути, кроме как посмотреть исходный код MyBatis, чтобы выяснить причину.

Посмотреть исходный код MyBatis

Подробно процесс поиска других исходных кодов MyBatis описывать не будем, ищите прямо здесь.XMLScriptBuilderкласс, найтиifПроцесс разбора грамматики, а затем пошаговое исследование0 == ''причина.XMLScriptBuilderбудет анализироватьtrim,ifДля грамматики, поддерживаемой MyBatis, принцип синтаксического анализа заключается в следующем:NodeHandlerЧтобы анализировать разные теги по отдельности:

  private void initNodeHandlerMap() {
    nodeHandlerMap.put("trim", new TrimHandler());
    nodeHandlerMap.put("where", new WhereHandler());
    nodeHandlerMap.put("set", new SetHandler());
    nodeHandlerMap.put("foreach", new ForEachHandler());
    nodeHandlerMap.put("if", new IfHandler());
    nodeHandlerMap.put("choose", new ChooseHandler());
    nodeHandlerMap.put("when", new IfHandler());
    nodeHandlerMap.put("otherwise", new OtherwiseHandler());
    nodeHandlerMap.put("bind", new BindHandler());
  }

Поскольку это неверно, синтаксисifтеги, просмотрIfHandlerЭто нормально, просто игнорируйте остальное пока.

 private class IfHandler implements NodeHandler {
    public IfHandler() {
      // Prevent Synthetic Access
    }

    @Override
    public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
      MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
      String test = nodeToHandle.getStringAttribute("test");
      IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
      targetContents.add(ifSqlNode);
    }
  }

MyBatis будетifЭтикетка абстрагируется вIfSqlNode:

public class IfSqlNode implements SqlNode {
  private final ExpressionEvaluator evaluator;
  private final String test;
  private final SqlNode contents;

  public IfSqlNode(SqlNode contents, String test) {
    this.test = test;
    this.contents = contents;
    this.evaluator = new ExpressionEvaluator();
  }

  @Override
  public boolean apply(DynamicContext context) {
    if (evaluator.evaluateBoolean(test, context.getBindings())) {
      contents.apply(context);
      return true;
    }
    return false;
  }

}

Наконец немного бровь, MyBatis будетifпомеченtestиспользование собственностиExpressionEvaluatorпроверить,trueили дляfalse:

public class ExpressionEvaluator {

  public boolean evaluateBoolean(String expression, Object parameterObject) {
    Object value = OgnlCache.getValue(expression, parameterObject);
    if (value instanceof Boolean) {
      return (Boolean) value;
    }
    if (value instanceof Number) {
      return new BigDecimal(String.valueOf(value)).compareTo(BigDecimal.ZERO) != 0;
    }
    return value != null;
  }

  public Iterable<?> evaluateIterable(String expression, Object parameterObject) {
    Object value = OgnlCache.getValue(expression, parameterObject);
    if (value == null) {
      throw new BuilderException("The expression '" + expression + "' evaluated to a null value.");
    }
    if (value instanceof Iterable) {
      return (Iterable<?>) value;
    }
    if (value.getClass().isArray()) {
        // the array may be primitive, so Arrays.asList() may throw
        // a ClassCastException (issue 209).  Do the work manually
        // Curse primitives! :) (JGB)
        int size = Array.getLength(value);
        List<Object> answer = new ArrayList<Object>();
        for (int i = 0; i < size; i++) {
            Object o = Array.get(value, i);
            answer.add(o);
        }
        return answer;
    }
    if (value instanceof Map) {
      return ((Map) value).entrySet();
    }
    throw new BuilderException("Error evaluating expression '" + expression + "'.  Return value (" + value + ") was not iterable.");
  }

}

В итоге получил вывод:Используется МибатисомOgnl表达式чтобы получить значение тестового свойства

последний аргумент

Уже известно, что MyBatis используется внутриOgnl表达式,Да или нетOgnl表达式вызвано этим? Вы узнаете это на практике, сначала введите зависимости:

<!-- https://mvnrepository.com/artifact/ognl/ognl -->
<dependency>
    <groupId>ognl</groupId>
    <artifactId>ognl</artifactId>
    <version>2.7.3</version>
</dependency>

Напишите тест программы:

    public static void main(String[] args) {

        Map<String, Object> objectMap = new HashMap<>();
        objectMap.put("state", 0);
        Object value = OgnlCache.getValue("state == ''", objectMap);
        System.out.println(value);
    }

Вывод вышеуказанной программы действительноtrue. . .

Суммировать

Это спазм мозга.IntegerТакже проверьте, является ли это пустой строкой. . .

Запишите эту яму, я надеюсь, что это поможет всем.

Добро пожаловать в официальный аккаунт: «Архитектурный дайджест». Получите эксклюзивные бесплатные учебные ресурсы 120G, которые помогут вашему архитектору учиться!

Фоновый ответ публичного аккаунтаarch028Получить информацию: