−Table of Contents
Spring - pierwsze kroki
Wygeneruj projekt
Wygeneruj projekt wybierając w InteliJ opcję Spring Initializer i zaznaczając:
- maven
- Developer Tools → Lombok
- Web → Spring Web
- SQL → Spring Data JPA i Postgresql Driver
Lokalna baza danych
- Utwórz rolę (użytkownika)
spring_demo_user
z hasłemspring_demo_user
; nadaj mu uprawnienia do logowania - Utwórz lokalną baze danych PostgreSQL o nazwie
spring_demo
i ustaw jako właścicielaspring_demo_user
application.properties
Plik znajduje się w katalogu resources. Wpisz tam parametry połączenia z bazą danych
spring.sql.init.mode=always spring.datasource.initialize=true spring.datasource.url=jdbc:postgresql://localhost:5432/spring_demo spring.datasource.username=spring_demo_user spring.datasource.password=spring_demo_user # Hibernate ddl auto (create, create-drop, validate, update) spring.jpa.hibernate.ddl-auto=create-drop spring.jpa.hibernate.show-sql=true spring.jpa.database=POSTGRESQL
model
Utwórz pakiet model
i dodaj do niego klasę AdminUnit
package com.example.spring_demo.model; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Entity @Table(name = "admin_units") @Data @AllArgsConstructor @NoArgsConstructor public class AdminUnit { @Id // @GeneratedValue(strategy = GenerationType.IDENTITY) Long id; String name; int adminLevel; double area; double population; Long parentId=-1L; // lub // AdminUnit parent; }
Czasem dla H2 @GeneratedValue(strategy = GenerationType.IDENTITY)
nie działa, wtedy trzeba generować jawnie.
repository
Utwórz pakiet repository i interfejs:
package com.example.spring_demo.repository; import com.example.spring_demo.model.AdminUnit; import org.springframework.data.domain.Example; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import java.util.List; public interface AdminUnitRepository extends JpaRepository<AdminUnit,Long> { }
Ten (minimalistyczny) interfejs zapewnia wszystkie operacje CRUD (Create, Read, Update, Delete) oraz sortowanie i paginację.
controller
Dodaj klasę AdminUnitController
. Zawiera definicje wszystkich metod wyeksportowanych przez REST API. Korzysta z AdminUnitRepository (wszczepiona zależność).
package com.example.spring_demo.controller; import com.example.spring_demo.model.AdminUnit; import com.example.spring_demo.repository.AdminUnitRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/api") public class AdminUnitController { @Autowired private AdminUnitRepository adminUnitRepository; @GetMapping("/hello") public String sayHello(@RequestParam(value = "myName", defaultValue = "World") String name) { return String.format("Hello %s!", name); } @GetMapping(value="/units", produces="application/json") public List<AdminUnit> getAllAdminUnits() { return this.adminUnitRepository.findAll(); } @PutMapping("/units") @ResponseBody public void update(@RequestBody AdminUnit newAdminUnit) { adminUnitRepository.save(newAdminUnit); } @DeleteMapping("/units/{id}") public ResponseEntity<String> delete(@PathVariable("id") long id){ if(adminUnitRepository.getReferenceById(id)==null){ return new ResponseEntity<>("Resource not found", HttpStatus.NOT_FOUND); } adminUnitRepository.deleteById(id); return new ResponseEntity<>("OK", HttpStatus.OK); } }
Jak widac wszystkie punkty końcowe będą miały adres localhost:8080/api/units…
Kod wykonywany podczas uruchamiania
Biblioteka Hibernate została skonfigurowana tak, aby przy każdym uruchomieniu usuwać i tworzyć od nowa bazę. Aby wypełnić ją rekordami dodaj (w głownym katalogu) klasę odpowiedzialną za wypełnienie bazy danych rekordami.
package com.example.spring_demo; import com.example.spring_demo.model.AdminUnit; import com.example.spring_demo.repository.AdminUnitRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; @Component public class LoadDataOnStartUp { @Autowired private AdminUnitRepository adminUnitRepository; @EventListener(ApplicationReadyEvent.class) public void loadData() { // do something AdminUnit[] units = { new AdminUnit(1L,"Kraków",7,300,800000,-1L), new AdminUnit(2L,"Warszawa",7,500,1500000,-1L) }; // adminUnitRepository.saveAll(new ArrayList<>(Arrays.asList(units))); for(var u:units)adminUnitRepository.save(u); } }
Opcjonalnie, kod można wykonać tylko raz, aby zasilić baze danych, a potem zakomentować. Należy wtedy przełączyć tryb generacji bazy danych Hibernate w application.properties
na update
.
Dodaj wsparcie dla Swaggera
Dodaj zależność do pom.xml
<dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.2.0</version> </dependency>
Dzięki temu pod adresem http://localhost:8080/swagger-ui/index.html można będzie przetestować wszystkie metody REST API
Wyszukiwanie według kryteriów
W AdminUnitRepository
dodaj metodę interfejsu, a w adnotacji Query kwerendę w języku JPQL:
@Query("SELECT u FROM AdminUnit u WHERE u.name LIKE %:partialName% AND (:aLevel IS NULL OR u.adminLevel = :aLevel)") List<AdminUnit> getAdminUnitsByNameContainingAndAdminLevel(@Param("partialName") String pname, @Param("aLevel") Integer al);
Dodaj w klasie AdminUnitController
kolejną metodę GET:
@GetMapping(value="/units/search", produces="application/json") public List<AdminUnit> getAdminUnitsByNameAndAdminLevel( @RequestParam(value = "partialname", defaultValue = "") String name, @RequestParam(value = "alevel", required=false) Integer adminLevel){ return this.adminUnitRepository.getAdminUnitsByNameContainingAndAdminLevel(name,adminLevel); }
W oknie Swagera można przetestować wyszukiwanie, np:
- jednostki zawierające 'r' w nazwie bez podania poziomu
- jednostki zawierające 's' w nazwie poziomu 7