DAO – Data Access Object – Pattern – CRUD – Oracle – IBM DB2 – MSSQL Server – Python

Publicado: 13 de dezembro de 2014 em Python

No ciência da computação, Data Access Object é um padrão para um objeto que fornece uma interface abstrata para algum tipo de banco de dados ou outro mecanismo de persistência e que permite separar regras de negócio das regras de acesso a banco de dados. A vantagem de usar objetos de acesso a dados é a separação simples e rigorosa entre duas partes importantes de uma aplicação que não devem e não podem conhecer quase que nada uma da outra, e que podem evoluir frequentemente e independentemente.

O DAO implementa o mecanismo de acesso necessário para trabalhar com a fonte de dados. A fonte de dados pode ser um armazenamento persistente como um RDBMS, um serviço externo, como uma troca de B2B, um repositório como um banco de dados LDAP, um serviço de negócios acessado via CORBA, Internet Inter-ORB Protocol (IIOP) ou soquetes de baixo nível. O componente de negócio que se baseia no DAO usa a interface mais simples exposta pelo DAO para seus clientes.

DAO e Python

O DAO é um Pattern da Oracle J2EE para Java, entretanto se tornou um padrão muito popular na programação orientada a objeto, pois separa a logica de negocio da logica do banco de dados e consequentemente da interface com o usuário. Apesar das diferenças entre a linguagem orientada a objeto Java e Python o DAO pode ser usado sem problemas com algumas variações e sendo um padrão flexível e intuitivo é bem possível que você já o utilize nos seus códigos orientados a objeto.

DAO - CRUD - Python

DAO – CRUD – Python

DAO Pattern

O DAO é um padrão flexível e não uma regra ele é utilizado em conjunto com Factory Pattern e Broker Pattern (veja Patterns relacionados no link abaixo) por isto é comum você encontrar algumas variações de sua implementação, o padrão possui quatro especificações para que possa ser utilizado corretamente elas são: DataAccessObject, DataSourceObject, BussinesObject e TransferObject.

  1. DataAccessObjecté o objeto principal desse padrão. O Data Access Object abstrai a implementação fundamental de acesso a dados para o Business Objects para permitir acesso transparente à fonte de dados.
  2. DataSourceObjectrepresenta uma implementação da fonte de dados. Uma fonte de dados pode ser uma base de dados tal como um RDBMS, repositório XML, e assim por diante.
  3. BussinesObject – o objeto de negócios representa o dados do cliente. É o objeto que requer acesso à fonte de dados para obter e armazenar dados.
  4. TransferObjectrepresenta um objeto de transferência usado como um suporte de dados. O Data Access Object pode usar um objeto de transferência para retornar dados para o cliente. O Data Access Object também pode receber os dados do cliente em um objeto de transferência para atualizar os dados na fonte de dados.

DAO Pattern: http://www.oracle.com/technetwork/java/dataaccessobject-138824.html

CRUD – DAO

CRUD acrónimo de Create, Read, Update e Delete, utiliza as quatro operações básicas  para criação, consulta, atualização e exclusão de dados.

Através do padrão DAO vamos utilizar as operações CRUD para manipular dados no banco de dados. O DAO é um Pattern J2EE, mas como ele separa as regras de negocio do acesso a dados também podemos utiliza-lo com qualquer tipo de interface, seja pela linha de comando, uma GUI desktop ou aplicações web.

Para usar o DAO você utiliza o Factory Pattern para criar uma abstração para o acesso a dados e uma classe concreta para seu tipo de acesso como por exemplo OracleDAOFactory, DB2DAOFactory e assim por diante. Então você pode criar a interface para seu objeto DAO chamada DAOFuncionario e a partir da interface (Python não possui interfaces)  você pode criar as classes DAO como, DAOFuncionarioOracle, DAOFuncionarioDB2 e utiliza o objeto de transferência para manipular dados.

Neste exemplo não criamos uma classe abstrata utilizando o Pattern Factory Method, cortamos caminho pois utilizamos apenas um objeto DAO para todos os bancos de dados, mas caso deseje criar uma abstração para classes concretas para cada banco de dados siga a especificação oficial do DAO Pattern no link logo acima.

DAO - Python Packages

