Как обновить строку и вставить ее, если она не существует, без ошибочного повышения auto_increment [duplicate]

Что такое NullPointerException?

Хорошим местом для начала является JavaDocs . Они охватывают это:

Брошено, когда приложение пытается использовать null в случае, когда требуется объект. К ним относятся:

  • Вызов метода экземпляра нулевого объекта.
  • Доступ или изменение поля нулевого объекта.
  • Выполнение длины null, как если бы это был массив.
  • Доступ или изменение слотов с нулевым значением, как если бы это был массив.
  • Бросать нуль, как если бы это было значение Throwable.

Приложения должны бросать экземпляры этого класса для указания других незаконных видов использования нулевого объекта.

Также, если вы попытаетесь использовать нулевую ссылку с synchronized, который также выдаст это исключение, за JLS :

SynchronizedStatement:
    synchronized ( Expression ) Block
  • В противном случае, если значение выражения равно null, NullPointerException.

Как это исправить?

Итак, у вас есть NullPointerException. Как вы это исправите? Возьмем простой пример, который выдает NullPointerException:

public class Printer {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.print();
    }
}

Идентифицирует нулевые значения

. Первый шаг - точно определить , значения которого вызывают исключение . Для этого нам нужно выполнить некоторую отладку. Важно научиться читать stacktrace . Это покажет вам, где было выбрано исключение:

Exception in thread "main" java.lang.NullPointerException
    at Printer.printString(Printer.java:13)
    at Printer.print(Printer.java:9)
    at Printer.main(Printer.java:19)

Здесь мы видим, что исключение выбрано в строке 13 (в методе printString). Посмотрите на строку и проверьте, какие значения равны нулю, добавив протоколирующие операторы или используя отладчик . Мы обнаруживаем, что s имеет значение null, а вызов метода length на него вызывает исключение. Мы видим, что программа прекращает бросать исключение, когда s.length() удаляется из метода.

Трассировка, где эти значения взяты из

Затем проверьте, откуда это значение. Следуя вызовам метода, мы видим, что s передается с printString(name) в методе print(), а this.name - null.

Трассировка, где эти значения должны быть установлены

Где установлен this.name? В методе setName(String). С некоторой дополнительной отладкой мы видим, что этот метод вообще не вызывается. Если этот метод был вызван, обязательно проверьте порядок , что эти методы вызывают, а метод set не будет называться после методом печати. ​​

Этого достаточно, чтобы дать нам решение: добавить вызов printer.setName() перед вызовом printer.print().

Другие исправления

Переменная может иметь значение по умолчанию setName может помешать ему установить значение null):

private String name = "";

Либо метод print, либо printString может проверить значение null например:

printString((name == null) ? "" : name);

Или вы можете создать класс, чтобы name всегда имел ненулевое значение :

public class Printer {
    private final String name;

    public Printer(String name) {
        this.name = Objects.requireNonNull(name);
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer("123");
        printer.print();
    }
}

См. также:

Я все еще не могу найти проблему

Если вы попытались отладить проблему и до сих пор не имеете решения, вы можете отправить вопрос для получения дополнительной справки, но не забудьте включить то, что вы пробовали до сих пор. Как минимум, включите stacktrace в вопрос и отметьте важные номера строк в коде. Также попробуйте сначала упростить код (см. SSCCE ).

16
задан Arslan Ali 7 May 2014 в 12:50
поделиться

2 ответа

Это поведение документировано:

Если вы укажете ON DUPLICATE KEY UPDATE и вставили строку, которая приведет к дублированию значения в индексе UNIQUE или PRIMARY KEY, MySQL выполнит ОБНОВЛЕНИЕ старой строки. Например, если столбец a объявлен как UNIQUE и содержит значение 1, следующие два утверждения имеют схожий эффект:

    INSERT INTO table (a,b,c) VALUES (1,2,3)   ON DUPLICATE KEY UPDATE c=c+1;

    UPDATE table SET c=c+1 WHERE a=1;

(эффекты не идентичны для таблицы InnoDB, где a - increment column. В столбце auto-increment оператор INSERT увеличивает значение автоинкремента, но UPDATE не делает этого.)

Вот простое объяснение. Сначала MySQL пытается сделать вставку. Это когда идентификатор автоматически увеличивается. После инкремента он остается. Затем обнаруживается дубликат и происходит обновление. Но значение будет пропущено.

Вы не должны зависеть от auto_increment, не имеющего пробелов. Если это требование, накладные расходы на обновления и вставки значительно больше. По существу, вам нужно поместить блокировку на всю таблицу и перенумеровать все, что нужно перенумеровать, как правило, с помощью триггера. Лучшим решением является вычисление инкрементных значений на выходе.

28
ответ дан Gordon Linoff 20 August 2018 в 07:11
поделиться
  • 1
    +1. Хорошо сказано. Спасибо за включение фактической документации. ;-) – ghoti 7 May 2014 в 13:01
  • 2
    Если auto_increment не является требованием, вы думаете, что разрыв в порядке? – newbie 7 May 2014 в 13:15
  • 3
    @newbie. , , Используйте auto_increment для того, для чего он предназначен, чтобы обеспечить возрастающую последовательность значений по мере добавления строк. Если вам нужно что-то еще, используйте соответствующий механизм, например, вычисление значений на выходе или использование триггеров. – Gordon Linoff 7 May 2014 в 15:11
  • 4
    Это глупая проблема с mysql. Хорошо, я не ретранслирую на нем, чтобы быть без пробелов, но сегодня я получил предел в 16000000+ (MEDIUMINT), когда у меня есть только около 500000 записей, которые часто обновляются. Проблема в том, что у меня есть еще одна таблица с размером выше 250 ГБ, связанная с этим, и обновить ее до INT (и свободного пространства для хранения, когда его полностью ненужное) занимает несколько дней. – lapkritinis 27 March 2017 в 09:59
  • 5
    Я не понимаю причины этого дизайна. Хотя я не возражаю против пробелов, я не вижу причин для автоматического повторения, поскольку идентификатор никогда не существовал, поэтому он просто создает множество идентификаторов мусора для ничего. – jsmars 26 February 2018 в 14:52

INSERT INTO table_blah ( material_item, ... hidden ) VALUES ( data, ... data ) ON DUPLICATE KEY UPDATE material_item = data, ... hidden = data

Да удалите ID = ID, поскольку он автоматически добавит, где PRIMARY KEY = PRIMARY KEY ...

-1
ответ дан Seti 20 August 2018 в 07:11
поделиться
  • 1
    но что, если у него есть дублирующий ключ, он будет увеличиваться правильно? – newbie 7 May 2014 в 12:53
  • 2
    Это не будет, если ваш стол MyISSAM, на InnoDB он будет. Есть другие способы исправить проблему в InnoDB – Seti 7 May 2014 в 12:58
Другие вопросы по тегам:

Похожие вопросы: