Rozliczenie pojazdu REST, JDBC, MySQL. Zaczynamy drugi projekt z wykorzystaniem wcześniej zbudowanej bazy danych. Jest to prostsza wersja komunikacji z bazą danych wbudowana w Spring Boot. Zaletą jest prostota budowy zapytań, jednak wadą jest fakt, że nie da się jej zastosować do bardziej wymagających zapytań. Jednak w naszym prostym projekcie zupełnie wystarczy. Tak jak w przypadku pierwszego projektu wykorzystamy wzorzec projektowy REST API i stworzymy aplikację pozwalające na komunikację klient – server. Tak samo, jak w artykule Rozliczenie pojazdu REST MyBatis nie będzie to w pełni działająca aplikacja. Pozwoli to porównać, jakie należy wprowadzić zmiany w zależności od zastosowanego sposobu komunikacji z bazą danych. Część front end powstanie w innym artykule.
Rozliczenie pojazdu REST JDBC – Tworzymy projekt
Zacznijmy od stworzenia nowego projektu. Na początku wejdę na stronę https://start.spring.io/, gdzie utworzę nasz projekt. W moim przypadku będzie on wyglądał następująco.

Rozliczenie pojazdu REST JDBC – Budowa projektu
Konfiguracja projektu
W pierwszej kolejności będziemy dodawać odpowiednie biblioteki, dzięki którym w naszym projekcie będzie można korzystać z REST, JDBC oraz MySQL. Przechodzimy do pliku pom.xml i dodajemy do sekcji dependences następujące elementy.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>
Pierwsze dwie sekcje dependencji informuje Spring, że ta aplikacja ma być aplikacją webową, czyli po jej uruchomieniu ma ciągle nasłuchiwać w oczekiwaniu na żądania. Trzecią to sterownik bazy danych MySQL. Czwarta dodaje bibliotekę JDBC. Jak porównamy wcześniejszy artykuł, zmiana jest tylko w ostatnim zapisie.
Kolejnym krokiem jest skonfigurowanie bazy danych. W tym celu edytujemy plik application.property i dodajemy odpowiednie linijki.
spring.datasource.url=jdbc:mysql://localhost:3306/transportMyBatis?serverTimezone=UTC spring.datasource.username=transportMyBatis spring.datasource.password=transportMyBatis logging.file=log/transportMyBatis.log server.port=8091
Jak widać, korzystamy z bazy danych stworzonych przy okazji wcześniejszego artykułu, jednak w tym przypadku nie ma to znaczenia.
Teraz zaczynamy wykorzystywać JDBC.
W przypadku JDBC tworzymy tylko klasę TransportDB w schemacie db. W klasie będziemy przechowywać wszystkie metody wraz z zapytaniami.
package pl.lomza.programowanie.rozliczenie_pojazdu_rest_jdbc.db; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import pl.lomza.programowanie.rozliczenie_pojazdu_rest_jdbc.common.Ride; import java.util.HashMap; import java.util.List; import java.util.Map; public class TransportDB { private final static Logger log= LoggerFactory.getLogger(TransportDB.class); private final NamedParameterJdbcTemplate template; private final BeanPropertyRowMapper<Ride> rideMapper = BeanPropertyRowMapper.newInstance(Ride.class); public TransportDB(NamedParameterJdbcTemplate template) { this.template = template; } public List<Ride> getRides(long vehicleId) { log.info("List rides"); return template.query( "SELECT r.id, r.rideDate, r.counterBefore, r.counterAfter, r.km, " + "r.whence, r.`where`, r.rideKind, r.fuelCondition, r.fuelAdd, r.fuelAfter, " + "SUM(s.fuel) as fuel, f.price, SUM(s.fuel*f.price) as currency, r.fuelNorm " + "FROM transportMyBatis.Ride r " + "LEFT JOIN transportMyBatis.Settlement s on r.id = s.idRide " + "LEFT JOIN transportMyBatis.Refuel f on f.id = s.idRefuel " + "WHERE r.idVehicle = :vehicleId "+ "GROUP BY r.id, r.whence, r.`where`, r.rideKind, f.price " + "ORDER BY r.id ", new MapSqlParameterSource("vehicleId", vehicleId), rideMapper); } public Ride getRide(long vehicleId, long rideId) { log.info("Object ride"); Map<String, String> map = new HashMap<>(); map.put("vehicleId", Long.toString(vehicleId)); map.put("rideId", Long.toString(rideId)); return template.queryForObject( "SELECT r.id, r.rideDate, r.counterBefore, r.counterAfter, r.km, " + "r.whence, r.`where`, r.rideKind, r.fuelCondition, r.fuelAdd, r.fuelAfter, " + "SUM(s.fuel) as fuel, f.price, SUM(s.fuel*f.price) as currency, r.fuelNorm " + "FROM transportMyBatis.Ride r " + "LEFT JOIN transportMyBatis.Settlement s on r.id = s.idRide " + "LEFT JOIN transportMyBatis.Refuel f on f.id = s.idRefuel " + "WHERE r.idVehicle = :vehicleId and r.id = :rideId " + "GROUP BY r.id, r.whence, r.`where`, r.rideKind, f.price " + "ORDER BY r.id", map, rideMapper); } }
Uwaga: W razie problemów z bazą danych należy sprawdzić w każdym zapytaniu, czy na końcu linii jest pusty znak. Jeżeli go zabraknie, zapytanie się nie wykona.
To wystarczy do komunikacji z bazą danych. Teraz musimy podłączyć to do naszego kontrolera. Robimy to w następujący sposób:
package pl.lomza.programowanie.rozliczenie_pojazdu_rest_jdbc.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import pl.lomza.programowanie.rozliczenie_pojazdu_rest_jdbc.common.Ride; import pl.lomza.programowanie.rozliczenie_pojazdu_rest_jdbc.db.TransportDB; import java.util.List; @RestController public class TransportController { private final static Logger log = LoggerFactory.getLogger(TransportController.class); private final TransportDB transportDB; public TransportController(TransportDB transportDB) { this.transportDB = transportDB; } @GetMapping("/vehicle/{vehicleId}/rides") public List<Ride> getRides(@PathVariable("vehicleId") long vehicleId) { log.info("Wyświetlamy listę przejazdów"); return transportDB.getRides(vehicleId); } @GetMapping("vehicle/{vehicleId}/ride/{rideId}") public Ride getRide(@PathVariable("vehicleId") long vehicleId, @PathVariable("rideId") long rideId) { log.info("Wyświetlamy przejazd"); return transportDB.getRide(vehicleId, rideId); } }
Jednak jest tu w porównaniu do artykułu z Rozliczenie pojazdu REST MyBatis mała zmiana. Wcześniej przy czytywaniu interfejsu TransportDAO używaliśmy @Autowired. Tym razem jednak robimy to przez konstruktor. Obie metody są dobre, ale ogólnie preferowana jest metoda przez konstruktor. Jest to związane przede wszystkim z testowaniem aplikacji. Dlatego teraz właśnie takie podejście zaprezentowałem.
Pokażemy jeszcze, jak wygląda klasa Ride:
public class Ride { private long id; private LocalDate rideDate; private int counterBefore; private int counterAfter; private int km; private String whence; private String where; private String rideKind; private double fuelCondition; private double fuelAdd; private double fuelAfter; private Double fuel; private Double price; private Double currency; private double fuelNorm; public long getId() { return id; } public void setId(long id) { this.id = id; } public LocalDate getRideDate() { return rideDate; } public void setRideDate(LocalDate rideDate) { this.rideDate = rideDate; } public int getCounterBefore() { return counterBefore; } public void setCounterBefore(int counterBefore) { this.counterBefore = counterBefore; } public int getCounterAfter() { return counterAfter; } public void setCounterAfter(int counterAfter) { this.counterAfter = counterAfter; } public int getKm() { return km; } public void setKm(int km) { this.km = km; } public String getWhence() { return whence; } public void setWhence(String whence) { this.whence = whence; } public String getWhere() { return where; } public void setWhere(String where) { this.where = where; } public String getRideKind() { return rideKind; } public void setRideKind(String rideKind) { this.rideKind = rideKind; } public double getFuelCondition() { return fuelCondition; } public void setFuelCondition(double fuelCondition) { this.fuelCondition = fuelCondition; } public double getFuelAdd() { return fuelAdd; } public void setFuelAdd(double fuelAdd) { this.fuelAdd = fuelAdd; } public double getFuelAfter() { return fuelAfter; } public void setFuelAfter(double fuelAfter) { this.fuelAfter = fuelAfter; } public Double getFuel() { return fuel; } public void setFuel(Double fuel) { this.fuel = fuel; } public Double getPrice() { return price; } public void setPrice(Double price) { this.price = price; } public Double getCurrency() { return currency; } public void setCurrency(Double currency) { this.currency = currency; } public double getFuelNorm() { return fuelNorm; } public void setFuelNorm(double fuelNorm) { this.fuelNorm = fuelNorm; } }
Jeszcze jeden element od pokazania mianowicie główny plik projektu trochę się zmienił, więc go prezentujemy.
package pl.lomza.programowanie.rozliczenie_pojazdu_rest_jdbc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import pl.lomza.programowanie.rozliczenie_pojazdu_rest_jdbc.db.TransportDB; import javax.sql.DataSource; @SpringBootApplication public class RozliczeniePojazduRestJdbcApplication { public static void main(String[] args) { SpringApplication.run(RozliczeniePojazduRestJdbcApplication.class, args); } @Bean public TransportDB transportDB(NamedParameterJdbcTemplate template){ return new TransportDB(template); } @Bean public PlatformTransactionManager transactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } }
Tak wygląda cała aplikacja z wykorzystaniem JDBC. Pozostało nam wszystko przetestować i powinno działać. Jeżeli jednak nie działa, warto podejrzeć, jak ja to zrobiłem. Zapraszam na mojego GitHub pod adresem:
https://github.com/grzegorzkossa/rozliczenie_pojazdu_rest_jdbc
To jednak nie koniec. W następnym artykule pokaże jak te samo zadanie wykonać przy wykorzystaniu JPA. Zapraszam do kolejnego artykułu.