DAO – Python Packages

Utilize a imagem acima para ver a organização das Packges para cada classe e atente-se aos drivers utilizados para cada banco de dados.

Exemplo:

Neste exemplo usamos o Pattern DAO para criar operações CRUD utilizando uma interface gráfica, como este exemplo não possuí uma tela de login mude o acesso ao banco e usuário e senha diretamente na classe DAConexaoFactory.

O DAO  pode ser utilizado para varias soluções como no exemplo abaixo para enfatizar sua eficiência utilizamos o DAO em uma interface gráfica escrita com Tkinter justamente para mostrar a independência do negocio e acesso a dados.

Este é o terceiro método que disponibilizamos para manipular dados através de um banco de dados, para ver este mesmo programa utilizando SQL direto da aplicação clique aqui.

Pare ver este programa usando objetos no banco de dados como Views e Triggers clique aqui ou procure na busca também por Stored Procedures e cursores.

Ainda veremos outros métodos para manipular dados através de frameworks específicos.

SQL

Oracle

-- Cria tabela de funcionarios
create table Funcionarios(
  ID_Funcionario  NUMBER(5),
  Nome            VARCHAR2(30),
  Sobrenome       VARCHAR2(70),
  Cargo           VARCHAR2(30),
  Salario         NUMBER(9,2));

DB2

-- Cria tabela de funcionarios
create table Funcionarios (
    ID_Funcionario  INTEGER,
    Nome            VARCHAR(30),
    Sobrenome       VARCHAR(70),
    Cargo           VARCHAR(30),
    Salario         NUMERIC(9,2));

MSSQL

-- Cria tabela de funcionarios
create table Funcionarios (
   ID_Funcionario  Int,
   Nome            VARCHAR(30),
   Sobrenome       VARCHAR(70),
   Cargo           VARCHAR(30),
   Salario         Decimal(9,2));

Python

Objeto – Negócio

# coding=utf-8
# Desenvolvimento Aberto
# Funcionario.py

__author__ = 'Desenvolvimento Aberto'

class Funcionario():

    # Define atributos privados
    def __init__(self):
        self.__id = None
        self.__nome = None
        self.__sobrenome = None
        self.__cargo = None
        self.__salario = None

    # Define métodos Getter e Setter
    # Você também pode optar por propriedades
    def getId(self):
        return self.__id

    def setId(self, id):
        self.__id = id

    def getNome(self):
        return self.__nome

    def setNome(self, nome):
        self.__nome = nome

    def getSobrenome(self):
        return self.__sobrenome

    def setSobrenome(self, sobrenome):
        self.__sobrenome = sobrenome

    def getCargo(self):
        return self.__cargo

    def setCargo(self, cargo):
        self.__cargo = cargo

    def getSalario(self):
        return self.__salario

    def setSalario(self, salario):
        self.__salario = salario

Factory Pattern – DAConexaoFactory

# coding=utf-8
# Desenvolvimento Aberto
# DAConexaoFactory.py

__author__ = 'Desenvolvimento Aberto'

# Importa fonte de dados
import cx_Oracle
import ibm_db
import odbc

class DAConexaoFactory():

    # Define atributos privados

    def __init__(self):
        self.__ORACLE = 1
        self.__DB2 = 2
        self.__MSSQL = 3
        self.__erroCon = None
        self.__factory = None
        self.__IBMDriver = None # IBM DB2 driver (ibm_db)

    # Cria Factory para objetos
    def getConexao(self, banco):

        # Define conexão e fonte de dados
        con = None
        self.__factory = banco

        # Cria string de conexão Oracle
        if (banco == self.__ORACLE):
            sconexao = "user/pass@localhost/XE"
            try:
                con = cx_Oracle.connect(sconexao)
            except Exception, e:
                self.__erroCon = str(e)

        # Cria string de conexão IBM DB2
        if (banco == self.__DB2):
            sconexao = "DATABASE=DEVA" + \
                       ";HOSTNAME=localhost;PORT=50000;PROTOCOL=TCPIP;" + \
                       "UID=user;" + \
                       "PWD=pass"
            try:
                self.__IBMDriver = ibm_db
                con = ibm_db.connect(sconexao, "", "")
            except Exception, e:
                self.__erroCon = str(e)

        # Cria string de conexão MSSQL
        if (banco == self.__MSSQL):
            sconexao = "MSSQLSERVER/user/pass"
            try:
                con = odbc.odbc(sconexao)
            except Exception, e:
                self.__erroCon = str(e)

        return con

    # Retorna Erros
    def getErros(self):
        return self.__erroCon

    # Retorna Factory da conexão
    def getFactory(self):
        return self.__factory

    # Retorna Driver da IBM (Oracle e MSSQL possui outro padrão)
    def getIbmDriver(self):
        return self.__IBMDriver

