Как исправить ошибку SimpleJdbcInsert в приложении SpringBoot

Я изучаю Spring Boot, читая книгу Spring in Action 5. Я пытаюсь вставить данные во встроенную базу данных H2. Я использую метод SimpleJdbcInsert и executeAndReturnKey.

Вот конструктор:

 @Autowired
public JdbcOrderRepository(JdbcTemplate jdbc) {

this.orderInserter = new SimpleJdbcInsert(jdbc).withTableName("Taco_Order").usingGeneratedKeyColumns("id");
    this.orderTacoInserter = new SimpleJdbcInsert(jdbc).withTableName("Taco_Order_Tacos");
    this.objectMapper = new ObjectMapper();
}

Вот методы для вставки данных:

@Override
public Order save(Order order) {

    order.setPlacedAt(new Date());
    long orderId = saveOrderDetails(order);
    order.setId(orderId);
    List<Taco> tacos = order.getTacos();

    for (Taco taco : tacos)
        saveTacoToOrder(taco, orderId);

    return order;
}

private long saveOrderDetails(Order order) {
    Map<String, Object> values = objectMapper.convertValue(order, Map.class);
    values.put("placedAt", order.getPlacedAt());
    long orderId = orderInserter.executeAndReturnKey(values).longValue();

    return orderId;
}

Ошибка в этой строке:

long orderId = orderInserter.executeAndReturnKey(values).longValue();

Текст ошибки:

 There was an unexpected error (type=Internal Server Error, status=500).
PreparedStatementCallback; ???????? NULL ?? ????????? ??? ???? 
"DELIVERYNAME" NULL not allowed for column "DELIVERYNAME"; SQL statement: 
INSERT INTO Taco_Order (DELIVERYNAME, DELIVERYSTREET, DELIVERYCITY, 
DELIVERYSTATE, DELIVERYZIP, CCNUMBER, CCEXPIRATION, CCCVV, PLACEDAT) 
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?) [23502-197]; nested exception is 
org.h2.jdbc.JdbcSQLException: ???????? NULL ?? ????????? ??? ???? 
"DELIVERYNAME" NULL not allowed for column "DELIVERYNAME"; SQL statement: 
INSERT INTO Taco_Order (DELIVERYNAME, DELIVERYSTREET, DELIVERYCITY, 
DELIVERYSTATE, DELIVERYZIP, CCNUMBER, CCEXPIRATION, CCCVV, PLACEDAT) 
   VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?) [23502-197]

Но в отладчике все в порядке: параметр value полностью загружен значениями. идея

Порядок класса:

@Data
public class Order {

@NotBlank(message = "Name is required")
private String name;

@NotBlank(message = "Name is required")
private String street;

@NotBlank(message = "Name is required")
private String city;

@NotBlank(message = "Name is required")
private String state;

@NotBlank(message = "Name is required")
private String zip;

@CreditCardNumber(message = "Not valid cc")
private String ccNumber;

@Pattern(regexp = "^(0[1-9]|1[0-2])([\/])([1-9][0-9])$", message = "Must be formatted MM/YY")
private String ccExpiration;

@Digits(integer = 3, fraction = 0, message = "Invalid CVV")
private String ccCVV;

private Long id;
private Date placedAt;

List<Taco> tacos = new ArrayList<>();

public void addDesign(Taco design) {
    this.tacos.add(design);
}

}

Как решить эту проблему и вставить данные в базу данных H2 с помощью SimpleJdbcInsert?

Сообщение не понятно? NULL не допускается для столбца "DELIVERYNAME";

Jens 23.03.2019 23:40

посмотрите на скриншот. Значения не нулевые

art 23.03.2019 23:42

Не могли бы вы также добавить класс заказа в сообщение.

MyTwoCents 24.03.2019 08:58

Добавлен. Я использовал Ломбок для этого

art 24.03.2019 09:19

В javadoc SimpleJdbcInsert говорится: Все, что вам нужно предоставить, это имя таблицы и карта, содержащая имена столбцов и значения столбцов. В вашей таблице есть столбец с именем DELIVERYNAME. Есть ли ключ с таким именем на карте, которую вы создаете? В вашем классе Order есть свойство с именем name. Я сомневаюсь, что преобразование Order в Map с помощью ObjectMapper волшебным образом превратит его в ключ с именем DELIVERYNAME.

JB Nizet 24.03.2019 09:26

Спасибо! ObjectMapper создает ключи в Map, названные так же, как поля в классе Order. Я изменил схему SQL - имена полей в базе данных теперь равны ключу карты. Это сработало.

art 24.03.2019 09:47
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
6
1 392
6

Ответы 6

