Błąd podczas wiązania danych w formularzu

0

Mam sklep w którym chce dodać nowy produkt, kontroler wygląda w ten sposób :

 @RequestMapping("/product/new")
    public String newProduct(Model model) {
        model.addAttribute("cat", categoryService.getAllCategories());
        model.addAttribute("productf", new ProductCommand());
        return "product/addProduct";
    }

 @PostMapping("product")
    public String saveNewProduct(@ModelAttribute("productf") ProductCommand productCommand, Model model) {
        productService.saveCommand(productCommand);
        return "redirect:/index";
    }

Formularz:

object="${productf}" th:action="@{/product/}" method="post">
    <input type="hidden" th:field="*{id}"/>
    <div class="container" style="margin-top: 20px">
        <div class="form-group">
            <div class="col-md-5">
    <h2>Nowy produkt</h2>
                <label>Nazwa:</label>
                <input type="text" class="form-control" placeholder="Wpisz nazwę produktu" th:field="*{name}"/>

                <label>Producent:</label>
                <input type="text" class="form-control" placeholder="Wpisz nazwę producenta" th:field="*{manufacturer}"/>
                </div>

                <div class="col-md-3">
                <label>Cena:</label>
                <input type="text" class="form-control" placeholder="Wpisz cenę" th:field="*{price}"/>

                    <label>Liczba produktów:</label>
                    <input type="text" class="form-control" placeholder="Wpisz l. produktów" th:field="*{unitsInStock}"/>


                    <label>Wycofany:</label>
                    <select class="form-control" th:field="*{discontinued}">
                        <option th:value="true">Tak</option>
                        <option th:value="false">Nie</option>
                    </select>
                </div>

        <div class="col-md-3 form-group">
            <label>Kategoria:</label>
        </div>
        <div class="col-md-9 form-group">
            <tr th:each="category : ${cat}">
            <div class="radio">
                <label>
                    <input type="checkbox" value="" th:value="${category.id}" th:text="${category.categoryName}" th:field="*{categories}"/>
                </label>
            </div>
        </div>

            <div class="col-md-12 form-group">
                <label>Opis produktu:</label>
                <textarea class="form-control" rows="3" placeholder="Wpisz opis produktu" th:field="*{description}"></textarea>
            </div>

            <div class="col-md-5">
                <button type="submit" class="btn btn-primary bg-info">Submit</button>
            </div>

Product i ProductCommand:

@Entity
@EqualsAndHashCode(exclude = "categories")
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String description;
    private BigDecimal price;
    private String manufacturer;
    private Long unitsInStock;
    private boolean discontinued;
    private Byte[] image;

    //@ElementCollection
    //private Map<String, String> productProperties = new HashMap<>();

    @ManyToMany(cascade = CascadeType.PERSIST)
    @JoinTable(name = "product_category",
            joinColumns = @JoinColumn(name = "product_id"),
    inverseJoinColumns = @JoinColumn(name = "category_id"))
    private Set<Category> categories = new HashSet<>();
}

@NoArgsConstructor
public class ProductCommand {

    private Long id;
    private String name;
    private String description;
    private BigDecimal price;
    private String manufacturer;
    private Long unitsInStock;
    private boolean discontinued;
    private Byte[] image;
    private Set<CategoryCommand> categories = new HashSet<>();
}

I dostaję taki dziwny error po kliknięciu przycisku wyślij:

Resolved exception caused by Handler execution: org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors Field error in object 'productf' on field 'categories': rejected value [1]; codes [typeMismatch.productf.categories,typeMismatch.categories,typeMismatch.java.util.Set,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [productf.categories,categories]; arguments []; default message [categories]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Set' for property 'categories'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'info.mike.webstorev1.commands.CategoryCommand' for property 'categories[0]': no matching editors or conversion strategy found]

Co dziwne, gdy metodę kontrolera POST ustawię na:

 @PostMapping("product")
    public String saveNewProduct(@ModelAttribute("productf") Product product, Model model) {
        productService.saveProduct(product);
        return "redirect:/index";
    }

przy niezmienionej pierwszej metodzie to wszystko działa. Proszę o jakieś wskazówki, bo ja jestem w lesie, obiekty Product i ProductCommand z założenia są identyczne, więc nie mam absolutnie pojęcia, co oznacza ten error i dlaczego przy jednym z nich działa, a przy drugim jest jakiś kłopot z konwersją.

0

Product i ProductCommand z założenia są identyczne... nie.
Szczególnie CategoryCommand i Category nie są identyczne. Sprawdź też tam.

0

Dostajesz błąd bo próbujesz zapisać dane o typie String do kolekcji o typie CategoryCommand, ta linijka o tym mówi : Cannot convert value of type 'java.lang.String' to required type 'info.mike.webstorev1.commands.CategoryCommand' for property 'categories[0]':

0
jarekr000000 napisał(a):

Product i ProductCommand z założenia są identyczne... nie.
Szczególnie CategoryCommand i Category nie są identyczne. Sprawdź też tam.

No okej, różnią się typem kolekcji, ale jak w metodzie GET pod parametrem "cat" przekażę zbiór CategoryCommand to mam ten sam błąd:

@RequestMapping("/product/new")
    public String newProduct(Model model) {
        model.addAttribute("cat", categoryService.getAllCategoriesCommand());
        model.addAttribute("productf", new ProductCommand());
        return "product/addProduct";
    }

Czyli jak przekazuję NewProduct i zbiór Category, to wszystko działa, a gdy przekazuję NewProductCommand i zbiór CategoryCommand to dostaję ten error. Nie wiem dlaczego. Fakt, że w jednej klasie mam zbiór products, a w drugiej nie, ale akurat do niego się nigdzie nie odwołuję.

@Entity
@EqualsAndHashCode(exclude = "products")
public class Category  {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String categoryName;

    @ManyToMany(mappedBy = "categories")
    private Set<Product> products; 
}
@NoArgsConstructor
public class CategoryCommand {

    private Long id;
    private String categoryName;
}
Kuba_or_JJ napisał(a):

Dostajesz błąd bo próbujesz zapisać dane o typie String do kolekcji o typie CategoryCommand, ta linijka o tym mówi : Cannot convert value of type 'java.lang.String' to required type 'info.mike.webstorev1.commands.CategoryCommand' for property 'categories[0]':

No okej, tyle wiem, ale skąd bierze się ten String ? Tak jak napisałem wyżej, gdy przekazuję new Product i Category do moedelu, to Spring, żadnego problemu nie ma.

0

Ok, doszedłem do takich wniosków: nieważne czy w modelu jest Product, czy ProductCommand, BindingResult zawsze zwraca takie same wartości: dla bindingResult.getFieldValue("categories")= 1,4 (są to numery kategorii, mogą być inne), a dla bindingResult.getFieldType("categories") = interface java.util.Set (czyli rodzaj kolekcji w klasie Product/ProductCommand). Czyli dla Product Spring/Hibernate posiada jakiś konwerter, który przekonwertuje Stringi i umieści je w kolekcji, a dla ProductCommand nie posiada i wywala error. Miał ktoś wcześniej podobny problem ? Jakby co klasy oznaczone @Component konwertujące Command do zwykłych obiektów i w drugą stronę oczywiście są..

1 użytkowników online, w tym zalogowanych: 0, gości: 1