TP4.1 - Integração PostgreSQL com Jakarta Persistence
Migração para um Banco de Dados de Produção
Objetivos
Este trabalho prático permitirá que você:
- Integrar o PostgreSQL com Spring Boot
- Utilizar Jakarta Persistence (JPA) para persistência de dados
- Configurar o Hibernate como ORM
- Criar e gerenciar um banco de dados relacional
- Entender a diferença entre Jakarta e Javax Persistence
- Dominar a configuração com application.properties e application.yml
Tabela de Conteúdos
- Revisão Rápida
- Adição do Banco de Dados
- Teste com PostgreSQL
- Exercício 1: Javax Persistence
- Exercício 2: Configuração YAML
- Exercício 3: Lombok
- Exercício 4: Recriação Automática
- Exercício 5: Outro Banco de Dados
1. Revisão Rápida
Contexto
Nos TPs anteriores, criamos uma aplicação CRUD completa com H2. Agora, vamos migrar para o PostgreSQL, um banco de dados relacional profissional adequado para produção.
Objetivos deste TP
- Migrar de H2 para PostgreSQL - Passar de um banco embarcado para um banco de produção
- Utilizar Jakarta Persistence (JPA) - Mapear objetos Java para tabelas SQL
- Configurar PostgreSQL - Instalar e configurar um banco de dados profissional
Pré-requisitos
- TP3 (Arquitetura CRUD) concluído
- PostgreSQL instalado (versão 12 ou superior)
- pgAdmin ou DBeaver para gerenciar o PostgreSQL
- Visual Studio Code com extensões Java
2. Adição do Banco de Dados
2.1 Adição das Dependências no pom.xml
Abra seu arquivo pom.xml e adicione as duas dependências abaixo na seção <dependencies>:
<!-- Driver PostgreSQL -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Spring Data JPA (inclui Jakarta Persistence) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Explicação:
| Dependência | Função |
|---|---|
postgresql | Driver JDBC para conectar ao PostgreSQL |
spring-boot-starter-data-jpa | Inclui JPA, Hibernate e Spring Data |
Nota: Após adicionar, o VS Code irá sugerir recarregar o projeto Maven. Aceite ou execute:
mvn clean install -DskipTests
2.2 Configuração do application.properties
Crie ou edite o arquivo src/main/resources/application.properties:
# URL de conexão com o banco de dados
spring.datasource.url=jdbc:postgresql://localhost:5432/greeting_db?useSSL=false
# Credenciais de conexão
spring.datasource.username=postgres
spring.datasource.password=postgres
# Configuração do Hibernate
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
# Estratégia de geração do schema
spring.jpa.hibernate.ddl-auto=create
# Exibir queries SQL (útil para desenvolvimento)
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
Explicação das propriedades:
| Propriedade | Descrição | Valores possíveis |
|---|---|---|
spring.datasource.url | URL de conexão JDBC | Formato: jdbc:postgresql://host:port/database |
spring.datasource.username | Nome de usuário do PostgreSQL | Padrão: postgres |
spring.datasource.password | Senha do PostgreSQL | Ajuste conforme sua instalação |
spring.jpa.hibernate.ddl-auto | Estratégia de criação do schema | none, validate, update, create, create-drop |
spring.jpa.show-sql | Exibir queries SQL no console | true ou false |
Valores de ddl-auto:
Importante: Troque greeting_db pelo nome do seu banco de dados, se necessário.
2.3 Anotação da Entidade Greeting.java
Altere a classe Greeting.java para torná-la uma entidade JPA:
package com.example.demo;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity
@Table(name = "greeting")
public class Greeting {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "content", nullable = false)
private String content;
// Construtor padrão (requerido pelo JPA)
public Greeting() {
}
// Construtor com parâmetros
public Greeting(Long id, String content) {
this.id = id;
this.content = content;
}
// Getters e Setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
Explicação das anotações:
| Anotação | Função |
|---|---|
@Entity | Marca a classe como entidade JPA (mapeada para uma tabela) |
@Table(name = "greeting") | Especifica o nome da tabela SQL |
@Id | Marca o campo como chave primária |
@GeneratedValue(strategy = GenerationType.IDENTITY) | Auto-incremento do ID |
@Column(name = "content", nullable = false) | Configuração da coluna SQL |
Arquitetura JPA:
2.4 Criação do Repository
Crie uma interface GreetingRepository.java no pacote com.example.demo:
package com.example.demo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface GreetingRepository extends JpaRepository<Greeting, Long> {
// O Spring Data JPA gera automaticamente a implementação
// Métodos disponíveis: save(), findAll(), findById(), delete(), etc.
}
Métodos gerados automaticamente:
| Método | Descrição |
|---|---|
save(Greeting g) | Salvar ou atualizar |
findAll() | Recuperar todas as entradas |
findById(Long id) | Recuperar por ID |
deleteById(Long id) | Remover por ID |
count() | Contar as entradas |
2.5 Modificação do Controller
Altere o GreetingController.java para usar o repository:
package com.example.demo;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
private final GreetingRepository greetingRepository;
public GreetingController(GreetingRepository greetingRepository) {
this.greetingRepository = greetingRepository;
}
@GetMapping("/greeting")
public Greeting greeting(@RequestParam(value="name") String name) {
Greeting greeting = new Greeting(
counter.incrementAndGet(),
String.format(template, name)
);
// Salvar no banco de dados
return greetingRepository.save(greeting);
}
}
Fluxo de dados:
2.6 Criação do Banco de Dados PostgreSQL
Opção A: Com pgAdmin
-
Abrir o pgAdmin
-
Criar um usuário (opcional):
- Clique direito em "Login/Group Roles"
- "Create" → "Login/Group Role"
- Aba "General": Name =
postgres - Aba "Definition": Password =
postgres - Aba "Privileges": Marque "Can login?" e "Create databases?"
- Salvar
-
Criar o banco de dados:
- Clique direito em "Databases"
- "Create" → "Database"
- Database name:
greeting_db - Owner:
postgres - Salvar
Opção B: Com SQL (linha de comando)
-- Criar o banco de dados
CREATE DATABASE greeting_db;
-- Criar o usuário (se necessário)
CREATE USER postgres WITH PASSWORD 'postgres';
-- Conceder privilégios
GRANT ALL PRIVILEGES ON DATABASE greeting_db TO postgres;
Para executar estes comandos:
# Windows
psql -U postgres
# macOS/Linux
sudo -u postgres psql
3. Teste com PostgreSQL
3.1 Verificação da Configuração
Antes de iniciar a aplicação, verifique:
Checklist:
- PostgreSQL está iniciado
- O banco de dados
greeting_dbexiste - O usuário
postgrestem os privilégios corretos - O arquivo
application.propertiesestá correto - A classe
Greetingpossui a anotação@Entity - A interface
GreetingRepositoryfoi criada - O
GreetingControllerutiliza o repository
3.2 Inicialização da Aplicação
No VS Code:
Método 1: Via Terminal
# Limpar e compilar
mvn clean install
# Iniciar a aplicação
mvn spring-boot:run
Método 2: Via Interface
- Abra
GreetingApplication.java - Clique em "Run" acima do método
main - Ou pressione F5
Logs esperados:
Hibernate: drop table if exists greeting cascade
Hibernate: create table greeting (id bigserial not null, content varchar(255) not null, primary key (id))
Started GreetingApplication in X.XXX seconds
3.3 Teste dos Endpoints
Teste 1: Criar um greeting
http://localhost:8080/greeting?name=John
Resposta esperada:
{
"id": 1,
"content": "Hello, John!"
}
Teste 2: Criar vários greetings
http://localhost:8080/greeting?name=Alice
http://localhost:8080/greeting?name=Bob
http://localhost:8080/greeting?name=Charlie
3.4 Verificação no PostgreSQL
Com pgAdmin:
- Abra o pgAdmin
- Navegue: Servers → PostgreSQL → Databases → greeting_db → Schemas → public → Tables
- Clique direito em "greeting" → "View/Edit Data" → "All Rows"
Com SQL:
-- Conectar ao banco
\c greeting_db
-- Exibir todas as entradas
SELECT * FROM greeting;
-- Resultado esperado:
-- id | content
-- ---+----------------
-- 1 | Hello, John!
-- 2 | Hello, Alice!
-- 3 | Hello, Bob!
-- 4 | Hello, Charlie!
3.5 Logs SQL no Console
Graças ao spring.jpa.show-sql=true, você verá as queries SQL no console:
Hibernate: insert into greeting (content) values (?)
4. Exercício 1: Refazer com Javax Persistence
Enunciado
Refaça o projeto utilizando Javax Persistence ao invés de Jakarta Persistence (versão antiga do JPA).
Objetivo
Entender a diferença entre Jakarta e Javax Persistence.
Contexto Histórico
Instruções
Etapa 1: Modificar pom.xml
Adicione a dependência Javax Persistence:
<!-- Adição do Javax Persistence -->
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>2.2</version>
</dependency>
Etapa 2: Modificar os imports em Greeting.java
Substitua:
import jakarta.persistence.*;
Por:
import javax.persistence.*;
Etapa 3: Classe completa com Javax
package com.example.demo;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "greeting")
public class Greeting {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "content", nullable = false)
private String content;
public Greeting() {}
public Greeting(Long id, String content) {
this.id = id;
this.content = content;
}
// Getters e Setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
Etapa 4: Testar
- Recompile:
mvn clean install -DskipTests - Reinicie a aplicação
- Verifique se tudo funciona
Perguntas para Reflexão
- Qual é a principal diferença entre Javax e Jakarta Persistence?
- Por que Jakarta é recomendado para novos projetos?
- É possível misturar os dois no mesmo projeto?
5. Exercício 2: Configuração com application.yml
Enunciado
Converta sua configuração application.properties para o formato YAML.
Objetivo
Dominar o formato YAML para configuração do Spring Boot.
Instruções
Etapa 1: Remover application.properties
Remova ou renomeie o arquivo application.properties.
Etapa 2: Criar application.yml
Crie src/main/resources/application.yml:
spring:
datasource:
url: jdbc:postgresql://localhost:5432/greeting_db?useSSL=false
username: postgres
password: postgres
jpa:
hibernate:
ddl-auto: create
show-sql: true
properties:
hibernate:
jdbc:
lob:
non_contextual_creation: true
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
Etapa 3: Testar
Reinicie a aplicação e verifique se tudo funciona.
Comparação
application.properties:
spring.datasource.url=jdbc:postgresql://localhost:5432/greeting_db
spring.datasource.username=postgres
spring.jpa.hibernate.ddl-auto=create
application.yml:
spring:
datasource:
url: jdbc:postgresql://localhost:5432/greeting_db
username: postgres
jpa:
hibernate:
ddl-auto: create
Vantagens do YAML:
- Mais legível para estruturas hierárquicas
- Menos repetição
- Formato moderno
6. Exercício 3: Simplificação com Lombok
Enunciado
Remova os getters, setters e construtores manuais utilizando Lombok.
Objetivo
Reduzir o código boilerplate com Lombok.
Pré-requisitos
Etapa 1: Adicionar a dependência Lombok no pom.xml
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
Etapa 2: Instalar a extensão Lombok no VS Code
- Extensões → Pesquisar "Lombok"
- Instalar "Lombok Annotations Support for VS Code"
Instruções
Modificar Greeting.java:
package com.example.demo;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "greeting")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Greeting {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "content", nullable = false)
private String content;
// Lombok gera automaticamente:
// - Getters
// - Setters
// - toString()
// - equals() e hashCode()
// - Construtor vazio
// - Construtor com todos os argumentos
}
Anotações Lombok:
| Anotação | Gera |
|---|---|
@Data | Getters, Setters, toString, equals, hashCode |
@NoArgsConstructor | Construtor vazio |
@AllArgsConstructor | Construtor com todos os campos |
@Getter | Apenas os getters |
@Setter | Apenas os setters |
Teste
- Recompile
- Verifique se a aplicação funciona
- Os métodos são gerados em tempo de compilação
7. Exercício 4: Recriação Automática do Banco
Enunciado
Configure a aplicação para que ela recrie automaticamente o banco de dados a cada inicialização.
Objetivo
Entender as diferentes estratégias de gerenciamento de schema.
Instruções
No application.properties (ou application.yml):
spring.jpa.hibernate.ddl-auto=create-drop
ou
spring:
jpa:
hibernate:
ddl-auto: create-drop
Comparação das Estratégias
| Estratégia | Comportamento | Uso |
|---|---|---|
none | Nenhuma ação | Produção |
validate | Valida o schema sem modificar | Produção |
update | Atualiza o schema | Desenvolvimento |
create | Cria o schema na inicialização | Testes |
create-drop | Cria na inicialização, remove ao parar | Testes unitários |
Atenção
create-drop remove todos os dados ao parar a aplicação!
Uso recomendado:
# Desenvolvimento
spring:
jpa:
hibernate:
ddl-auto: update
# Produção
spring:
jpa:
hibernate:
ddl-auto: validate
8. Exercício 5: Migração para MySQL, SQL Server ou Oracle
Enunciado
Refaça a integração completa com outro banco de dados.
Escolha do Banco de Dados
Opção A: MySQL
Dependência no pom.xml:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
Configuração application.properties:
spring.datasource.url=jdbc:mysql://localhost:3306/greeting_db?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto=update
Criação do banco no MySQL:
CREATE DATABASE greeting_db;
CREATE USER 'root'@'localhost' IDENTIFIED BY 'root';
GRANT ALL PRIVILEGES ON greeting_db.* TO 'root'@'localhost';
FLUSH PRIVILEGES;
Opção B: SQL Server
Dependência no pom.xml:
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<scope>runtime</scope>
</dependency>
Configuração application.properties:
spring.datasource.url=jdbc:sqlserver://localhost:1433;databaseName=greeting_db
spring.datasource.username=sa
spring.datasource.password=YourStrong@Password
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLServerDialect
spring.jpa.hibernate.ddl-auto=update
Criação do banco no SQL Server:
CREATE DATABASE greeting_db;
GO
ALTER LOGIN sa WITH PASSWORD = 'YourStrong@Password';
GO
USE greeting_db;
GO
Opção C: Oracle
Dependência no pom.xml:
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<scope>runtime</scope>
</dependency>
Configuração application.properties:
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:xe
spring.datasource.username=system
spring.datasource.password=oracle
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.Oracle12cDialect
spring.jpa.hibernate.ddl-auto=update
Criação do banco no Oracle:
CREATE USER greeting_user IDENTIFIED BY oracle;
GRANT ALL PRIVILEGES TO greeting_user;
Tabela Comparativa
| Banco de Dados | Porta Padrão | Dialeto Hibernate | Dificuldade |
|---|---|---|---|
| PostgreSQL | 5432 | PostgreSQLDialect | Fácil |
| MySQL | 3306 | MySQLDialect | Fácil |
| SQL Server | 1433 | SQLServerDialect | Média |
| Oracle | 1521 | Oracle12cDialect | Difícil |
Instruções Gerais
- Instale o banco de dados escolhido
- Altere o pom.xml com o driver correto
- Altere o application.properties com a configuração correta
- Crie o banco de dados e o usuário
- Recompile e teste
Resolução de Problemas
Problema 1: Connection Refused
Sintoma:
org.postgresql.util.PSQLException: Connection to localhost:5432 refused
Soluções:
- Verifique se o PostgreSQL está iniciado:
Windows:
# No Serviços, procure por PostgreSQL
services.msc
Linux:
sudo systemctl status postgresql
sudo systemctl start postgresql
- Verifique a porta em
postgresql.conf
Problema 2: Authentication Failed
Sintoma:
PSQLException: FATAL: password authentication failed for user "postgres"
Soluções:
- Verifique a senha em
application.properties - Redefina a senha:
ALTER USER postgres WITH PASSWORD 'nova_senha';
Problema 3: Database Does Not Exist
Sintoma:
PSQLException: FATAL: database "greeting_db" does not exist
Solução:
Crie o banco de dados:
CREATE DATABASE greeting_db;
Problema 4: Table Already Exists
Sintoma:
Table "greeting" already exists
Solução:
Altere a estratégia DDL:
spring.jpa.hibernate.ddl-auto=update
Problema 5: Lombok Não Funciona
Sintoma:
error: cannot find symbol
method getId()
Soluções:
- Instale a extensão Lombok no VS Code
- Reinicie o VS Code
- Verifique se a dependência está no pom.xml
- Recompile:
mvn clean install -DskipTests spring-boot:run
Perguntas de Revisão
-
Qual a diferença entre Jakarta e Javax Persistence?
-
O que faz a anotação @Entity?
-
Qual a diferença entre create, update e validate para ddl-auto?
-
Como o Spring Data JPA gera a implementação do repository?
-
O que faz a anotação @GeneratedValue(strategy = GenerationType.IDENTITY)?
-
Por que é necessário um construtor vazio em uma entidade JPA?
-
O que a anotação @Data do Lombok gera?
-
Qual configuração usar em produção para ddl-auto?
Entregáveis
A entregar:
- Código fonte completo com PostgreSQL funcional
- Capturas de tela mostrando:
- A aplicação em execução
- Os dados no pgAdmin
- As queries SQL no console
- Pelo menos 2 exercícios concluídos
- Documento de respostas para as perguntas de revisão
Critérios de Avaliação
| Critério | Pontos |
|---|---|
| Integração PostgreSQL funcional | 25% |
| Entidade Greeting corretamente anotada | 20% |
| Repository criado e utilizado | 15% |
| Dados persistidos no PostgreSQL | 20% |
| Exercícios concluídos | 10% |
| Perguntas de revisão respondidas | 10% |
| Total | 100% |
Próximos Passos
No próximo TP, veremos:
- TP3: Criação de uma API CRUD completa
- Operações: Create, Read, Update, Delete
- Tratamento de erros
- Validação de dados