W tym tutorialu porównamy dwie metody wyciągania danych z bazy danych, przy wykorzystaniu Frameworka Spring JPA a JdbcTemplate . Pierwsza metoda to JdbcTemplate, która upraszcza klasyczne użycie interfejsu JDBC. Druga to Spring Data JPA, które wykorzystuje podejście Object-Relational Mapping (ORM). Skupimy się na samej implementacji, pomijając szczegóły konfiguracji połączenia z serwerem bazy danych.
Aby skorzystać z obu rozwiązań, użyjemy repozytorium Spring Boot. W tym tutorialu porównamy dwie metody wyciągania danych z bazy danych, wykorzystując Framework Spring. Pierwsza metoda to JdbcTemplate, która upraszcza klasyczne użycie interfejsu JDBC. Druga to Spring Data JPA, które stosuje podejście Object-Relational Mapping (ORM). Dla uproszczenia pominiemy konfigurację połączenia z serwerem bazy danych. Sprawdźmy JdbcTemplate a Spring JPA.
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
} Aby zredukować nadmiar kodu, wykorzystujemy adnotacje z biblioteki Lombok do automatycznego generowania getterów, setterów oraz konstruktorów. Dodatkowo, używamy adnotacji @Entity, @Id oraz @GeneratedValue, które są wykorzystywane przez ORM, o czym opowiemy później.
JdbcTemplate to interfejs, który upraszcza wykonywanie zapytań za pomocą klasycznego interfejsu JDBC. Pozwala również uniknąć typowych błędów, które mogą wystąpić przy korzystaniu z czystego JDBC. Dzięki JdbcTemplate nasz kod staje się bardziej zwięzły i przejrzysty.
Za pomocą JdbcTemplate spróbujemy wyciągnąć dane z tabeli Person. W tym celu musimy stworzyć zapytanie SQL oraz klasę mapującą rezultat zapytania na obiekt domenowy. Jeśli mielibyśmy więcej tabel i chcielibyśmy wyciągać dane z każdej z nich, dla każdej tabeli należałoby stworzyć osobną klasę mapującą. W naszym przykładzie stworzymy klasę mapującą dla relacji Person.
private final RowMapper<Person> personRowMapper = (rs, i) -> new Person(
rs.getLong("id"),
rs.getString("first_name"),
rs.getString("last_name")
);
Potrzebujemy również samo zapytanie, które w przykładzie chcemy żeby zwróciło wszystkie osoby z tabeli Person. Zapytanie jest bardzo proste i skonstruowane w SQL-u:
private static final String PG_FIND_ALL_PERSONS = "SELECT id, first_name, last_name FROM person";
Teraz można już stworzyć metodę zwracającą pożądany wynik:
public List<Person> findAll() { return jdbcTemplate.query(PG_FIND_ALL_PERSONS, personRowMapper); }
ORM (Object-Relational Mapping) to technika, która pozwala na odwzorowanie obiektów domenowych na tabele w relacyjnej bazie danych. Najpopularniejszą biblioteką ORM w Javie jest Hibernate, który został wykorzystany w tym tutorialu. Dodatkowo, Spring Data JPA to nakładka na technikę ORM, która znacząco upraszcza dostęp do danych. Główną zaletą tej biblioteki jest dostarczenie podstawowych operacji umożliwiających wyciąganie danych za pomocą interfejsów, które wystarczy rozszerzyć.
W przeciwieństwie do JdbcTemplate, w przypadku ORM wykorzystywane są wcześniej wspomniane adnotacje do mapowania relacji Person. Ponieważ klasę Person stworzyliśmy już wcześniej, teraz potrzebujemy tylko odpowiedniego repozytorium.
@Repository
public interface SprintDataJpaPersonRepository extends JpaRepository<Person, Long> {
}
Interfejs rozszerzamy o JpaRepository, gdzie wskazujemy Encję, której dotyczą nasze zapytania oraz klucz główny. To wystarczy, aby uzyskać gotowe metody do wszystkich operacji CRUD. Przykładem może być metoda List findAll(), która znajduje się w interfejsie JpaRepository. Dzięki temu, aby wyciągnąć wszystkie wpisy z tabeli Person, wystarczy wykorzystać tę gotową metodę. Poniżej znajduje się lista dostępnych metod z JpaRepository:
@NoRepositoryBean public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> { List<T> findAll(); List<T> findAll(Sort var1); List<T> findAllById(Iterable<ID> var1); <S extends T> List<S> saveAll(Iterable<S> var1); void flush(); <S extends T> S saveAndFlush(S var1); void deleteInBatch(Iterable<T> var1); void deleteAllInBatch(); T getOne(ID var1); <S extends T> List<S> findAll(Example<S> var1); <S extends T> List<S> findAll(Example<S> var1, Sort var2); }
Jeśli planujemy manipulować danymi w reprezentacji klas-relacja, zdecydowanie wygodniejszym rozwiązaniem będzie wykorzystanie ORM. Spring Data JPA przyspiesza proces budowania warstwy dostępu do danych, upraszczając wiele operacji. Z kolei, w przypadku gdy musimy stworzyć skomplikowane zapytanie i niekoniecznie potrzebujemy uzyskać reprezentację obiektową, JdbcTemplate może okazać się lepszym i wygodniejszym rozwiązaniem.
Projekt dostępny pod adresem:
$ git clone git clone https://bitbucket.org/ptmsoft/ptm-blog.git