Este projeto implementa um CRUD completo utilizando JDBC e PostgreSQL, sem o auxílio de frameworks como Spring Data ou Hibernate.
Além da implementação proposta pelo curso, este projeto recebeu uma série de melhorias arquiteturais desenvolvidas por iniciativa própria, incluindo a utilização de Generics, abstração da camada DAO, centralização de consultas SQL compartilhadas e otimização da suíte de testes.
Objetivos do projeto
- Implementar um CRUD completo utilizando JDBC
- Praticar SQL e PostgreSQL integrado ao Java
- Abstração de Classe DAO
- Aplicar Generics para reduzir duplicação de código (DRY)
- Criar testes automatizados via JUnit
O projeto foi estruturado com foco na camada de persistência, concentrando o estudo na implementação do padrão DAO e na comunicação direta entre Java e PostgreSQL através do JDBC.
src
├── main
│ ├── factory
│ │ └── ConnectionFactory
│ │
│ ├── jdbc
│ │ ├── dao
│ │ │ ├── InterfaceDAO<T>
│ │ │ ├── AbstractDAO<T>
│ │ │ ├── ClienteDAO
│ │ │ └── ProdutoDAO
│ │ │
│ │ └── domain
│ │ ├── Cliente
│ │ └── Produto
│ │
│ ├── service
│ └── Main
│
├── resources
│ ├── config
│ │ └── database.properties
│ │
│ └── database
│ ├── schemaCliente.sql
│ └── schemaProduto.sql
│
└── test
├── ClienteTest
├── ProdutoTest
└── SuiteTest
| Pacote | Responsabilidade |
|---|---|
| factory | Centraliza a criação da conexão com o PostgreSQL |
| jdbc/dao | Implementação do padrão DAO utilizando Interfaces, Generics e uma Classe Abstrata |
| jdbc/domain | Entidades utilizadas pela aplicação |
| resources/config | Configuração da conexão com o banco de dados |
| resources/database | Scripts SQL responsáveis pela criação automática das tabelas |
| test | Testes automatizados das operações CRUD utilizando JUnit |
Os scripts responsáveis pela criação das tabelas ficam armazenados em resources/database.
Sempre que um DAO é inicializado, sua implementação verifica a existência do schema correspondente e executa automaticamente os comandos SQL necessários.
Dessa forma, basta criar um banco PostgreSQL vazio e configurar a conexão para que toda a estrutura seja criada sem depender de ferramentas externas.
CREATE SEQUENCE IF NOT EXISTS SQ_CLIENTE
START WITH 1 INCREMENT BY 1;
CREATE TABLE IF NOT EXISTS TB_CLIENTE (
id BIGINT NOT NULL DEFAULT nextval('SQ_CLIENTE'),
nome VARCHAR(50) NOT NULL,
codigo VARCHAR(20) NOT NULL,
CONSTRAINT PK_ID_CLIENTE PRIMARY KEY (ID),
CONSTRAINT UK_CODIGO_CLIENTE UNIQUE (CODIGO)
);public abstract class AbstractDAO<T> {
/// ÁREA DE CONEXÃO
protected void executarSchema(Connection connection) throws Exception {
InputStream input = getClass()
.getClassLoader()
.getResourceAsStream(getSchema());
if (input == null) {
throw new RuntimeException("ARQUIVO .sql NÃO ENCONTRADO NA PASTA RESOURCE.");
}
String sql;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) {
sql = reader.lines().collect(Collectors.joining("\n"));
}
String[] comandos = sql.split(";");
for (String comando : comandos) {
if (!comando.isBlank()) {
try (PreparedStatement statement = connection.prepareStatement(comando)) {
statement.execute();
}
}
}
}
{...}
}O método abstrato getSchema() permite que o diretório do .sql correto seja definido diretamente na camada DAO, continuando válido para reutilização em quantos bancos o projeto precisar.
InterfaceDAO<T>
▲
AbstractDAO<T>
▲
ClienteDAO | ProdutoDAOA implementação utiliza uma Interface Genérica para definir o contrato das operações CRUD e uma Classe Abstrata responsável por concentrar toda a lógica compartilhada entre os DAOs, reduzindo código repetitivo (boilerplate) através dos princípios de reutilização e DRY (Don’t Repeat Yourself).
| Implementação | Objetivo |
|---|---|
| Generics | Tornar o contrato DAO reutilizável |
| AbstractDAO | Centralizar lógica compartilhada |
| SQL reutilizável | Evitar duplicação entre entidades |
| Criação automática do schema | Facilitar execução em qualquer ambiente |
| Refatoração dos testes | Redução de boilerplate utilizando métodos auxiliares |
-
Clone o repositório
-
Crie um banco PostgreSQL vazio
-
Configure as variáveis de ambiente
-
Execute os testes
Embora este projeto cumpra seus objetivos como estudo de JDBC e persistência de dados, ele também estabelece uma base sólida para futuras evoluções arquiteturais:
- Service Layer (separar regras de negócio)
- Controller Layer (organizar o fluxo da aplicação)
- Interface gráfica com Java Swing (apresentação)
- Migração para Spring Boot (comparação entre arquitetura manual e framework)
A intenção não é apenas expandir suas funcionalidades, mas compreender como aplicações Java podem evoluir gradualmente de uma arquitetura “vanilla” para soluções mais completas, incorporando novas camadas, interfaces gráficas e frameworks sem perder de vista os conceitos fundamentais estudados neste projeto.