UC02831 · Interfaces entre BD e linguagens
Aulify · Componente Tecnológica

Interfaces entre bases de dados e linguagens de programação

Drivers, ORM, queries parametrizadas
UC02831 · 50 horas · 4,5 pontos Nível 4 · TDS · Ano 3
Aulify · Componente Tecnológica · Cursos Profissionais
UC02831 · Interfaces entre BD e linguagens

Resultados de aprendizagem

  1. Conectar programa a BD via drivers.
  2. Executar queries com parâmetros seguros.
  3. Usar ORM (SQLAlchemy) para abstrair SQL.
  4. Gerir migrações e pool de conexões.
Aulify · Componente Tecnológica · Cursos Profissionais
UC02831 · Interfaces entre BD e linguagens

Conexão directa

Aulify · Componente Tecnológica · Cursos Profissionais
UC02831 · Interfaces entre BD e linguagens

Driver Python · psycopg2

import psycopg2

conn = psycopg2.connect(
    host="localhost",
    database="aulify",
    user="app",
    password=os.environ["DB_PASSWORD"]
)
cur = conn.cursor()

cur.execute("SELECT * FROM Cliente WHERE id = %s", (42,))
row = cur.fetchone()
print(row)

conn.close()

Drivers: psycopg2 (PG), mysql-connector-python (MySQL), sqlite3 (builtin).

Aulify · Componente Tecnológica · Cursos Profissionais
UC02831 · Interfaces entre BD e linguagens

Prepared statements

# ERRADO — SQL injection!
nome = "Ana' OR '1'='1"
cur.execute(f"SELECT * FROM users WHERE nome = '{nome}'")

# CORRECTO — placeholder
cur.execute("SELECT * FROM users WHERE nome = %s", (nome,))

Driver escapa automaticamente. Regra de ouro.

Aulify · Componente Tecnológica · Cursos Profissionais
UC02831 · Interfaces entre BD e linguagens

Cursor patterns

# Fetch one
cur.execute("SELECT * FROM Cliente WHERE id = %s", (1,))
row = cur.fetchone()

# Fetch all
cur.execute("SELECT * FROM Cliente")
rows = cur.fetchall()

# Iterar (streaming)
cur.execute("SELECT * FROM Cliente")
for row in cur:
    process(row)

# Como dict (DictCursor)
from psycopg2.extras import RealDictCursor
cur = conn.cursor(cursor_factory=RealDictCursor)
cur.execute("SELECT id, nome FROM Cliente WHERE id = %s", (1,))
print(cur.fetchone()["nome"])
Aulify · Componente Tecnológica · Cursos Profissionais
UC02831 · Interfaces entre BD e linguagens

Transações

try:
    cur.execute("UPDATE Conta SET saldo = saldo - 100 WHERE id = 1")
    cur.execute("UPDATE Conta SET saldo = saldo + 100 WHERE id = 2")
    conn.commit()
except Exception:
    conn.rollback()
    raise
finally:
    cur.close()
    conn.close()

with conn: faz commit/rollback automático em saída do bloco.

Aulify · Componente Tecnológica · Cursos Profissionais
UC02831 · Interfaces entre BD e linguagens

ORM · SQLAlchemy

Aulify · Componente Tecnológica · Cursos Profissionais
UC02831 · Interfaces entre BD e linguagens

Definir modelo

from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import declarative_base, relationship, sessionmaker

Base = declarative_base()
engine = create_engine("postgresql://app:pwd@localhost/aulify")
Session = sessionmaker(bind=engine)

class Cliente(Base):
    __tablename__ = "cliente"
    id = Column(Integer, primary_key=True)
    nome = Column(String(150), nullable=False)
    email = Column(String(150), unique=True)
    encomendas = relationship("Encomenda", back_populates="cliente")

class Encomenda(Base):
    __tablename__ = "encomenda"
    id = Column(Integer, primary_key=True)
    cliente_id = Column(Integer, ForeignKey("cliente.id"))
    total = Column(Float)
    cliente = relationship("Cliente", back_populates="encomendas")
Aulify · Componente Tecnológica · Cursos Profissionais
UC02831 · Interfaces entre BD e linguagens

CRUD com ORM

session = Session()

# Create
c = Cliente(nome="Ana", email="ana@ex.pt")
session.add(c)
session.commit()

# Read
ana = session.query(Cliente).filter_by(email="ana@ex.pt").first()
todos = session.query(Cliente).all()

# Update
ana.nome = "Ana Silva"
session.commit()

# Delete
session.delete(ana)
session.commit()
Aulify · Componente Tecnológica · Cursos Profissionais
UC02831 · Interfaces entre BD e linguagens

Queries

# Filtros
session.query(Cliente).filter(Cliente.nome.like("A%")).all()

# Ordenação
session.query(Cliente).order_by(Cliente.nome).limit(10).all()

# Agregações
from sqlalchemy import func
session.query(func.count(Cliente.id)).scalar()

# JOIN
results = (session.query(Cliente, Encomenda)
                  .join(Encomenda)
                  .filter(Encomenda.total > 100)
                  .all())

# Eager loading (evita N+1)
session.query(Cliente).options(joinedload(Cliente.encomendas)).all()
Aulify · Componente Tecnológica · Cursos Profissionais
UC02831 · Interfaces entre BD e linguagens

Migrações

Aulify · Componente Tecnológica · Cursos Profissionais
UC02831 · Interfaces entre BD e linguagens

Alembic

Alembic gere mudanças de schema versionadas.

# Inicializar
alembic init migrations

# Gerar migração automática
alembic revision --autogenerate -m "Adicionar tabela Produto"

# Aplicar
alembic upgrade head

# Reverter
alembic downgrade -1

Cada migração é um ficheiro Python com upgrade() e downgrade().

Aulify · Componente Tecnológica · Cursos Profissionais
UC02831 · Interfaces entre BD e linguagens

N+1 problem

# MAU: N+1 queries
clientes = session.query(Cliente).all()
for c in clientes:
    print(c.nome, len(c.encomendas))  # query extra por cada cliente

# Com 100 clientes: 1 + 100 = 101 queries.

Solução: eager loading

clientes = session.query(Cliente).options(
    joinedload(Cliente.encomendas)
).all()
# Apenas 1 query com JOIN
Aulify · Componente Tecnológica · Cursos Profissionais
UC02831 · Interfaces entre BD e linguagens

Outros padrões

Aulify · Componente Tecnológica · Cursos Profissionais
UC02831 · Interfaces entre BD e linguagens

Connection pool

Abrir conexão é caro. Usar pool de conexões reutilizáveis.

engine = create_engine(
    "postgresql://...",
    pool_size=10,
    max_overflow=20,
    pool_pre_ping=True,
)
  • pool_size — conexões mantidas abertas.
  • max_overflow — adicionais temporárias.
  • pool_pre_ping — verifica conexão antes de usar.
Aulify · Componente Tecnológica · Cursos Profissionais
UC02831 · Interfaces entre BD e linguagens

NoSQL

Bases não-relacionais para casos específicos:

  • MongoDB — documentos JSON.
  • Redis — key-value, cache em RAM.
  • Elasticsearch — pesquisa full-text.
  • Neo4j — grafos.

Usar quando o modelo relacional não encaixa bem (ex.: logs hierárquicos, dados muito flexíveis).

Aulify · Componente Tecnológica · Cursos Profissionais
UC02831 · Interfaces entre BD e linguagens
Aulify · UC02831 · Fim

Código + BD = onde a aplicação ganha vida.

📧 geral@aulify.pt
Aulify · Componente Tecnológica · Cursos Profissionais