Spring, kilka pytań odnośnie pierwszej apki crud

0

Cześć. Piszę sobie pierwszą apkę w Springboocie (prosty crud). I zastanawiają mnie dwie rzeczy:

  1. Jeśli w przyszłości planuję stworzyć np. formularze rejestracji czy dodawania rekordów do bazy danych(front) to jak zrobić metodę, która powiedzmy na 5 parametrów wymaga tylko dwóch i 3 są opcjonalne jeżeli poda je użytkownik? Jak w takim przypadku tworzyć obiekt klasy i go dodawać do bazy? Konstruktorem czy setterami?

  2. Co powinny zwracać metody? String, void? Może ResponseEntity?

0
  1. Nie rozumiem pytania. Przecież Spring sam zmapuje formularz do obiektu DTO który dostaniesz w kontrolerze. Chyba że pytasz o przepisywanie tych danych do Entity? Wtedy jak wolisz, chociaż przy parametrach opcjonalnych lepiej Builder albo settery.
  2. Mówisz o metodach kontrolera? Te które mapują URLe nie bardzo mogą void ;) Ja bym powiedział że:
  • String jeśli zwracasz prosty widok bez zadnych danych
  • ModelAndView jeśli zwracasz dane do widoku
  • Obiekty mapowanie do JSONa jeśli to RestController
  • ResponseEntity jeśli chcesz ustawić sobie jakiś kod błędu (403, 404, 500 czy cos).
0
Shalom napisał(a):
  1. Nie rozumiem pytania. Przecież Spring sam zmapuje formularz do obiektu DTO który dostaniesz w kontrolerze. Chyba że pytasz o przepisywanie tych danych do Entity? Wtedy jak wolisz, chociaż przy parametrach opcjonalnych lepiej Builder albo settery.
  2. Mówisz o metodach kontrolera? Te które mapują URLe nie bardzo mogą void ;) Ja bym powiedział że:
  • String jeśli zwracasz prosty widok bez zadnych danych
  • ModelAndView jeśli zwracasz dane do widoku
  • Obiekty mapowanie do JSONa jeśli to RestController
  • ResponseEntity jeśli chcesz ustawić sobie jakiś kod błędu (403, 404, 500 czy cos).
  1. Chodzi mi o to, że np. jeśli mam metodę create, która przyjmuje te parametry opcjonalne to wygląda to tak:
    create?name=n&type=t i create?name=n&type=t&price=500.
@RequestMapping("/create")
    public String create(
            @RequestParam(value = "name") String name,
            @RequestParam(value = "type") String type,
            @RequestParam(value = "price", required = false) Integer price){

        Product p = new Product();
        p.setName(name);
        p.setType(type);
        p.setPrice(price);
        productRepository.save(b);
        return "Saved";
    }

Jeśli parametr price nie zostanie podany to lepiej żeby przyjął null czy np. 0? Jakie rozwiązanie jest lepsze do stworzenia warstwy View?
Tutaj nie sprawdzam czy price jest null, więc otrzymam NPE jeśli się go nie poda.
Jeśli tych parametrów będzie więcej to jak je sprawdzać? Ify czy są nullami?

0
  1. Używaj POST a nie GET do formularzy i mapuj to od razu na jakieś DTO a nie oderwane zmienne.
  2. Użyj buildera w takim razie albo setterów. Nie będzie żadnego NPE bo niby gdzie? Ustawienie pola na null nie powoduje NPE.
0
Shalom napisał(a):
  1. Używaj POST a nie GET do formularzy i mapuj to od razu na jakieś DTO a nie oderwane zmienne.
  2. Użyj buildera w takim razie albo setterów. Nie będzie żadnego NPE bo niby gdzie? Ustawienie pola na null nie powoduje NPE.
  1. Zmieniłem na @RequestMapping(value = "/create", method = RequestMethod.POST). W jaki sposób mapować na DTO? Możesz coś więcej powiedzieć?
  2. Racja, po zmianie na POST już mi nie wyrzuca NPE
0
Shalom napisał(a):