Objeto DAO – FuncionarioDAO

# coding=utf-8
# Desenvolvimento Aberto
# FuncionarioDAO.py

__author__ = 'Desenvolvimento Aberto'

# Importa pacotes DAO
from DesenvolvimentoAberto.Modelo.Funcionario import Funcionario
from DesenvolvimentoAberto.Conexao.DAConexaoFactory import DAConexaoFactory

class FuncionarioDAO():

    # Construtor da classe
    def __init__(self):
        self.__erro = None
        self.__con = None
        self.__factory = None
        self.__IBMDriver = None
        try:
            # Cria Conexão com o Factory Method Pattern
            # Você pode ter uma classe para cada fonte de dados
            # Unimos as três fontes para o exemplo
            conexao = DAConexaoFactory()
            self.__con = conexao.getConexao(3)
            self.__factory = conexao.getFactory()
            self.__IBMDriver = conexao.getIbmDriver()
        except Exception, e:
            self.__erro = str(e)

    # Metodo de Manipulação de dados

    def buscaFuncionario(self, id):

        # Cria instancia do objeto
        funcionario = Funcionario()

        # Define SQL
        sql = "SELECT * FROM FUNCIONARIOS WHERE ID_FUNCIONARIO = " + str(id)

        # Executa SQL
        try:
            if (self.__factory != 2):
                cursor= self.__con.cursor()
                cursor.execute(sql)
                dados = cursor.fetchone()

            else:
                cursor = self.__IBMDriver.exec_immediate(self.__con, sql)
                dados  = self.__IBMDriver.fetch_tuple(cursor)

        except Exception, e:
            self.__erro = str(e)

        # Alimenta objeto
        funcionario.setId(dados[0])
        funcionario.setNome(dados[1])
        funcionario.setSobrenome(dados[2])
        funcionario.setCargo(dados[3])
        funcionario.setSalario(dados[4])

        # Retorna Objeto
        return funcionario

    def insereFuncionario(self, funcionario ):

        # Define SQL
        # Troca decimal é requerido se Oracle/DB2 for ptbr.
        sql = "INSERT INTO FUNCIONARIOS VALUES (" + \
              funcionario.getId() + ", '" + \
              funcionario.getNome() + "', '" + \
              funcionario.getSobrenome() + "', '" + \
              funcionario.getCargo() + "', " + \
              str(funcionario.getSalario()).replace(",",".") + ")"

        # Executa SQL
        try:
             if (self.__factory != 2):
                 cursor=self.__con.cursor()
                 cursor.execute(sql)
                 self.__con.commit()
                 return True
             else:
                 cursor = self.__IBMDriver.exec_immediate(self.__con, sql)
                 return True

        except Exception, e:
            self.__erro = str(e)
            return False

    def alteraFuncionario(self, funcionario):

       #  Define SQL
       sql = "UPDATE FUNCIONARIOS SET " + \
             "ID_FUNCIONARIO = " + funcionario.getId() + ", " + \
             "NOME = '" + funcionario.getNome() + "', " + \
             "SOBRENOME = '" + funcionario.getSobrenome() + "', " + \
             "CARGO = '" + funcionario.getCargo() + "', " + \
             "SALARIO = " + str(funcionario.getSalario()).replace(",",".") + \
             " WHERE ID_FUNCIONARIO = " + funcionario.getId()

       # Executa SQL
       try:
           if (self.__factory != 2):
               cursor=self.__con.cursor()
               cursor.execute(sql)
               self.__con.commit()
               return True
           else:
               cursor = self.__IBMDriver.exec_immediate(self.__con, sql)
               return True

       except Exception, e:
           self.__erro = str(e)
           return False

    def apagarFuncionario(self, funcionario):

        # Define SQL
        sql = "DELETE FROM FUNCIONARIOS WHERE ID_FUNCIONARIO = " + funcionario.getId()

        # Executa SQL
        try:
            if (self.__factory != 2):
                cursor=self.__con.cursor()
                cursor.execute(sql)
                self.__con.commit()
                return True
            else:
                cursor = self.__IBMDriver.exec_immediate(self.__con, sql)
                return True

        except Exception, e:
            self.__erro = str(e)
            return False

    # Retorna Erro
    def getErro(self):
        return self.__erro

