5. Wprowadzenie do Spring Data JPA
W tym rozdziale omówimy następującą architekturę:
![]() |
Pomiędzy warstwą [DAO] a sterownikiem JDBC w SGBD wstawiamy warstwę [JPA] (Java Persistence API). Obecnie to warstwa JPA wysyła polecenia SQL przeznaczone dla SGBD. Warstwa [DAO] nie przetwarza już poleceń SQL, a jedynie obiekty zwane encjami JPA, które są obrazami różnych tabel eksploatowanej bazy danych. Pola tych encji są jednoznacznie powiązane z kolumnami tabel za pomocą adnotacji Java. To właśnie pozwala warstwie JPA na przekształcenie w SQL operacji warstwy [DAO] wykonywanych na encjach JPA.
Spring Data to gałąź biblioteki Spring zajmująca się dostępem do danych, niezależnie od tego, czy są one przechowywane w relacyjnej bazie danych SGBDR, bazie NOSQL czy w innych rodzajach repozytoriów. W tym miejscu skupiamy się wyłącznie na SGBDR i dostępie do nich za pośrednictwem JPA. W dalszej części tekstu będziemy czasami używać skrótu [Spring JPA], aby w rzeczywistości odnieść się do [Spring Data JPA]. W powyższej architekturze warstwa [Spring Data] zapewnia warstwie [DAO] narzędzia do zarządzania encjami JPA.
JPA jest w rzeczywistości specyfikacją. Przetestujemy trzy jej implementacje:
- Hibernate (http://hibernate.org/);
- EclipseLink (http://www.eclipse.org/eclipselink/);
- OpenJpa (http://openjpa.apache.org/);
5.1. Exemple-01
Na stronie Springa znajduje się wiele samouczków dotyczących rozpoczęcia pracy ze Springiem [http://spring.io/guides]. Wykorzystamy jeden z nich, aby zapoznać się z Spring Data. W tym celu użyjemy Spring Tool Suite (STS).
![]() |
- w [1] importujemy jeden z samouczków z [spring.io/guides];
![]() |
- w [2] wybieramy samouczek [Accessing Data Jpa], który pokazuje, jak uzyskać dostęp do bazy danych za pomocą Spring Data;
- w [3] wybieramy projekt skonfigurowany przez Maven;
- w [4] samouczek może być udostępniony w dwóch formach: [initial], czyli pusta wersja, którą wypełnia się zgodnie z samouczkiem, lub [complete], czyli ostateczna wersja samouczka. Wybieramy tę drugą;
- w pliku [5] można wybrać opcję wyświetlenia samouczka w przeglądarce;
- w pliku [6] znajduje się projekt końcowy.
5.1.1. Konfiguracja projektu w Maven
Zależności Maven projektu są skonfigurowane w pliku [pom.xml]:
<groupId>org.springframework</groupId>
<artifactId>gs-accessing-data-jpa</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
</dependencies>
<properties>
<!-- użyj UTF-8 do wszystkiego -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<start-class>hello.Application</start-class>
</properties>
- wiersze 5–9: definiują nadrzędny projekt Maven. To właśnie on określa większość zależności projektu. Mogą one być wystarczające, w którym to przypadku nie dodaje się żadnych nowych, lub nie, w którym to przypadku dodaje się brakujące zależności;
- wiersze 12–15: definiują zależność od [spring-boot-starter-data-jpa]. Ten artefakt zawiera klasy Spring Data;
- wiersze 16–19: definiują zależność od artefaktu SGBD oraz H2, który umożliwia tworzenie i zarządzanie bazami danych w pamięci.
Przyjrzyjmy się klasom dostarczanym przez te zależności:
![]() | ![]() | ![]() |
Jest ich bardzo wiele:
- niektóre należą do ekosystemu Spring (te zaczynające się od „spring”);
- inne należą do ekosystemu Hibernate (hibernate, jboss), z którego implementacją JPA korzystamy tutaj;
- inne to biblioteki testowe (junit, hamcrest);
- inne to biblioteki logowania (log4j, logback, slf4j);
Zachowamy je wszystkie. W przypadku aplikacji produkcyjnej należałoby zachować tylko te, które są niezbędne.
W wierszu 26 pliku [pom.xml] znajduje się następujący wiersz:
<start-class>hello.Application</start-class>
Ten wiersz jest powiązany z następującymi wierszami:
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
W wierszach 6–9 wtyczka [spring-boot-maven-plugin] umożliwia wygenerowanie pliku jar zawierającego kod wykonywalny aplikacji. Wiersz 26 pliku [pom.xml] określa zatem klasę wykonywalną tego pliku jar.
5.1.2. Warstwa [JPA]
Dostęp do bazy danych odbywa się za pośrednictwem warstwy [JPA], Java Persistence API:
![]() |
![]() |
Aplikacja jest prosta i obsługuje klientów [Customer]. Klasa [Customer] należy do warstwy [JPA] i ma następującą postać:
package hello;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String firstName;
private String lastName;
protected Customer() {
}
public Customer(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public String toString() {
return String.format("Customer[id=%d, firstName='%s', lastName='%s']", id, firstName, lastName);
}
}
Klient ma identyfikator [id], imię [firstName] i nazwisko [lastName]. Każda instancja [Customer] reprezentuje wiersz w tabeli bazy danych.
- wiersz 8: adnotacja JPA, która powoduje, że trwałość instancji [Customer] (tworzenie, odczyt, aktualizacja, usunięcie) będzie zarządzana przez implementację JPA. Z zależności Maven wynika, że wykorzystywana jest implementacja JPA / Hibernate;
- wiersze 11–12: adnotacje JPA, które powiązują pole [id] z kluczem głównym tabeli [Customer]. Wiersz 12 wskazuje, że implementacja JPA będzie korzystać z metody generowania klucza głównego właściwej dla używanego SGBD, w tym przypadku H2;
Nie ma innych adnotacji dotyczących JPA. W takim przypadku zostaną zastosowane wartości domyślne:
- tabela o nazwie [Customer] będzie nosiła nazwę klasy, tj. [Customer];
- kolumny tej tabeli będą nosiły nazwy pól klasy: [id, firstName, lastName], przy czym w nazwie kolumny tabeli nie jest brana pod uwagę wielkość liter;
Należy zauważyć, że w żadnym momencie nie podano nazwy używanej implementacji JPA.
5.1.3. Warstwa [Spring Data]
Klasa [CustomerRepository] implementuje warstwę dostępu do tabeli [Customer]. Jej kod wygląda następująco:
![]() |
![]() |
package hello;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
public interface CustomerRepository extends CrudRepository<Customer, Long> {
List<Customer> findByLastName(String lastName);
}
Jest to zatem interfejs, a nie klasa (wiersz 7). Rozszerza on interfejs [CrudRepository], interfejs Spring Data (wiersz 5). Interfejs ten jest definiowany przez dwa typy: pierwszy to typ zarządzanych elementów, w tym przypadku typ [Customer], a drugi to typ klucza głównego zarządzanych elementów, w tym przypadku typ [Long]. Interfejs [CrudRepository] wygląda następująco:
package org.springframework.data.repository;
import java.io.Serializable;
@NoRepositoryBean
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
<S extends T> S save(S entity);
<S extends T> Iterable<S> save(Iterable<S> entities);
T findOne(ID id);
boolean exists(ID id);
Iterable<T> findAll();
Iterable<T> findAll(Iterable<ID> ids);
long count();
void delete(ID id);
void delete(T entity);
void delete(Iterable<? extends T> entities);
void deleteAll();
}
Interfejs ten definiuje operacje CRUD (Create – Read – Update – Delete), które można wykonać na typie JPA T:
- wiersz 8: metoda save pozwala na zapisanie encji T w bazie danych. Zapisuje ona encję przy użyciu klucza głównego nadanego jej przez SGBD. Pozwala również na aktualizację encji T identyfikowanej za pomocą jej klucza głównego id. Wybór jednej lub drugiej operacji zależy od wartości klucza głównego id: jeśli ma on wartość null, wykonywana jest operacja zapisania, w przeciwnym razie – operacja aktualizacji;
- wiersz 10: to samo, ale dla listy encji;
- wiersz 12: metoda findOne pozwala odzyskać encję T identyfikowaną przez jej klucz pierwotny id;
- wiersz 22: metoda delete pozwala usunąć encję T identyfikowaną przez jej klucz główny id;
- wiersze 24–28: warianty metody [delete];
- wiersz 16: metoda [findAll] pozwala odzyskać wszystkie zapisane encje T;
- wiersz 18: to samo, ale ograniczone do encji, dla których przekazano listę identyfikatorów;
Wróćmy do interfejsu [CustomerRepository]:
package hello;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
public interface CustomerRepository extends CrudRepository<Customer, Long> {
List<Customer> findByLastName(String lastName);
}
- w wierszu 9 można odnaleźć obiekt [Customer] na podstawie jego nazwy [lastName];
I to wszystko, jeśli chodzi o warstwę [DAO]. Nie ma klasy implementującej poprzedni interfejs. Jest ona generowana w czasie wykonywania przez [Spring Data]. Metody interfejsu [CrudRepository] są implementowane automatycznie. W przypadku metod dodanych do interfejsu [CustomerRepository] sytuacja jest różna. Wróćmy do definicji [Customer]:
private long id;
private String firstName;
private String lastName;
Metoda w wierszu 9 jest automatycznie implementowana przez [Spring Data], ponieważ odwołuje się do pola [lastName] (wiersz 3) z [Customer]. Gdy Spring Data napotka metodę [findBySomething] w interfejsie do zaimplementowania, implementuje ją za pomocą następującego zapytania JPQL (Java Persistence Query Language):
Typ T musi zatem posiadać pole o nazwie [something]. W ten sposób metoda
zostanie zaimplementowana za pomocą kodu podobnego do poniższego:
return [em].createQuery("select c from Customer c where c.lastName=:value").setParameter("value",lastName).getResultList()
gdzie [em] oznacza kontekst trwałości JPA. Jest to możliwe tylko wtedy, gdy klasa [Customer] posiada pole o nazwie [lastName], co ma miejsce.
Podsumowując, w prostych przypadkach Spring Data pozwala nam zaimplementować warstwę [DAO] za pomocą prostego interfejsu.
5.1.4. Warstwa [console]
![]() |
![]() |
Klasa [Application] wygląda następująco:
package hello;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application implements CommandLineRunner {
@Autowired
CustomerRepository repository;
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
@Override
public void run(String... strings) throws Exception {
// zapisz kilku klientów
repository.save(new Customer("Jack", "Bauer"));
repository.save(new Customer("Chloe", "O'Brian"));
repository.save(new Customer("Kim", "Bauer"));
repository.save(new Customer("David", "Palmer"));
repository.save(new Customer("Michelle", "Dessler"));
// pobierz wszystkich klientów
System.out.println("Customers found with findAll():");
System.out.println("-------------------------------");
for (Customer customer : repository.findAll()) {
System.out.println(customer);
}
System.out.println();
// pobierz pojedynczego klienta za pomocą ID
Customer customer = repository.findOne(1L);
System.out.println("Customer found with findOne(1L):");
System.out.println("--------------------------------");
System.out.println(customer);
System.out.println();
// pobieranie klientów według nazwiska
System.out.println("Customer found with findByLastName('Bauer'):");
System.out.println("--------------------------------------------");
for (Customer bauer : repository.findByLastName("Bauer")) {
System.out.println(bauer);
}
}
}
- wiersz 9: klasa implementuje interfejs [CommandLineRunner], który jest interfejsem [Spring Boot] (wiersz 4). Interfejs ten posiada tylko jedną metodę, znajdującą się w wierszu 19;
- wiersz 8: @SpringBootApplication to adnotacja łącząca kilka adnotacji [Spring Boot]:
- @Configuration: wskazuje, że klasa jest klasą konfiguracyjną;
- @EnableAutoConfiguration: nakazuje [Spring Boot] samodzielne utworzenie określonej liczby beanów w oparciu o różne właściwości, w szczególności zawartość ścieżki Classpath projektu. Ponieważ biblioteki Hibernate znajdują się w ścieżce Classpath, bean [entityManagerFactory] zostanie zaimplementowany przy użyciu Hibernate. Ponieważ biblioteka SGBD H2 znajduje się w ścieżce Classpath, bean [dataSource] zostanie zaimplementowany przy użyciu H2. W beanie [dataSource] należy również zdefiniować użytkownika i jego hasło. W tym przypadku Spring Boot użyje domyślnego administratora z H2, który nie ma hasła. Ponieważ biblioteka [spring-tx] znajduje się w ścieżce Classpath, zostanie użyty menedżer transakcji Springa;
- @EnableWebMvc: jeśli w ścieżce Classpath znajduje się biblioteka [spring-mvc]. W takim przypadku następuje automatyczna konfiguracja aplikacji internetowej;
- @ComponentScan: informuje Spring, gdzie szukać pozostałych beanów, konfiguracji i usług. Domyślnie są one wyszukiwane w pakiecie zawierającym klasę oznaczoną adnotacją, tj. w pakiecie [hello]. W ten sposób zostaną znalezione klasy [Customer] i [CustomerRepository]. Ponieważ pierwsza z nich posiada adnotację [@Entity], zostanie skatalogowana jako encja do zarządzania przez Hibernate. Ponieważ druga rozszerza interfejs [CrudRepository], zostanie zarejestrowana jako bean Spring;
- wiersze 11–12: bean [CustomerRepository] jest wstrzykiwany do kodu klasy głównej;
- wiersz 15: wykonywana jest metoda statyczna [run] klasy [SpringApplication] z projektu Spring Boot. Jej parametrem jest klasa posiadająca adnotację [Configuration] lub [EnableAutoConfiguration]. Następnie odbiegnie wszystko, co zostało wyjaśnione powyżej. Wynikiem jest kontekst aplikacji Spring, tj. zbiór beanów zarządzanych przez Spring;
- wiersze 19–48: kolejne operacje polegają jedynie na wykorzystaniu metod fasoli implementującej interfejs [CustomerRepository];
Wyniki wyświetlane w konsoli są następujące:
- wiersze 1–8: logo projektu Spring Boot;
- wiersz 9: uruchamiana jest klasa [hello.Application];
- wiersz 10: [AnnotationConfigApplicationContext] to klasa implementująca interfejs [ApplicationContext] biblioteki Spring. Jest to kontener beanów;
- wiersz 11: bean [entityManagerFactory] jest zaimplementowany za pomocą klasy [LocalContainerEntityManagerFactory], klasy biblioteki Spring. Zarządza on warstwą [JPA];
- wiersz 12: pojawia się [Hibernate]. Wybrano właśnie tę implementację JPA;
- wiersz 19: dialektem Hibernate jest wariant SQL, który ma być używany wraz z SGBD. W tym przypadku dialekt [H2Dialect] wskazuje, że Hibernate będzie współpracować z SGBD i H2;
- wiersze 21–22: utworzono bazę danych. Utworzono tabelę [CUSTOMER]. Oznacza to, że Hibernate został skonfigurowany do generowania tabel na podstawie definicji JPA, w tym przypadku definicji JPA klasy [Customer];
- wiersze 26–30: wynik metody [findAll] z interfejsu;
- wiersz 34: wynik metody [findOne] interfejsu;
- wiersze 38–39: wyniki metody [findByLastName];
- wiersze 41 i kolejne: logi zamknięcia kontekstu Spring.
5.1.5. Ręczna konfiguracja projektu Spring Data
Duplikujemy poprzedni projekt w projekcie [gs-accessing-data-jpa-02]:
![]() |
W tym nowym projekcie nie będziemy polegać na automatycznej konfiguracji dokonywanej przez Spring Boot. Zrobimy to ręcznie. Może to być przydatne, jeśli domyślne ustawienia nam nie odpowiadają.
Najpierw określimy niezbędne zależności w pliku [pom.xml]:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-accessing-data-jpa-02</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.3.RELEASE</version>
</parent>
<dependencies>
<!-- Spring Data -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
<!-- Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
</dependency>
<!-- Baza danych H2 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<!-- Tomcat JDBC -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
</dependency>
</dependencies>
<properties>
<!-- używaj UTF-8 do wszystkiego -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/libs-release</url>
</repository>
<repository>
<id>org.jboss.repository.releases</id>
<name>JBoss Maven Release Repository</name>
<url>https://repository.jboss.org/nexus/content/repositories/releases</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/libs-release</url>
</pluginRepository>
</pluginRepositories>
</project>
- wiersze 10–14: nadrzędny projekt Maven, z którego będziemy korzystać z zdefiniowanych w nim bibliotek;
- wiersze 18–21: Spring Data używany do dostępu do bazy danych;
- wiersze 23–26: implementacja specyfikacji JPA w Hibernate;
- wiersze 28–31: SGBD H2;
- wiersze 33–36: bazy danych są często używane z pulami otwartych połączeń, co pozwala uniknąć wielokrotnego otwierania i zamykania połączeń. W tym przypadku zastosowano implementację [tomcat-jdbc];
W nowym projekcie encja [Customer] i interfejs [CustomerRepository] pozostają bez zmian. Zmienimy klasę [Application], która zostanie podzielona na dwie klasy:
- [Config], która będzie klasą konfiguracyjną:
- [Main], która będzie klasą wykonalną;
![]() |
Klasa wykonywalna [Application] ma teraz następujący wygląd:
package console;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import repositories.CustomerRepository;
import config.AppConfig;
import entities.Customer;
public class Application {
public static void main(String[] args) {
// instancjonowanie kontekstu Spring
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
CustomerRepository repository = context.getBean(CustomerRepository.class);
// zapisanie kilku klientów
repository.save(new Customer("Jack", "Bauer"));
repository.save(new Customer("Chloe", "O'Brian"));
repository.save(new Customer("Kim", "Bauer"));
repository.save(new Customer("David", "Palmer"));
repository.save(new Customer("Michelle", "Dessler"));
...
// zamknięcie kontekstu
context.close();
}
}
- wiersz 9: klasa [Application] nie zawiera już adnotacji konfiguracyjnych;
- wiersze 3–7: należy zauważyć, że nie ma już importów pakietów [Spring Boot];
- wiersz 12: tworzone są instancje beanów Spring. Uzyskuje się kontekst Spring zawierający odniesienia do tak utworzonych beanów;
- wiersz 13: żądamy odwołania do fasoli typu [CustomerRepository];
Klasa [Config] , która konfiguruje projekt, wygląda następująco:
package config;
import javax.persistence.EntityManagerFactory;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = { "repositories" })
@Configuration
// @ComponentScan(basePackages={"package1","package2"})
public class AppConfig {
// baza danych H2
@Bean
public DataSource dataSource() {
// źródło danych TomcatJdbc
DataSource dataSource = new DataSource();
// konfiguracja dostępu JDBC
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:./demo");
dataSource.setUsername("sa");
dataSource.setPassword("");
// początkowo otwarte połączenie
dataSource.setInitialSize(1);
// wynik
return dataSource;
}
// dostawca JPA
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(false);
hibernateJpaVendorAdapter.setGenerateDdl(true);
hibernateJpaVendorAdapter.setDatabase(Database.H2);
return hibernateJpaVendorAdapter;
}
// EntityManagerFactory
@Bean
public EntityManagerFactory entityManagerFactory(JpaVendorAdapter jpaVendorAdapter, DataSource dataSource) {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(jpaVendorAdapter);
factory.setPackagesToScan("entities");
factory.setDataSource(dataSource);
factory.afterPropertiesSet();
return factory.getObject();
}
// Menedżer transakcji
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
}
- wiersz 17: adnotacja [@EnableTransactionManagement] wskazuje, że należy zinterpretować adnotacje [@Transactional]. Metody interfejsów [CrudRepository] posiadają te adnotacje. Są one zatem wykonywane w ramach transakcji;
- wiersz 18: adnotacja [@EnableJpaRepositories] pozwala wskazać katalogi, w których znajdują się interfejsy Spring Data [CrudRepository]. Interfejsy te staną się komponentami Spring i będą dostępne w jego kontekście;
- wiersz 19: adnotacja [@Configuration] sprawia, że klasa [Config] staje się klasą konfiguracyjną Springa;
- wiersz 20: adnotacja [@ComponentScan] pozwala wymienić katalogi, w których należy szukać komponentów Springa. Komponenty Spring to klasy oznaczone adnotacjami Spring, takimi jak @Service, @Component, @Controller itp. W tym przypadku nie ma innych komponentów poza tymi zdefiniowanymi w klasie [AppConfig], dlatego adnotacja została skomentowana;
- wiersze 24–37: definiują źródło danych, bazę danych H2. To właśnie adnotacja @Bean w wierszu 25 sprawia, że obiekt utworzony przez tę metodę staje się komponentem zarządzanym przez Spring. Nazwa metody może być tutaj dowolna. Musi ona jednak nosić nazwę [dataSource], jeśli metoda EntityManagerFactory z wiersza 51 nie istnieje i jest definiowana przez autokonfigurację;
- wiersz 30: baza danych będzie nosić nazwę [demo] i zostanie wygenerowana w folderze projektu;
- wiersze 40–47: definiują używaną implementację JPA, w tym przypadku implementację Hibernate. Nazwa metody może być dowolna;
- wiersz 43: brak logów SQL;
- wiersz 44: baza danych zostanie utworzona, jeśli jeszcze nie istnieje;
- wiersze 50–58: definiują metodę EntityManagerFactory, która będzie zarządzać trwałością JPA. Metoda ta musi obowiązkowo nosić nazwę [entityManagerFactory];
- wiersz 51: metoda otrzymuje dwa parametry o typach odpowiadających dwóm wcześniej zdefiniowanym beanom. Zostaną one następnie skonstruowane, a następnie wstrzyknięte przez Spring jako parametry metody;
- wiersz 53: określa używaną implementację JPA;
- wiersz 54: określa katalogi, w których znajdują się encje JPA;
- wiersz 55: określa źródło danych, które ma być obsługiwane;
- wiersze 61–66: menedżer transakcji. Metoda musi obowiązkowo nosić nazwę [transactionManager]. Jako parametr otrzymuje ona bean z wierszy 51–58;
- wiersz 64: menedżer transakcji jest powiązany z EntityManagerFactory;
Powyższe metody można zdefiniować w dowolnej kolejności.
Uruchomienie projektu daje te same wyniki. W folderze projektu pojawia się nowy plik – plik bazy danych o nazwie H2:
![]() |
5.1.6. Tworzenie archiwum wykonywalnego
Aby utworzyć archiwum wykonywalne projektu, można postępować w następujący sposób:
![]() |
- w [1]: tworzymy konfigurację uruchomienia;
- w [2]: typu [Java Application]
- w [3]: określa projekt do uruchomienia (użyj przycisku Browse);
- w [4]: określa klasę do wykonania;
- w [5]: nazwa konfiguracji uruchomienia – może być dowolna;
![]() |
- w [6]: eksportuje się projekt;
- w [7]: w postaci archiwum wykonywalnego JAR;
- w [8]: podaje ścieżkę i nazwę pliku wykonywalnego, który ma zostać utworzony;
- w [9]: nazwa konfiguracji uruchomieniowej utworzonej w [5];
10 ![]() |
- w [10] – utworzony plik archiwum;
Po wykonaniu tych czynności otwieramy konsolę w folderze zawierającym archiwum wykonywalne:
Archiwum uruchamia się w następujący sposób:
.....\dist>java -jar gs-accessing-data-jpa-02.jar
W konsoli pojawiają się następujące wyniki:
5.1.7. Tworzenie projektu [Spring Data]
Aby utworzyć szkielet projektu Spring Data, można postępować w następujący sposób:
![]() |
- w [1] tworzymy nowy projekt;
- w [2]: typu [Spring Starter Project];
- w [3] podajemy nazwę grupy projektu;
- w [4]: podajemy nazwę artefaktu (w tym przypadku pliku jar), który zostanie utworzony podczas kompilacji projektu;
- w polu [5]: nazwa projektu w Eclipse – może być dowolna (nie musi być identyczna z wartością z pola [4]);
- w [7]: określa się, że zostanie utworzony projekt zawierający warstwę [JPA] wraz z SGBD i MySQL. Niezbędne zależności dla takiego projektu zostaną następnie uwzględnione w pliku [pom.xml];
![]() |
- w pliku [8] należy podać nazwę folderu projektu;
- w pliku [9] zakończyć pracę kreatora;
![]() |
- w pliku [10]: utworzony projekt;
Plik [pom.xml] zawiera zależności niezbędne dla projektu JPA:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st.springdata</groupId>
<artifactId>intro-spring-data-01</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>intro-spring-data-01</name>
<description>démo spring data avec table de produits</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.3.RELEASE</version>
<relativePath/> <!-- wyszukiwanie elementu nadrzędnego z repozytorium -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<start-class>demo.IntroSpringData01Application</start-class>
<java.version>1.7</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- wiersze 14–19: nadrzędny projekt Maven;
- wiersze 28–31: zależność niezbędna dla projektu JPA – obejmie projekt [Spring Data];
- wiersze 32–36: zależność MySQL od sterownika JDBC;
- wiersze 37–41: zależności wymagane dla testów JUnit zintegrowanych ze Springiem;
Klasa wykonywalna [Application] nie wykonuje żadnych czynności, ale jest wstępnie skonfigurowana:
package demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class IntroSpringData01Application {
public static void main(String[] args) {
SpringApplication.run(IntroSpringData01Application.class, args);
}
}
- adnotacja [@SpringBootApplication] sprawia, że klasa ta staje się klasą samokonfigurującą się w projekcie;
Klasa testowa [ApplicationTests] nie wykonuje żadnych czynności, ale jest wstępnie skonfigurowana:
package demo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = IntroSpringData01Application.class)
public class IntroSpringData01ApplicationTests {
@Test
public void contextLoads() {
}
}
- wiersz 9: adnotacja [@SpringApplicationConfiguration] umożliwia wykorzystanie pliku konfiguracyjnego [IntroSpringData01Application]. Klasa testowa będzie w ten sposób korzystać ze wszystkich beanów zdefiniowanych w tym pliku;
- wiersz 8: adnotacja [@RunWith] umożliwia integrację Springa z JUnit: klasa będzie mogła zostać uruchomiona jako test JUnit. [@RunWith] jest adnotacją JUnit (wiersz 4), podczas gdy klasa [SpringJUnit4ClassRunner] jest klasą Spring (wiersz 6);
Teraz, gdy mamy już szkielet aplikacji JPA, możemy go uzupełnić.




















