SpringMVC+Hibernate+Thymeleaf ManyToOne formularz wwww

0

Kombinuję i nic. Przejrzałem już cały internet :) Próbowałem różnych rozwiązań, ale nic mi z tego nie wychodzi. Być może jest gdzieś jakiś dobry tutorial jak zrobić formularz www, w którym można dodawać dane do bazy zawierający relacje? (formularz bez relacji to nie problem, takich tutoriali jest milion w internecie i jestem w stanie to zrobić)

To na czym jestem w tej chwili to: (nazwy przykładowe, żeby łatwiej było opisywać):

  • mam stworzoną relacyjną bazę danych, tabele Pracodawca, Pracownik
  • mam formularz z polami input (tabela Pracodawca) i polami select (z tabeli Pracownik) zwracającymi ID
  • dwie klasy encje

Zatwierdzenie formularza powoduje wysłanie danych, i obiekt ma być zapisany do bazy, ale obiekt Pracodawca nie jest tworzony, ponieważ (dość logiczne w sumie) zamiast parametru typu Pracownik pojawia się wartość typu String (czyli ID pracownika). Próbowałem stworzyć konwerter (kod poniżej), ale ciągle coś mi nie działa... Zanim wkleję więcej kodu, pytanie czy ja w ogóle idę w dobrym kierunku? Czy rzeczywiście powinien być konwerter, czy może to się powinno w jakiś 'magiczny' sposób robić samo, ale się nie robi bo na przykład spaprałem coś z encjami?

public class UserTypeConverter implements Converter<String, User> {

    private UserHelper userhelper;

    @Override
    public User convert(String s) {
        return userhelper.getUserByID(Integer.parseInt(s));
    }
} 

Pytanie drugie dodatkowe: czy te framworki to najlepszy z możliwych wybór jeżeli chodzi o tworzenie www z dużą ilością formularzy i relacji w bazie?

0

Specem nie jestem, też dopiero zaczynam. Pewnie da się to zrobić lepiej i ktoś mnie poprawi.

Jeżeli robisz formularz z polem typu select to w kontrolerze możesz przekazać ów obiekt do widoku :

@RequestMapping(value = "/myForm", method = RequestMethod.GET)
    public ModelAndView form() {
        ModelAndView mav = new ModelAndView("empList");
        mav.addObject("employeeList", employeeService.getEmployees());
        mav.addObject("employer", new Employer());
        return mav;
    }

Dzięki temu w thymeleaf możesz odwołać się bezpośrednio do obiektów typu Employer, który stworzyłeś w kontrolerze (na razie pust) th:object="${employer}" i listy pracowników iterowanych do option :

<select class="form-control" th:field="*{employee}">
<option th:each="p : ${employeeList}" th:value="${p.name}" th:text="${p.name}"></option>
</select>

Zakładam że Employer posiada listę obiektów typu Employee.
Do wysłaniu formularza robisz kontroler :

 @RequestMapping(value = "/add", method = RequestMethod.POST)
    public String add(@Valid @ModelAttribute Employer employer, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return "error";
        }
        employerService.addEmployer(employer);
        return "redirect:empList";
    }

W serwisach piszesz sobie logikę która pewnie sprowadzi się do zapisania rekordu do bazy.
W ten mniej więcej sposób robisz zapis nowego pracodawcy wraz z listą pracowników. Oczywiście pisane z palca mam jednak nadzieję że załapiesz idee.
Jeżeli da się to zrobić inaczej lub lepiej to również chętnie się dowiem :)

0

Właśnie chodzi o to, że to co napisałeś to ja już mam - chyba, że gdzieś jest błąd, którego nie zauważam.

              <tr>
                 <td> 
                        <select th:field="*{userByQcname}">
                            <option th:each="u : ${user}" th:value="${u.id}" th:text="${u.name}">Opcja</option>
                        </select> 
                    </td>
                    <td th:if="${#fields.hasErrors('userByQcname')}" th:errors="*{userByQcname}" th:value="*{userByQcname}"></td></tr>
    @RequestMapping(method = RequestMethod.GET, path = "/add")
    public String showNewCardForm(Model model) {
        model.addAttribute("card", new Card());
        model.addAttribute("user", userhelper.getUserNames(1, 2)); 
        return "addnewcard";
    }

     @RequestMapping(method = RequestMethod.POST, path = "/add")
    public String addNewCard(@Valid Card card, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return "addnewcard";
        }
        cardhelper.session.save(card);
        return "showcard";
    }

