Я изучаю 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?
посмотрите на скриншот. Значения не нулевые
Не могли бы вы также добавить класс заказа в сообщение.
Добавлен. Я использовал Ломбок для этого
В javadoc SimpleJdbcInsert говорится: Все, что вам нужно предоставить, это имя таблицы и карта, содержащая имена столбцов и значения столбцов. В вашей таблице есть столбец с именем DELIVERYNAME. Есть ли ключ с таким именем на карте, которую вы создаете? В вашем классе Order есть свойство с именем name. Я сомневаюсь, что преобразование Order в Map с помощью ObjectMapper волшебным образом превратит его в ключ с именем DELIVERYNAME.
Спасибо! ObjectMapper создает ключи в Map, названные так же, как поля в классе Order. Я изменил схему SQL - имена полей в базе данных теперь равны ключу карты. Это сработало.




Я просмотрел снимок экрана и не вижу значения для столбца DELIVERYNAME.
Проблема связана с тем, что таблица, в которой вы выполняете операцию вставки, не допускает нулевых значений для столбца DELIVERYNAME.
Способы решить это: -
Удалите ограничения NOT NULL из столбца в определении таблицы.
Удалите аннотацию @NotBlank из членов объекта, если вы не уверены, будут ли данные пустыми или нет.
Используйте правильное сопоставление между именами столбцов в таблице и столбцами с именем элемента в Class/Entity Definition. Вы можете использовать аннотацию @Column, чтобы легко сделать это.
Насколько я могу понять по вашему коду и снимку экрана, проблема связана с неправильным сопоставлением имен столбцов и членов объекта.
Рекомендуется, чтобы кодировщик Spring Boot не полагался на автоматическое сопоставление, вручную выполнял сопоставление с помощью аннотации @Column или сопоставления через файл конфигурации спящего режима.
Проблема связана с именем атрибута в классе Order. Они должны отражать имя столбца соответствующей таблицы. Например:
частная строка deliveryName; -> будет сопоставлено с полем deliveryName в db.
Проблема возникла, поскольку в schema.sql поле называется «имя доставки», а в классе ингредиентов атрибут называется «имя».
пожалуйста, добавьте немного больше пояснений к этому ответу
это может быть комментарий.
Этот ответ для тех, кто следует примерам из книги "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 и т. д.
Сообщение не понятно? NULL не допускается для столбца "DELIVERYNAME";