Instrukcja do przykładu z technologii Spring Data
Na seminarium zostanie przedstawione krok po kroku jak powstaje projekt oparty o tech- nologie Spring Data. Zostanie on wsparty o technologię Spring Boot, dzięki której w prosty sposób zostanie skonfigurowane połączenie z bazą danych. Do tego zostanie również wykorzystana relacyjna baza danych H2 w wersji “in memory”
Plan instrukcji:
1. Stworzenie minimalnej aplikacji pozwalającej na komunikacje z bazą danych.
2. Rozwinięcie aplikacji o różne funkcjonalności Spring Data.
1. Część dotycząca minimalnej konfiguracji
1.1. Inicjalizacja projektu SpringInicjalizacja projektu Spring za pomocą Spring Initializer dostępna na stronie https://start.spring.io/
Po wygenerowaniu projektu rozpakowujemy go i przenosimy do katalogu roboczego.
W Eclipse przechodzimy w menu do File -> Open project from File System... i następnie wska- zujemy we ścieżce źródło do naszego projektu.
1.2. Konfiguracja bazy danych
Konfiguracja odbywa się w prosty sposób dzięki użycia Spring Boota.
Modyfikujemy plik application.properties dodając następujący kod
# H2 Databse
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa
spring.datasource.password=password
#JPA - implementacja Hibernate
spring.jpa.database-platform=org.hibernate.dia- lect.H2Dialect
spring.jpa.hibernate.ddl-auto=create
#Konsola Web bazy danych H2 spring.h2.console.enabled=true spring.h2.console.path=/h2-console server.port=8081
1.3. Tworzymy encje utrwalane w bazie danych Tworzymy klase Person.java w pakiecie zti.shop.model Klasa ta odwzorowuje obiekt w bazie danych.
Adnotacje:
@Entity - w JPA mówi o tym, że ta klasa jest odwzorowaniem encji w bazie danych
@Id - mówi o tym, że dane pole jest unikalne
@GeneratedValue - mówi o tym, że ta wartość ma być automatycznie generowana
package zti.shop.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
//Adnotacja @Entity w JPA mówi o tym, że ta klasa jest odwzorowaniem encji w bazie da- nych@Entity
public class Person {
//Adnotacja @Id mówi o tym, że dane pole jest unikalne
//Adnotacja @Generated value mówi o tym, że ta wartość ma być automatycznie generowana
@Id
@GeneratedValue private Long id;
private String firstName;
private String lastName;
public Long getId() { return id;
}
public void setId(Long id) { this.id = id;
}
public String getFirstName() { return firstName;
}public void setFirstName(String firstName) { this.firstName = firstName;
}
public String getLastName() { return lastName;
}
public void setLastName(String lastName) { this.lastName = lastName;
} }
1.4. Stworzenie repozytorium dla encji
Jest to interface rozszerzający inny interfejs CrudRepository<encja,typ identyfikatora>, który dostarcza nam zbiór podstawowych metod do zarządzania encjami w bazie danych.
1.5. Stworzenie komponentu
Komponent będzie wykorzystywał zaimplementowane przez nas fukcjonalności Tworzymy plik RunAtStart.java w pakiecie zti.shop
package zti.shop.repository;
import org.springframework.data.repository.CrudRepository;
import zti.shop.model.Person;
public interface PersonRepository extends CrudRepository <Person, Long>{
}
package zti.shop;
import javax.annotation.PostConstruct;
import org.springframework.stereotype.Component;
import zti.shop.model.Person;
import zti.shop.repository.PersonRepository;
@Component
public class RunAtStart {
private final PersonRepository personRepository;
public RunAtStart(PersonRepository personRepository) { super();
this.personRepository = personRepository;
}
@PostConstruct
public void runAtStart(){
Person person1 = new Person();
person1.setFirstName("Adam");
person1.setLastName("Nowak");
personRepository.save(person1);
System.out.println(person1);
} }
2. Cześć rozwijająca aplikacje o różne funkcjonalności Spring Data
2.1. Dodajemy encję produktu product.java w pakiecie modelpackage zti.shop.model;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
@Entity
public class Product {
@Id
@GeneratedValue private Long id;
private String name;
private int price;
@ManyToMany(mappedBy = "products") private List<Person> people;
public Long getId() { return id;
}
public void setId(Long id) { this.id = id;
}
public String getName() { return name;
}
public void setName(String name) { this.name = name;
}
public int getPrice() { return price;
}
public void setPrice(int price) { this.price = price;
}
public List<Person> getPeople() { return people;
}
public void setPeople(List<Person> people) { this.people = people;
}
@Override
public String toString() {
return "Product [id=" + id + ", name=" + name + ", price=" + price + ", people=" + people +
"]";
} }
2.2. Edydutjemy encję person.java określając relację wiele do wielu z encją product.
package zti.shop.model;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
@Entity
public class Person {
@Id
@GeneratedValue private Long id;
private String firstName;
private String lastName;
@ManyToMany
private List<Product> products;
public List<Product> getProducts() { return products;
}
public void setProducts(List<Product> products) { this.products = products;
}
public Long getId() { return id;
}
public void setId(Long id) { this.id = id;
}
public String getFirstName() { return firstName;
}
public void setFirstName(String firstName) { this.firstName = firstName;
}
public String getLastName() { return lastName;
}
public void setLastName(String lastName) { this.lastName = lastName;
}
@Override
public String toString() {
return "Person [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", prod- ucts=" + products
+ "]";
} }
2.3. Modyfikujemy repozytorium dla encji Person
Do encji person dodane zostało zapytanie wbudowane findByFirstName(String firstName), które na podstawie naz- wy funkcji jest generowane przez Spring Data. Funkcja zwraca użytkowników z bazy danych o podanym imieniu w argumencie. Zapytania wbudowane są generowane na podstawie słów kluczowych takich jak findBy oraz nazw pól z bazy danych, czyli FirstName. Pełny zestaw możliwości jest dostępny w dokumentacji Spring Data.
2.4. Tworzymy repozytorium dla encji Product w pakiecie repository.
package zti.shop.repository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import zti.shop.model.Person;
public interface PersonRepository extends CrudRepository <Person, Long>{
Iterable<Person> findByFirstName(String firstName);
// @Query(value = "Select p.firstName from Person p") // Iterable<Person> findAll();
}
package zti.shop.repository;
import org.springframework.data.repository.CrudRepository;
import zti.shop.model.Product;
public interface ProductRepository extends CrudRepository <Product, Long>{
}
2.5. Modyfikujemy RunAtStart.java
W rozszerzonym komponencie uzupełniamy dodatkowe pola dla stworzonych encji.
package zti.shop;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import org.springframework.stereotype.Component;
import zti.shop.model.Person;
import zti.shop.model.Product;
import zti.shop.repository.PersonRepository;
import zti.shop.repository.ProductRepository;
@Component
public class RunAtStart {
private final PersonRepository personRepository;
private final ProductRepository productRepository;
public RunAtStart(PersonRepository personRepository, ProductRepository productRepository) { super();
this.personRepository = personRepository;
this.productRepository = productRepository;
}
@PostConstruct
public void runAtStart(){
Person person1 = new Person();
person1.setFirstName("Adam");
person1.setLastName("Nowak");
Person person2 = new Person();
person2.setFirstName("Zbigniew");
person2.setLastName("Brzezinski");
Product bike = new Product();
Product lego = new Product();
bike.setName("rower");
bike.setPrice(200);
lego.setName("lego");
lego.setPrice(90);
List<Product> order1= new ArrayList<Product>();
order1.add(bike);
order1.add(lego);
List<Product> order2= new ArrayList<Product>();
order2.add(lego);
person1.setProducts(order1);
productRepository.saveAll(order1);
personRepository.save(person1);
personRepository.save(person2);
System.out.println(person1);
} }
2.6. Tworzymy kontroler ShopController.java do naszej aplikacji obsługujący zapytania do serwera.
Tworzymy plik ShopController.java w pakiecie controller.
package zti.shop.controller;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import zti.shop.model.Person;
import zti.shop.model.Product;
import zti.shop.repository.PersonRepository;
import zti.shop.repository.ProductRepository;
@RequestMapping("api/v1/shop")
@RestController
@CrossOrigin("*")
public class ShopController {
@Autowired
private PersonRepository personRepository;
@Autowired
private ProductRepository productRepository;
@GetMapping(path = "/person")
public @ResponseBody Iterable<Person> getAllPeople(){
return personRepository.findAll();
}
@GetMapping(path = "/product")
public @ResponseBody Iterable<Product> getAllProducts(){
return productRepository.findAll();
}
@GetMapping(path = "person/firstname/{firstNameValue}")
public @ResponseBody Iterable<Person> getPersonByFirstName(@PathVariable("firstNameValue") String firstNameValue){
return personRepository.findByFirstName(firstNameValue);
}
@PostMapping(path = "person/{firstNameValue}/{lastNameValue}")
public void addPerson(@PathVariable("firstNameValue") String firstNameValue, @PathVariable("lastNa- meValue") String lastNameValue){
Person person = new Person();
person.setFirstName(firstNameValue);
person.setLastName(lastNameValue);
personRepository.save(person);
} }
2.7 Sprawdzamy działanie zapytań do serwera
Przez połączenie wiele do wielu encji product i person we zwróconym JSONie powstaje zapętlo- na zależność, która przy przekształcaniu tabel z bazy:
w dwa obiekty java, powoduje przesyłanie zapętlony plik json.
W takich jak ta sytuacjach przydatne jest tworzenie zapytań własnych, którymi programista może nadpisywać te domyślnie generowane przez Spring Data, jak również tworzyć nowe, które są bardziej złożone i dostosowane do stworzonej bazy danych.
2.8 Dodanie zapytania własnego do PersonRepozytory.java
W ten sposób zostaje nadpisana funkcja findAll() dla encji Person. W rezultacie powtórzenie wy- konania zapytania zwróci dane w prawidłowym jsonie.
package zti.shop.repository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import zti.shop.model.Person;
public interface PersonRepository extends CrudRepository <Person, Long>{
Iterable<Person> findByFirstName(String firstName);
@Query(value = "Select p.firstName from Person p") Iterable<Person> findAll();
}