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

#java #spring #spring-boot #jdbctemplate

#java #spring #весенняя загрузка #jdbctemplate

Вопрос:

Я изучаю 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?

Комментарии:

1. Сообщение непонятно? Недопустимо значение NULL для столбца «DELIVERYNAME»;

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

3. Не могли бы вы, пожалуйста, добавить класс Order также в post..

4. Добавлено. Для этого я использовал Lombok

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

Ответ №1:

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

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

Из книги

SimpleJdbcInsert имеет пару полезных методов для выполнения вставки: execute() и executeAndReturnKey(). Оба принимают Map, где ключи map соответствуют именам столбцов в таблице, в которую вставляются данные. Значения карты вставляются в эти столбцы.

Имена ключей — это поля из Order.java

Ответ №2:

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

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

Способы решения этой:-

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

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

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

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

Ответ №3:

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

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

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

Комментарии:

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

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

Ответ №4:

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

     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 из следующей главы.

Ответ №5:

Имена полей в классе 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;
    }
}
  

Ответ №6:

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