Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Yuri89/spring-crud-postgres

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

1 Commit

Repository files navigation

🚀 CRUD de Usuários com Spring Boot + JPA + PostgreSQL + Swagger

Este projeto é um exemplo completo de API RESTful desenvolvida com Spring Boot, JPA/Hibernate, PostgreSQL e Swagger (OpenAPI 3), seguindo uma arquitetura limpa e organizada em camadas:
Controller → Service → Repository → Model.


📘 Sumário


📂 Estrutura do Projeto

com.test.crud_auto ├─ controller │ └─ UserController.java ├─ dto │ └─ UserDTO.java ├─ model │ └─ UserEntity.java ├─ repository │ └─ UserRepository.java ├─ service │ └─ UserService.java └─ CrudAutoApplication.java


⚙️ Tecnologias Utilizadas

Tecnologia Função
Java 21+ Linguagem principal
Spring Boot 3 Framework principal
Spring Data JPA Mapeamento ORM
PostgreSQL 16 Banco de dados
Docker Contêinerização do banco
Swagger / Springdoc OpenAPI Documentação automática da API
Hibernate @Version Controle de concorrência otimista

🐘 Configurando o Banco de Dados com Docker

Execute os comandos abaixo no terminal para preparar o ambiente de banco:

# Passo 1: criar uma rede Docker isolada
docker network create backend-net
# Passo 2: subir Postgres + pgAdmin na mesma rede
docker run -d \
 --name postgress-test \
 --network backend-net \
 -p 5432:5432 \
 -e POSTGRES_USER=admin \
 -e POSTGRES_PASSWORD=admin123 \
 -e POSTGRES_DB=mydb \
 postgres:16
docker run -d \
 --name pgadmin \
 --network backend-net \
 -p 5050:80 \
 -e PGADMIN_DEFAULT_EMAIL=admin@admin.com \
 -e PGADMIN_DEFAULT_PASSWORD=admin123 \
 dpage/pgadmin4

Após subir os containers, o pgAdmin estará acessível em: http://localhost:5050 Login: admin@admin.com Senha: admin123


🧩 Dependências Principais (pom.xml)

<dependencies>
 <!-- Web + JPA + PostgreSQL -->
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-data-jpa</artifactId>
 </dependency>
 <dependency>
 <groupId>org.postgresql</groupId>
 <artifactId>postgresql</artifactId>
 </dependency>
 <!-- Swagger / OpenAPI -->
 <dependency>
 <groupId>org.springdoc</groupId>
 <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
 <version>2.8.13</version>
 </dependency>
</dependencies>

⚙️ Configuração do Spring Boot (application.properties)

spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=admin
spring.datasource.password=admin123
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
# Swagger
springdoc.api-docs.path=/api-docs
springdoc.swagger-ui.path=/swagger-ui.html

🧠 Arquitetura e Código-Fonte

🧩 Entidade (UserEntity.java)

@Entity
@Table(name = "users")
public class UserEntity {
 @Id
 @GeneratedValue
 private UUID id;
 @Column(nullable = false)
 private String name;
 @Column(nullable = false)
 private int age;
 @Version
 @Column(nullable = false)
 private Long version = 0L;
 public UserEntity() {}
 public UserEntity(String name, int age) {
 this.name = name;
 this.age = age;
 }
 public UserDTO toDTO() {
 return new UserDTO(id, name, age);
 }
}

🧾 DTO (UserDTO.java)

public record UserDTO(UUID id, String name, int age) {}

💾 Repository (UserRepository.java)

public interface UserRepository extends JpaRepository<UserEntity, UUID> {}

⚙️ Service (UserService.java)

@Service
public class UserService {
 private final UserRepository repo;
 public UserService(UserRepository repo) {
 this.repo = repo;
 }
 @Transactional
 public UserDTO create(String name, int age) {
 return repo.save(new UserEntity(name, age)).toDTO();
 }
 @Transactional(readOnly = true)
 public List<UserDTO> list(int page, int size) {
 Pageable pageable = PageRequest.of(page, size, Sort.by("id").descending());
 return repo.findAll(pageable).map(UserEntity::toDTO).toList();
 }
 @Transactional
 public UserDTO update(UUID id, String name, int age) {
 UserEntity user = repo.findById(id)
 .orElseThrow(() -> new EntityNotFoundException("Usuário não encontrado"));
 user.setName(name);
 user.setAge(age);
 return repo.save(user).toDTO();
 }
 @Transactional
 public void delete(UUID id) {
 if (!repo.existsById(id))
 throw new EntityNotFoundException("Usuário não encontrado");
 repo.deleteById(id);
 }
}

🌐 Controller (UserController.java)

@RestController
@RequestMapping("/api/users")
@Tag(name = "Usuários", description = "Gerenciamento de usuários")
public class UserController {
 private final UserService service;
 public UserController(UserService service) { this.service = service; }
 @Operation(summary = "Cria um novo usuário")
 @PostMapping
 public ResponseEntity<UserDTO> create(@RequestBody UserDTO req) {
 return ResponseEntity.status(HttpStatus.CREATED)
 .body(service.create(req.name(), req.age()));
 }
 @Operation(summary = "Lista usuários paginados")
 @GetMapping
 public ResponseEntity<List<UserDTO>> list(
 @RequestParam(defaultValue = "0") int page,
 @RequestParam(defaultValue = "50") int size) {
 return ResponseEntity.ok(service.list(page, size));
 }
 @Operation(summary = "Atualiza um usuário existente")
 @PutMapping("/{id}")
 public ResponseEntity<UserDTO> update(@PathVariable UUID id, @RequestBody UserDTO req) {
 return ResponseEntity.ok(service.update(id, req.name(), req.age()));
 }
 @Operation(summary = "Remove um usuário pelo ID")
 @DeleteMapping("/{id}")
 public ResponseEntity<Void> delete(@PathVariable UUID id) {
 service.delete(id);
 return ResponseEntity.noContent().build();
 }
}

🧾 Endpoints da API

Método Endpoint Descrição
POST /api/users Cria um novo usuário
GET /api/users Lista usuários paginados
PUT /api/users/{id} Atualiza usuário existente
DELETE /api/users/{id} Remove um usuário

🧭 Acessando o Swagger UI

Após iniciar a aplicação, acesse:

http://localhost:8080/swagger-ui.html

A documentação interativa será exibida automaticamente com todos os endpoints da API.


🐞 Tratamento de Concorrência com @Version

O campo @Version na entidade protege contra updates simultâneos. Se dois usuários tentarem salvar a mesma entidade ao mesmo tempo, o segundo update causará uma exceção:

org.hibernate.StaleObjectStateException

Essa abordagem evita perda de dados e inconsistências silenciosas.

AltStyle によって変換されたページ (->オリジナル) /