Mapowanie do DTO masz np. tutaj: https://github.com/Pharisaeus/SpringScaffoldApplication/blob/master/src/main/java/scaffold/controllers/main/MainController.java plus https://github.com/Pharisaeus/SpringScaffoldApplication/blob/master/src/main/webapp/WEB-INF/pages/main.ftl wiele do tego nie potrzeba, @ModelAttribute w kontrolerze i bindowanie po stronie widoku.

Próbowałem zrobić analogicznie(kontroller jest mapowany na /books):

 @RequestMapping(value = "/book_list.html", method = RequestMethod.GET)
    public ModelAndView getBooksListView(){
        ModelAndView mav = new ModelAndView("book_list");
        Iterable<Book> allData = bookRepository.findAll();
        mav.addObject("books", allData);
        return mav;
    }

jednak po wpisaniu adresu http://localhost:8080/books/book_list.html dostaje bad request 400, z komunikatem:
Failed to convert value of type [java.lang.String] to required type [long]; nested exception is java.lang.NumberFormatException: For input string: "book_list"

Rekordy w bazie są, tak wygląda encja Book:

@Entity
@Table(name = "books")
public class Book {


    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String author;
    private String tittle;
    private String ISBN;
    private String language;

Przeszukałem naście tematów z takim błędem i dalej nie wiem co jest źle. Pliku z widokiem .jsp nie wrzucam, bo nie zależnie od tego co w nim jest, jest błąd. Prosze ratuj :)

0

Myślę że powinieneś najpierw poczytać choćby to:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html
:)

0

@elmohamati ale gdzie dokładnie dostajesz taki wyjątek? Puść debugger i zobacz co popsułeś ;]

0
Shalom napisał(a):

@elmohamati ale gdzie dokładnie dostajesz taki wyjątek? Puść debugger i zobacz co popsułeś ;]

Ustawiłem sobie breakpointy(dopóki debugger był w tej funkcji to w miarę ogarniałem co się dzieje, ale jak już przeszło w warstwy springa...) w tej metodzie i raczej wygląda to w porządku, tworzy model i dodaje do niego dane(20 rekordów z bazy).

Dokładny log:
WARN 3440 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Failed to bind request element: org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type [java.lang.String] to required type [long]; nested exception is java.lang.NumberFormatException: For input string: "book_list"

propertiesy:
spring.view.prefix=/WEB-INF/jsp/
spring.view.suffix=.jsp

widok book_list.jsp znajduje się w katalogu src/main/WEB-INF/jsp

0

Daj jakis link do repo bo bez szklanej kuli będzie ciężko :)

0
Shalom napisał(a):

Daj jakis link do repo bo bez szklanej kuli będzie ciężko :)

https://github.com/credentialinfo/demo2

2

Ok już na pierwszy rzut oka:

  • Zrobiłeś RestController a chcesz zwracać z niego ModelAndView a tak nie wolno. RestController zwraca JSONy! Moze to miał być zwykły Controller? Albo potrzebujesz dwa?
  • ComponentScan to masz mieć w klasie startowej boota a nie w kontrolerze... Dziwie sie że w ogóle to ci zadziałało
  • Zaczynam wątpić ze to w ogóle ci działało. Przecież tego kontrolera to tam nic chyba nie wykrywa bo nie jest w component scan w ogóle o_O
  • Nie masz w ogóle dependency do spring-boot-starter-freemarker i te widoki w ogóle ci sie nie konfigurują (bo zakładam że jednak chciałeś ftl?)
  • WEB-INF powinno być w katalogu webapp

Po tych fixach coś tam zaczęło działać ;)

  1. Propertisy:
spring.freemarker.template-loader-path=WEB-INF/jsp
spring.freemarker.suffix=.jsp
  1. WEB-INF ląduje w katalogu /src/main/webapp
  2. Oddzielasz te RESTowe mappingi do osobnego kontrolera a to co ma zwracać ModelAndView dajesz do zwykłego @Controller
  3. W klasie boota dajesz
@SpringBootApplication
@Configuration
@ComponentScan("com.example2")
  1. Dodajesz dependency
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
  1. Zmieniasz to JSTL na FTL, chyba ze bardzo chcesz JSTL ale wtedy musisz odpowiednio je skonfigurować znowu.

Tyle na początek ;]

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