GUI – DADaoApp

#!/usr/bin/env python
# coding=utf-8
# Desenvolvimento Aberto
# DADaoApp.py

# importa modulos
from Tkinter import *
from DesenvolvimentoAberto.Modelo.Funcionario import Funcionario
from DesenvolvimentoAberto.Dao.FuncionarioDAO import FuncionarioDAO
import tkMessageBox

class DADaoApp(Frame):

    def __init__(self, formulario=None):

        # Define formulario
        Frame.__init__(self, formulario)

        # Cria Widgets
        self.titulo = Label(formulario, text="DAO - Pattern")
        self.separador1 = Frame(height=2, bd=1, relief=SUNKEN)
        self.separador2 = Frame(height=2, bd=1, relief=SUNKEN)

        # labels
        self.lcodigo = Label(formulario, text="Codigo:")
        self.lpnome = Label(formulario, text="Nome:")
        self.lsnome = Label(formulario, text="Sobrenome:")
        self.lcargo = Label(formulario, text="Cargo:")
        self.lsalario = Label(formulario, text="Salario:")

        # Entry
        self.tcodigo = Entry(formulario, width=20)
        self.tpnome = Entry(formulario, width=50)
        self.tsnome = Entry(formulario, width=45)
        self.tcargo = Entry(formulario, width=35)
        self.tsalario = Entry(formulario, width=30)

        # Pesquisa
        self.lpesquisa = Label(formulario, text="Pesquisa:")
        self.tpesquisa = Entry(formulario)
        self.botao = Button(formulario, text="Pesquisar", command=self.on_Pesquisar)

        # Ações
        self.painel = Frame()
        self.bnovo = Button(self.painel, text="Novo", command=self.on_novo, width=8)
        self.binserir = Button(self.painel, text="Inserir", command=self.on_inserir, width=8)
        self.balterar = Button(self.painel, text="Alterar", command=self.on_alterar, width=8)
        self.bapagar = Button(self.painel, text="Apagar", command=self.on_apagar, width=8)

        # Cria janela para menssagem
        self.janela = Tk()
        self.janela.wm_withdraw()

        # Define Layout
        self.titulo.grid(row=0, sticky=W+E+N+S, pady=20)
        self.separador1.grid(row=1, sticky=W+E+N+S, columnspan=3)

        # Define Layout Dados
        self.lcodigo.grid(row=3, sticky=W, padx=20)
        self.tcodigo.grid(row=3, column=1, pady=5, sticky=W)
        self.lpnome.grid(row=4, sticky=W, padx=20)
        self.tpnome.grid(row=4, column=1, pady=5, sticky=W)
        self.lsnome.grid(row=5, sticky=W, padx=20)
        self.tsnome.grid(row=5, column=1, pady=5, sticky=W)
        self.lcargo.grid(row=6, sticky=W, padx=20)
        self.tcargo.grid(row=6, column=1, pady=5, sticky=W)
        self.lsalario.grid(row=7, sticky=W, padx=20)
        self.tsalario.grid(row=7, column=1, pady=5, sticky=W)

        # Layout pesquisa
        self.lpesquisa.grid(row=2, column=0, pady=20)
        self.tpesquisa.grid(row=2, column=1, pady=20)
        self.botao.grid(row=2, column=2, padx=10, pady=20)

        # Loayout Ações
        self.bnovo.grid(row=1, column=0, pady=10, padx=5)
        self.binserir.grid(row=1, column=1, pady=10, padx=5)
        self.balterar.grid(row=1, column=2, pady=10, padx=5)
        self.bapagar.grid(row=1, column=3, pady=10, padx=5)
        self.separador2.grid(row=9, sticky=W+E, columnspan=3, pady=10)
        self.painel.grid(row=10, sticky=W+E+S, column=1, columnspan=1)

    # Limpa campo
    def limpar(self):
        self.tcodigo.delete(0, END)
        self.tpnome.delete(0, END)
        self.tsnome.delete(0, END)
        self.tcargo.delete(0, END)
        self.tsalario.delete(0, END)

    # Evento do botão
    def on_Pesquisar(self):

        # Cria objetos Modelo e DAO
        funcionario = Funcionario()
        pesquisar = FuncionarioDAO()

        # Executa trasferencia de Objetos
        try:
            funcionario = pesquisar.buscaFuncionario(self.tpesquisa.get())
        except ValueError:
            tkMessageBox.showinfo(title="Erro", message=pesquisar.getErro(), parent=self.janela)

        # Exibe dados
        self.limpar()
        self.tcodigo.insert(0, str(funcionario.getId()))
        self.tpnome.insert(0, funcionario.getNome())
        self.tsnome.insert(0, funcionario.getSobrenome())
        self.tcargo.insert(0, funcionario.getCargo())
        self.tsalario.insert(0, str(funcionario.getSalario()))

    # limpa widgets
    def on_novo(self):
        self.limpar()
        self.tcodigo.focus()

    # Insere dados
    def on_inserir(self):

        # Cria objeto Modelo
        funcionario = Funcionario()

        # Alimenta dados ao objeto
        funcionario.setId(self.tcodigo.get())
        funcionario.setNome(self.tpnome.get())
        funcionario.setSobrenome(self.tsnome.get())
        funcionario.setCargo(self.tcargo.get())
        funcionario.setSalario(self.tsalario.get())

        # Cria objeto DAO
        inserir = FuncionarioDAO()

        # Executa trasferencia de Objetos
        resultado = inserir.insereFuncionario(funcionario)

        # Exibe Resultado
        if (resultado):
            tkMessageBox.showinfo(title="Menssagem", message="Dados inseridos com suscesso!", parent=self.janela)
        else:
            tkMessageBox.showinfo(title="Erro", message=inserir.getErro(), parent=self.janela)

    # Altera dados
    def on_alterar(self):
        # Cria objeto Modelo
        funcionario = Funcionario()

        # Alimenta objeto
        funcionario.setId(self.tcodigo.get())
        funcionario.setNome(self.tpnome.get())
        funcionario.setSobrenome(self.tsnome.get())
        funcionario.setCargo(self.tcargo.get())
        funcionario.setSalario(self.tsalario.get())

        # Cria objeto DAO
        alterar = FuncionarioDAO()

        # # Executa trasferencia de Objetos
        resultado = alterar.alteraFuncionario(funcionario)

        # Exibe resultado
        if (resultado):
            tkMessageBox.showinfo(title="Menssagem", message="Dados alterados com suscesso!", parent=self.janela)
        else:
            tkMessageBox.showinfo(title="Erro", message=alterar.getErro(), parent=self.janela)

    # Exclui dados
    def on_apagar(self):

        # Cria objeto Modelo
        funcionario = Funcionario()

        # Alimenta objeto
        funcionario.setId(self.tcodigo.get())
        funcionario.setNome(self.tpnome.get())
        funcionario.setSobrenome(self.tsnome.get())
        funcionario.setCargo(self.tcargo.get())
        funcionario.setSalario(self.tsalario.get())

        #Cria objeto DAO
        apagar = FuncionarioDAO()

        # Executa trasferencia de Objetos
        resultado = apagar.apagarFuncionario(funcionario)

        # Exibe resultado
        if (resultado):
            tkMessageBox.showinfo(title="Menssagem", message="Dados apagados com suscesso!", parent=self.janela)
            self.limpar()
        else:
            tkMessageBox.showinfo(title="Erro", message=apagar.getErro(), parent=self.janela)

# loop do tcl
root = Tk()
root.title('DA - DAO - Data Access Object')
app = DADaoApp(formulario=root)
app.mainloop()
root.destroy()

 

Deixe um comentário