Spring JDBCTemplate Oracle

0

Cześć,

Chce pobrać dane z bazy i zapisać je do listy. Następnie przekazać listę do widoku.
Pierwszą część robię tak:

  public List<Company> getAllCompanies() {

        String getQueryCompanies = "SELECT * FROM Company";
        List<Company> listCompanies = jdbcTemplate.query(getQueryCompanies, BeanPropertyRowMapper.newInstance(Company.class));
        System.out.println(listCompanies);
        return listCompanies;
    }
}

Efekt jest taki, że w widoku rysuje mi się pusta tabela z ilością pozycji odpowiadających ilości pozycji w bazie danych.
Gdy wstawiłem System.out.println() zaraz po zapełnieniu listy, otrzymuję listę wypełnioną w całości null`ami.

[Company{name='null', city='null', typeOfActivity='null'}, Company{name='null', city='null', typeOfActivity='null'}, Company{name='null', city='null', typeOfActivity='null'}, Company{name='null', city='null', typeOfActivity='null'}, Company{name='null', city='null', typeOfActivity='null'}

Co może być tego powodem? Konsola nie daje żadnych błędów....

4

Moja rada: zamiast jakiegoś rakulca w stylu BeanPropertyRowMapper to weź po ludzku sam to zmapuj własnym row mapperem i nie będziesz się musiał zastanawiać nad tym co się stało w jakimś magicznym kodzie którego nie widzisz. Może np. nazwy kolumn w bazie nie są takie same jak nazwy twoich pól w klasie Company, albo nie masz w tej klasie bezargumentowego kontruktora i setterów dla wszystkich pól? Bez tego ten twój magiczny mapper nie zadziała.

0

Rakulca :D ?
Aż sprawdziłem i nie ma w słowniku tego słowa :)

Wracając do tematu...
Napisałem własnego row mappera i wszystko działa elegancko - dzięki za podpowiedź.
Rozwiązanie dobre ale sporo nowego kodu powstało... Nowa klasa CompanyRowMapper oraz kilka metod w klasie Company ustawiających mapowanie.
Rozumiem że najczęściej pisze się właśnie takie własne klasy mapujące?

2

Rozwiązanie dobre ale sporo nowego kodu powstało... Nowa klasa CompanyRowMapper oraz kilka metod w klasie Company ustawiających mapowanie.

Nie przesadziłeś trochę? :D

jdbcTemplate.query(getQueryCompanies, (rs,row)->new Company(rs.getString("name"), rs.getString("city"), rs.getString("typeOfActivity")));

Coś takiego nie wystarczy?

Rozumiem że najczęściej pisze się właśnie takie własne klasy mapujące?

Lepsze to niż jakieś magiczne klasy które polegają na tym że jakieś stringi do siebie pasują, albo że masz settery i pusty konstruktor. Dzięki temu możesz mieć normalny konstruktor albo buildera i w ogóle możesz też przemapować wynik na obiekt który nie odpowiada 1:1 strukturze wyników z zapytania :)
Wyobraź sobie że klasa Company ma w sobie pole Address które składa się z kilku innych pól.

0
Cannot resolve method 'query(java.lang.String, <lambda expression>)'

W Company mam właśnie zrobionego Buildera więc ta lambda którą napisałeś nie zadziała ...

0

No to jakieś:

        jdbcTemplate.query(getQueryCompanies,
                (rs, row) -> Company.builder()
                        .withName(rs.getString("name"))
                        .withCity(rs.getString("city"))
                        .withType(rs.getString("typeOfActivity"))
                        .build()
        );

To akurat szczegół. Ale oczywiście dla skomplikowanego mappingu można zrobić sobie osobną klasę.

0

Ok, chciałbym jednak sprawdzić jak można to zrobić krócej dla mappingów mało skomplikowanych.
To teraz mam tak:

        jdbcTemplate.query(getQueryCompanies,
                (rs, row) -> Company.CompanyBuilder.build()
                        .withName(rs.getString("name"))
                        .withCity(rs.getString("city"))
                        .withType(rs.getString("typeOfActivity"))
                        .build()
        );

ale IDE czepia się że metoda build() musi być statyczna a nie może bo:

        public Company build() {
            return new Company(name, city, typeOfActivity);
        }

Gdybym nie miał budowniczego, to owszem Twój pierwszy przykład byłby dobry. A tak to w klasie Company mam konstruktor prywatny i niestety to nie dziala.
Szukałem innej metody klasy RowMapper, która byłaby odpowiednia dla budowniczego ale nie znalazłem.

1

Eee coś ten twój builder jakiś dziwny bo robisz Company.CompanyBuilder.build() a potem na koniec build co nie bardzo ma sens. To pierwsze wywołanie to musi być statyczna metoda która zwraca obiekt Buildera, a to drugie to faktyczne budowanie Company, a u ciebie chyba masz tylko jedną metodę build :D
Ta pierwsza powinna się nazywać raczej jakoś builder() i ja bym ją dał w ogóle do klasy Company:

class Company{

    public static CompanyBuilder builder(){
        return new CompanyBuilder();
    }
}

I wtedy Company.builder().with....build()

0

To moja okrojona klasa Company z budowniczym

public class Company {

    public Long id_Company;
    public String name;
    public String city;
    public String typeOfActivity;

    public Company() {
    }

    private Company(String name, String city, String typeOfActivity) {
        this.name = name;
        this.city = city;
        this.typeOfActivity = typeOfActivity;
    }

    public static class CompanyBuilder {
        private String name;
        private String city;
        private String typeOfActivity;

        public CompanyBuilder setName(String name) {
            this.name = name;
            return this;
        }

        public CompanyBuilder setCity(String city) {
            this.city = city;
            return this;
        }

        public CompanyBuilder setTypeOfActivity(String typeOfActivity) {
            this.typeOfActivity = typeOfActivity;
            return this;
        }

        public Company build() {
            return new Company(name, city, typeOfActivity);
        }

    }

1

No to przecież to co napisałeś wyżej -> Company.CompanyBuilder.build().withName(rs.getString("name"))... w ogóle nie ma sensu. Gdzieś musisz mieć new CompanyBuilder a u ciebie w kodzie jakoś tego nie widać ;) Zrób tak jak napisałem wyżej z metodą builder w klasie Company.

0

Aj tamto co popełniłem było faktycznie jakieś od czapy.

Teraz śmiga. Użyłem seterów z klasy buildera ale to chyba nie zbrodnia.

       listCompanies = jdbcTemplate.query(getQueryCompanies,
                (rs, row) -> Company.builder()
                        .setName(rs.getString("NAME"))
                        .setCity(rs.getString("CITY"))
                        .setTypeOfActivity(rs.getString("TYPE_OF_ACTIVITY"))
                        .build()
        );

dzięki :)

Zastanawiam się jednak czy to dobre rozwiązanie. Do tej pory klasa Company była hermetyczna. A teraz poprzez metodę builder() już taka nie jest i budowniczy traci trochę na wartości.

1

Co do buildera - zawsze możesz użyć lomboka i adnotacji @Builder ;) nie ma co pisac boilerplate kodu, chyba że w ramach nauki

0

@Shalom napisałeś że JPA to rak i żeby używać jdbcTemplate.

Ale w jaki sposób bez JPA odzwierciedlisz w kodzie relacje OneToMany, tabele pośrednie itp?

0
anckor napisał(a):

@Shalom napisałeś że JPA to rak i żeby używać jdbcTemplate.

Ale w jaki sposób bez JPA odzwierciedlisz w kodzie relacje OneToMany, tabele pośrednie itp?

A umiesz SQLa? Wiesz że przed powstaniem JPA oraz Hibernate ludzie też używali relacyjnych baz danych?

3

@anckor robisz sobie żarty, prawda? :D A jak niby te twoje magiczne relacje są odzwierciedlone w SQLowej bazie? Bo rozumiesz chyba ze tam nie ma żadnych czarów, tylko te dane lecą ładnie do tabelek?

Zresztą jak człowiek wyłącza te automaty i zaczyna myśleć nad tym co robi to nagle takie problemy w ogóle znikają, bo okazuje się że nie trzeba wszystkiego trzymać w bazie ;) Mam wrażenie ze z JPA jest trochę jak ze springowym @Autowire - ludzie wyłączają myślenie bo te narzędzia pozwalają "łatwo" zrobić coś bardzo głupiego. I nagle masz klasę która ma 30 takich @Autowire albo jakieś @Entity z dziesiatkami pól i zależności n:m:k. Gdyby nie automat, to już przy trzecim czy czwartym argumencie konstruktora, albo przy drugiej tabeli zacząłbyś myśleć czy w ogóle dobrze się do tego zabierasz.

Your scientists were so preoccupied with whether or not they could, they didn't stop to think if they should

Ayway, JPA się sprawdza w tym do czego zostało zaprojektowane -> do CRUDa, gdzie faktycznie ktoś potrzebuje zrobić w swojej aplikacji nakładkę na bazę danych. Jak robisz normalny projekt z biznesowymi repozytoriami, to JPA nijak się nie przydaje do niczego.

żeby używać jdbcTemplate

Nie mówie że koniecznie jdbc template, jest wiele innych rozwiązań które można wykorzystać, to jest po prostu jedno z nich jeśli ktoś już i tak ma springa w projekcie.

2

@Shalom: nie przesadzajmy, oczywiście powiązania w JPA są nadmiarowe dosyc często, ale jednak czasami potrzebujesz z nich korzystać i te JdbcTemplate może być nieco upierdliwe. @anckor może warto rozważyc JOOQ :)

0

Z tego co czytam różne materiały w necie, to w większości pracy ze springiem używa się JPA. Nawet na kursach programistycznych głównie na niego stawiają, a nie na JDBCTemplate. Dlaczego?

0
Dazon napisał(a):

Z tego co czytam różne materiały w necie, to w większości pracy ze springiem używa się JPA. Nawet na kursach programistycznych głównie na niego stawiają, a nie na JDBCTemplate. Dlaczego?

Bo można szybciej wyklepać aplikację. Ale nawet jak używasz JPA to biznes potrafi wymyślić takie widoki i raporty że prościej wyklepać to w JDBCTemplate, JDBI lub JOOQ

1

Głównym problemem JPA są niuanse i detale ukryte pod magicznymi adnotacjami, i ludzie pewnie nawet nie wiedzą jak maja zrypany performance przez to.

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