- Spring Web MVC
- Spring Data JPA
- H2 Database
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version> <!--$NO-MVN-MAN-VER$ -->
</plugin>
properties :
spring.profiles.active=**test**
spring.jpa.open-in-view=false
yaml :
spring:
profiles:
active:
- test
jpa:
open-in-view: false
properties :
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.username=sa
spring.datasource.password=
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.defer-datasource-initialization=true
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
yaml :
spring:
datasource:
url: jdbc:h2:mem:testdb
username: sa
password:
h2:
console:
enabled: true
path: /h2-console
jpa:
show-sql: true
properties:
hibernate:
format_sql: true
database-platform: org.hibernate.dialect.H2Dialect
defer-datasource-initialization: true
@Column(columnDefinition = "TIMESTAMP WITHOUT TIME ZONE")
private Instant moment;
public class Order {
...
@ManyToOne
@JoinColumn(name = "client_id")
private User client;
public class User {
...
@OneToMany(mappedBy = "client")
private List<Order> orders = new ArrayList<>();
public class Payment {
...
@OneToOne
@MapsId
private Order order;
public class Order {
...
@OneToOne(mappedBy = "order", cascade = CascadeType.ALL)
private Payment payment;
public class Product {
...
@ManyToMany
@JoinTable(name = "tb_product_category",
joinColumns = @JoinColumn(name = "product_id"),
inverseJoinColumns = @JoinColumn(name = "category_id"))
private Set<Category> categories = new HashSet<>();
public class Category {
...
@ManyToMany(mappedBy = "categories")
private Set<Product> products = new HashSet<>();
@Embeddable
public class OrderItemPK {
@ManyToOne
@JoinColumn(name = "order_id")
private Order order;
@ManyToOne
@JoinColumn(name = "product_id")
private Product product;
...
@Entity
@Table(name = "tb_order_item")
public class OrderItem {
@EmbeddedId
private OrderItemPK id = new OrderItemPK();
private Integer quantity;
private Double price;
public OrderItem() {
}
public OrderItem(Order order, Product product, Integer quantity, Double price) {
id.setOrder(order);
id.setProduct(product);
this.quantity = quantity;
this.price = price;
}
public Order getOrder() {
return id.getOrder();
}
public void setOrder(Order order) {
id.setOrder(order);
}
...
public class Order {
...
@OneToMany(mappedBy = "id.order")
private Set<OrderItem> items = new HashSet<>();
...
public Set<OrderItem> getItems() {
return items;
}
public List<Product> getProducts() {
return items.stream().map(x -> x.getProduct()).toList();
}
public class Product {
...
@OneToMany(mappedBy = "id.product")
private Set<OrderItem> items = new HashSet<>();
...
public Set<OrderItem> getItems() {
return items;
}
public List<Order> getOrders() {
return items.stream().map(x -> x.getOrder()).toList();
}
@Service
@Transational(readOnly=true)
@JPARepository<T, ID>
@Transactional(propagation = Propagation.SUPPORTS)
On doit l'utiliser pour capturer les exceptions qui ne sont pas liés au framework spring, par exemple h2.JDBC...Exception, avec le "Propagation.SUPPORTS" le DataIntegrityViolationException du Spring sera capture e on pourra le traiter dans le ExceptionHandler.
@PostMapping
@RequestBody
ResponseEntity
URI location = ServletUriComponentsBuilder
.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(dto.getId())
.toUri();
return ResponseEntity.created(location).body(dto);
@GetMapping
@Pageable
Exemplos de parametros : ?size=12&page=0&sort=name,desc
@GetMapping("/{id}")
@PathVariable
Criar uma classe que vai interceptar todas as exceptions de determinadas classes customizadas. Por exemplo :
@ControllerAdvice
public class ControllerExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<CustomError> resourceNotFound(ResourceNotFoundException e, HttpServletRequest request) {
final HttpStatus status = HttpStatus.NOT_FOUND;
final CustomError err = new CustomError(Instant.now(), status.value(), e.getMessage(), request.getRequestURI());
return ResponseEntity.status(status).body(err);
}
}
ResourceNotFoundException.class é nossa classe de erro customizada que extende RuntimeException, podemos tratar outros tipos de exception dentro desta classe.
CustomError classe criada com os mesmo atributos de um exception normal lançada por um Controller em um erro 500
{
"timestamp": "2023-06-05T19:42:18.945+00:00",
"status": 500,
"error": "Internal Server Error",
"path": "/products/100"
}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
https://jakarta.ee/specifications/bean-validation/3.0/
https://jakarta.ee/specifications/bean-validation/3.0/apidocs/
https://javaee.github.io/tutorial/bean-validation.html
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
- Dependencias AuthorizationServer e ResourceServer
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
O Spring Security disponibiliza um caminho padrao no path para geracao do token JWT
POST > host:port/oauth2/token
- Auth Server
- app credentials + user credentials | token (signature, claims, expiration)
- Resource Server
- resource URI + token | resource access
- JWT
- CORS (Configurar quais hosts podem acessar o backend)
com.devsuperior.demo.config.AuthorizationServerConfig
com.devsuperior.demo.config.ResourceServerConfig
@PreAuthorize("hasRole('ROLE_ADMIN')")
@PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_OPERATOR')")
Adicionar o plugin :
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.7</version>
<configuration>
<excludes>
<exclude>com/devsuperior/dscommerce/DscommerceApplication.class</exclude>
<exclude>com/devsuperior/dscommerce/config/**</exclude>
<exclude>com/devsuperior/dscommerce/entities/**</exclude>
<exclude>com/devsuperior/dscommerce/dto/**</exclude>
<exclude>com/devsuperior/dscommerce/controllers/handlers/**</exclude>
<exclude>com/devsuperior/dscommerce/services/exceptions/**</exclude>
</excludes>
</configuration>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>jacoco-report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<outputDirectory>target/jacoco-report</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>