Cały problem sprowadza się właśnie do zapisania rekordu do bazy, czego nie mogę zrobić. Rozumiem, że po zatwierdzeniu formularza tworzony jest nowy obiekt Pracodawca i wywoływany konstruktor - ale konstruktor musi być wywołany z parametrem typu Pracownik, a dostaje parametr typu String - i dokładnie taki błąd jest zwracany. Pytanie czy muszę dodać jakiś konwerter, czy przegapiłem coś co 'uruchamia automatyczny mechanizm' zamiany Stringa z ID Pracownika na obiekt typu Pracownik i wcale nie powinienem mieć tu błędu?
Kolejnym krokiem byłoby tylko zapisanie do bazy, co powinno być załatwione dzięki prostemu cardhelper.session.save(card);

0

Dzięki.

0

Może i pomoże...ale spędziłem chyba z 6 godzin próbując ten projekt odpalić w Netbeansie i mi się nie udało :/ Ledwie jakieś podstawy Mavena załapałem, a tu jest Gradle więc leżę i kwiczę. Powalczę jeszcze trochę :) jak się nie uda to wrócę, albo se chociaż poprzeglądam w notatniku źródła :)

EDIT:
Ten przykład jest świetny, w zasadzie czegoś takiego szukałem bo coś takiego chcę zrobić, dużo się z niego zapewne nauczę - POLECAM ;). Szkoda, że jest mało komentarzy, ale nie można mieć wszystkiego. Niestety jest też dość mocno skomplikowany (jak dla mnie)...choć i tak dobrze, że zajarzyłem w końcu, że można zamiast wersji 5.0 ściągnąć 1.8 :D

1

Jednak ten przykład nie zawierał dokładnie tego co chciałem, ale jest dużo innych ciekawych rzeczy, z których skorzystałem.
Formularz udało mi się zrobić w taki sposób jaki próbowałem na początku, czyli poprzez dodanie dodatkowego formatera formatterRegistry.addConverter. Mam tylko jeden problem, na wszystkich parametrach ustawiony jest validator, jeżeli dane są nieprawidłowe to zwracamy ten sam formularz oraz błąd - wszystkie pola pojawiają się z danymi, które zostały wpisane, oprócz pola select. Nie byłoby wielkim problemem gdyby było po prostu puste, ale niestety obiekt odpowiedzialny za wypełnianie pól option 'znika' i nie ma możliwości ponownego wyboru. Co robię źle, albo co jeszcze muszę zrobić?

    @RequestMapping(value = "/card/new", method = RequestMethod.GET)
    public String initAddCardForm(Model model) {
        Card card = new Card();
        model.addAttribute("card",card);

        Collection<User> results = userService.getAllUsers();
        model.addAttribute("users", results);
        
        return CARD_FORM_VIEW;
    }

    @RequestMapping(value = "/card/new", method = RequestMethod.POST)
    public String addCard(@Valid Card card, BindingResult result,
                             SessionStatus status, RedirectAttributes attributes) {
        if (result.hasErrors()) {
            return CARD_FORM_VIEW;
        } else {
            CardDTO cardDTO = CardUtils.cardToCardDTO(card);
            Card added = cardService.add(cardDTO);
            logger.info("Added card with information: {}", added);
            status.setComplete();

            addFeedbackMessage(attributes, FEEDBACK_MESSAGE_KEY_CARD_ADDED,
                    added.getCardNr(), added.getArticleName());

            return "redirect:/cards/";
        }
    } 
0

Możesz sobie zdefiniować w kontrolerze metodę oznaczoną annotacją @ModelAttribute zwracjącą kolekcję potrzebną w widoku. Ewentualnie przy zwracaniu widoku po błędzie dodać do modelu tą kolekcję.

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