Я просмотрел снимок экрана и не вижу значения для столбца DELIVERYNAME.

Проблема связана с тем, что таблица, в которой вы выполняете операцию вставки, не допускает нулевых значений для столбца DELIVERYNAME.

Способы решить это: -

  1. Удалите ограничения NOT NULL из столбца в определении таблицы.

  2. Удалите аннотацию @NotBlank из членов объекта, если вы не уверены, будут ли данные пустыми или нет.

  3. Используйте правильное сопоставление между именами столбцов в таблице и столбцами с именем элемента в Class/Entity Definition. Вы можете использовать аннотацию @Column, чтобы легко сделать это.

Насколько я могу понять по вашему коду и снимку экрана, проблема связана с неправильным сопоставлением имен столбцов и членов объекта. Рекомендуется, чтобы кодировщик Spring Boot не полагался на автоматическое сопоставление, вручную выполнял сопоставление с помощью аннотации @Column или сопоставления через файл конфигурации спящего режима.

Проблема связана с именем атрибута в классе Order. Они должны отражать имя столбца соответствующей таблицы. Например:

частная строка deliveryName; -> будет сопоставлено с полем deliveryName в db.

Проблема возникла, поскольку в schema.sql поле называется «имя доставки», а в классе ингредиентов атрибут называется «имя».

пожалуйста, добавьте немного больше пояснений к этому ответу

mahen3d 27.09.2019 05:44

это может быть комментарий.

c-an 27.09.2019 05:50

Этот ответ для тех, кто следует примерам из книги "Spring в действии". Вы не будете делать это в реальной жизни, но если вы читаете эту книгу, разумно заменить это:

    Map<String, Object> values = objectMapper.convertValue(order, Map.class);
    values.put("placedAt", order.getPlacedAt());

с этим:

        Map<String,Object> values = new HashMap<>();
        values.put("ID", order.getId());
        values.put("PLACEDAT", order.getPlacedAt());
        values.put("DELIVERYNAME", order.getName());
        values.put("DELIVERYSTREET", order.getStreet());
        values.put("DELIVERYCITY", order.getCity());
        values.put("DELIVERYSTATE", order.getState());
        values.put("DELIVERYZIP", order.getZip());
        values.put("CCNUMBER", order.getCcNumber());
        values.put("CCEXPIRATION", order.getCcExpiration());
        values.put("CCCVV", order.getCcCVV());

В предыдущих ответах упоминалось об изменении схемы, которая вызовет проблемы, если вы будете следовать примерам из следующих глав или использовать аннотации jpa для выполнения сопоставления, но, по словам автора книги, вы не знаете о них, потому что они представлены в следующих главах. Кроме того, вы не будете использовать класс JdbcOrderRepository из следующей главы.

Сегодня я столкнулся с той же проблемой. Я смог решить это.

Чтобы исправить это удаление, перейдите в schema.sql(файл, в котором вы создаете таблицы) и удалите доставку из полей в таблице Taco_Order.

Из книги

SimpleJdbcInsert has a couple of useful methods for executing the insert: execute() and executeAndReturnKey(). Both accept a Map, where the map keys correspond to the column names in the table the data is inserted into. The map values are inserted into those columns.

Название ключей - это поля из Order.java

Имена полей в классе Order должны совпадать с именами столбцов в таблице Taco_Order.

@Data
public class Order {
    private Long id;

    private Date placedAt;

    @NotBlank(message = "Podanie imienia i nazwiska jest obowiazkowe")
    private String deliveryName;

    @NotBlank(message = "Podanie ulicy jest obowiązkowe")
    private String deliveryStreet;

    @NotBlank(message = "Podanie miasta jest obowiązkowe")
    private String deliveryCity;

    @NotBlank(message = "Podanie województwa jest obowiązkowe")
    private String deliveryState;

    @NotBlank(message = "Podanie kodu pocztowego jest obowiązkowe")
    private String deliveryZip;

    @CreditCardNumber(message = "To nie jest prawidłowy numer karty kredytowej")
    private String ccNumber;

    @Pattern(regexp = "^(0[1-9]|1[0-2])([\/])([1-9][0-9])$", message = "Wartość musi być w formacie MM/RR")
    private String ccExpiration;

    @Digits(integer = 3, fraction = 0, message = "Nieprawidłowy kod CVV")
    private String ccCVV;

    public List<Taco> tacos = new ArrayList<>();

    public void addDesign(Taco design) {
        this.tacos.add(design);
    }

    public List<Taco> getTacos() {
        return tacos;
    }
}

Сегодня я столкнулся с той же проблемой, и все, что я забыл сделать, это обновить поля в файле order.html на deliveryName, deliveryCity и т. д.

Другие вопросы по теме