«

»

Apr 02

Print this Post

Implementando a Camada DAO

Continuando o artigo anterior Implementando uma arquitetura em camadas, neste artigo vamos demonstrar na prática toda a definição que expliquei no anterior.
Mas antes de começar a codificar as camadas, vamos falar de reaproveitamento de código, singleton e etc.
Primeiro quero abordar, que escolhi o ColdFusion como linguagem de implementação desse artigo, primeiro pelo ColdFusion ser focado em segurança, ter diversos facilitadores e por eu já ter um projeto pessoal em andamento seguindo fielmente esse formato de arquitetura.

Para começar vamos falar de Singleton:
O Singleton é um padrão que garante a criação de somente uma instância de uma classe, impedindo que desnecessariamente a aplicação crie instâncias iguais, gerindo melhor o uso de memória do servidor entre outras coisas.
Tenho por definição, o costume de implementar a camada DAO em Singleton, como é uma das camadas mais usadas e já tem a responsabilidade de se conectar ao banco de dados, costumo deixa-la toda instanciada desde o início da aplicação. Assim é um conjunto de instâncias a menos para criar durante a utilização da aplicação, além de impedir que sejam criadas múltiplas instâncias desnecessariamente.
E o mais legal, é que o ColdFusion tem o Application, que é executado antes de qualquer operação e que nos permite que instanciemos dentro dele esses objetos, tornando-os visíveis para toda a aplicação entre outras coisas.
Sugiro uma pesquisada sobre Application.cfc e Application.cfm para um maior entendimento do Application e seu escopo, também chamado de Application.

Então vamos ver como são os instanciamentos dos DAOs em Singleton no Application.cfc.


<cfcomponent>
<cfscript>
this.name = 'NomeAplicacao';
this.applicationtimeout=CreateTimeSpan(0,0,30,0);
this.sessiontimeout=CreateTimeSpan(0,0,30,0);
this.sessionmanagement = true;
this.clientmanagement=true;
this.loginStorage = 'session';
function OnApplicationStart() {
    // cria instancias dos objetos
    this.getInstances();
    return true;
}
function OnSessionStart(){}
function getInstances() {
    // variáveis de uso interno
    Application.config = StructNew();
    Application.config.datasource = "DATASOURCE_DB";
    Application.instance = StructNew();
    // FACTORY
    Application.instance.Factory = CreateObject('component','libs.Factory');
    // CAMADA DE NEGÓCIO
    Application.instance.dao.Usuarios = CreateObject('component','dao.Usuarios').init(application.config.datasource);
}
</cfscript>
<cffunction name="OnRequestStart">
    <cfargument name="arquivo" />
</cffunction>
<cffunction name="OnRequest">
    <cfargument name="arquivo" />
    <cfinclude template="#arguments.arquivo#">
</cffunction>
</cfcomponent>

Não implementei o Application.cfc por completo, assim como falei no primeiro artigo, não é o foco codificar o que não tem haver com a definição da arquitetura.
Como podemos ver no código, instanciei no Application também em Singleton um objeto chamado Factory, esse objeto será utilizado na aplicação para pegar as instâncias das demais camadas. Ou seja, servirá como uma fábrica de instâncias. Veremos seu funcionamento logo logo.
Ps. Factory ou Abstract Factory é um padrão que cria uma espécie de famílias de objetos, esses objetos devem ser relacionados ou dependentes. E as instâncias desses objetos devem ser carregadas pela mesma, sem que a classe concreta seja referenciada.
Application.cfc definido, instância da entidade usuários implementada, agora vamos a construção da classe que representará a tabela usuários do banco de dados.
Mas antes de implementar o DAO desta entidade, vamos voltar a falar de reutilização de código. Quando começar a implementar suas classes de DAO, verá que em todas elas, terá um método construtor chamado init(), contendo a mesma implementação. E este método foi usado na instância do DAO no Application.cfc e através deste método passamos o datasource que faz a conexão com o banco de dados e com a tabela específica.
E para reaproveitar código e não ter que escrever códigos repetidos em todos os DAOs, faço uso de Herança para criar uma base para meus DAOs, e faço o mesmo para as outras camadas.

Vamos ver o código da classe base do DAO:
DaoBase.cfc


<cffunction name="init" access="package" returntype="void">
    <cfargument name="datasource" type="string" required="true" />
    <cfscript>
    this.datasource = arguments.datasource;
    this.result = "";
    </cfscript>
</cffunction>

A classe base do DAO é a mais curta, já que a implementação dos outros métodos da classe são bem particulares, assim, fica mais complicado de criar métodos bases para os mesmos.
Então optei por fazer somente o método construtor nessa base.
Agora já tendo a classe DaoBase implementada, vamos implementar o DAO Usuarios.cfc

Usuarios.cfc


<cfcomponent displayname="Usuarios" hint="Usuarios" output="false" extends="DaoBase">
<cffunction name="init" access="public" returntype="struct">
    <cfargument name="datasource" type="string" required="yes" />
    <cfscript>
    super.init( datasource : arguments.datasource );
    return this;
    </cfscript>
</cffunction>
<cffunction name="listar" access="public" output="false" returntype="query">
    <cfargument name="usuario" type="any" displayname="usuario" required="false" default="0" />
    <cfscript>
    var nId = 0;
    if( isInstanceOf( arguments.usuario , "dto.Usuarios" ) == true ) {
        nId = arguments.usuario.getId();
    } else if( isNumeric( arguments. usuario ) ) {
        nId = arguments.usuario;
    }
    </cfscript>
    <cfquery name="rsListarUsuarios" datasource="#this.datasource#">
        SELECT id, nome, sobrenome, email, dtcadastro, senha, permicao, confirm
        FROM usuarios
        <cfif nId GT 0>
            WHERE id = <cfqueryparam cfsqltype="cf_sql_integer" value="#nId#" />
        </cfif>
        ORDER BY nome, sobrenome
    </cfquery>
    <cfset this.result = rsListarUsuarios />
    <cfreturn this.result />
</cffunction>
<cffunction name="save" access="public" output="false" returntype="void">
    <cfargument name="usuario" type="dto.Usuarios" displayname="usuario" required="true" />
    <cfquery datasource="#this.datasource#" result="daoResult">
        <cfif arguments.usuario.getId() EQ 0 >
            INSERT INTO usuarios (
                nome, sobrenome, email, dtcadastro, senha, permicao, confirm )
            VALUES(
                <cfqueryparam cfsqltype="cf_sql_char" value="#arguments.user.getNome()#" maxlength="100"/>,
                <cfqueryparam cfsqltype="cf_sql_char" value="#arguments.usuario.getSobrenome()#" maxlength="100" />,
                <cfqueryparam cfsqltype="cf_sql_char" value="#arguments.usuario.getEmail()#" maxlength="255" />,
                <cfqueryparam cfsqltype="cf_sql_date" value="#CreateODBCDate( now() )#" />,
                SHA1(<cfqueryparam cfsqltype="cf_sql_char" value="#arguments.usuario.getSenha()#" maxlength="10" />),
                <cfqueryparam cfsqltype="cf_sql_char" value="#arguments.usuario.getPermicao()#" maxlength="1" />,
                <cfqueryparam cfsqltype="cf_sql_char" value="#arguments.usuario.getConfirm()#" maxlength="1" />
            )
        <cfelse>
            UPDATE usuarios SET
                nome = <cfqueryparam cfsqltype="cf_sql_char" value="#arguments.usuario.getNome()#" maxlength="100"/>,
                sobrenome = <cfqueryparam cfsqltype="cf_sql_char" value="#arguments.usuario.getSobrenome()#" maxlength="100" />,
                email = <cfqueryparam cfsqltype="cf_sql_char" value="#arguments.usuario.getEmail()#" maxlength="255" />,
                senha = SHA1(<cfqueryparam cfsqltype="cf_sql_char" value="#arguments.usuario.getSenha()#" maxlength="10" />),
                permicao = <cfqueryparam cfsqltype="cf_sql_char" value="#arguments.usuario.getPermicao()#" maxlength="1" />,
                confirm = <cfqueryparam cfsqltype="cf_sql_char" value="#arguments.usuario.getConfirm()#" maxlength="1" />
            WHERE id = <cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.usuario.getId()#" />
        </cfif>
    </cfquery>
    <cfif arguments.usuario.getId() EQ 0>
        <cfset arguments.usuario.setId( daoResult.GENERATED_KEY ) />
    </cfif>
</cffunction>
<cffunction name="delete" access="public" output="false" returntype="void">
    <cfargument name="usuario" type="numeric" displayname="usuario" required="true" />
    <cfquery datasource="#this.datasource#">
        DELETE FROM usuarios
        WHERE id = <cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.usuario#" />
    </cfquery>
</cffunction>
<cffunction name="autenticar" access="public" output="false" returntype="query">
    <cfargument name="usuario" type="dto.Usuarios" displayname="usuario" required="false" default="0" />
    <cfquery name="rsAutenticar" datasource="#this.datasource#">
        SELECT id, nome, sobrenome, email, permicao, confirm
        FROM usuario
        WHERE email = <cfqueryparam cfsqltype="cf_sql_char" value="#arguments.usuario.getEmail()#" maxlength="255" />
            AND senha = SHA1(<cfqueryparam cfsqltype="cf_sql_char" value="#arguments.usuario.getSenha()#" maxlength="10" />)
    </cfquery>
    <cfscript>
    if( rsAutenticar.recordCount NEQ 1 ) {
        this.result = QueryNew("id,nome,sobrenome,email,permicao,confirm");
    } else{
        this.result = rsAutenticar;
    }
    return this.result;
    </cfscript>
</cffunction>
<cffunction name="ativarUsuario" access="public" output="false" returntype="boolean">
    <cfargument name="usuario" type="dto.Usuarios" displayname="usuario" required="true" />
    <cfquery name="validaUsuario" datasource="#this.datasource#">
        SELECT id
        FROM usuarios
        WHERE id = <cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.usuario.getId()#" />
            AND email = <cfqueryparam cfsqltype="cf_sql_char" value="#arguments.usuario.getEmail()#" maxlength="255" />
            AND confirm = 0
    </cfquery>
    <cfif validaUsuario.recordCount EQ 1>
        <cfquery datasource="#this.datasource#">
            UPDATE usuarios SET
                confirm = <cfqueryparam cfsqltype="cf_sql_char" value="#arguments.usuario.getConfirm()#" maxlength="1" />
            WHERE id = <cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.usuario.getId()#" />
        </cfquery>
        <cfset this.result = true />
    <cfelse>
        <cfset this.result = false />
    </cfif>
    <cfreturn this.result />
</cffunction>
<cffunction name="listarUsuarios" access="public" output="false" returntype="query">
    <cfargument name="usuario" type="any" displayname="usuario" required="false" default="0" />
    <cfargument name="ativos" type="boolean" displayname="ativos" required="false" default="true" />
    <cfargument name="inativos" type="boolean" displayname="inativos" required="false" default="true" />
    <cfscript>
    var nId = 0;
    if( isInstanceOf(arguments. usuario , "dto.Usuarios" ) == true ) {
        nId = arguments.usuario.getId();
    } else if( isNumeric( arguments.usuario ) ) {
        nId = arguments.usuario;
    }
    </cfscript>
    <cfquery name="rsListarUsuarios" datasource="#this.datasource#">
        SELECT id, nome, sobrenome, email, dtcadastro, senha, permicao, confirm
        FROM usuario
        WHERE id != 0
        <cfif nId GT 0>
            AND id = <cfqueryparam cfsqltype="cf_sql_integer" value="#nId#" />
        </cfif>
        <cfif arguments.ativo IS true >
            AND confirm = <cfqueryparam cfsqltype="cf_sql_char" value="1" />
        </cfif>
        <cfif arguments.inativo IS true >
            AND confirm = <cfqueryparam cfsqltype="cf_sql_char" value="0" />
        </cfif>
        ORDER BY dtcadastro DESC, nome, sobrenome ASC
    </cfquery>
    <cfset this.result = rsListarUsuarios />
    <cfreturn this.result />
</cffunction>
</cfcomponent>

Como vimos, a classe base, nesse caso DaoBase.cfc serve para deixarmos de implementar coisas que se repetem, como o método construtor da camada DAO que sempre terá a base:


<cfscript>
this.datasource = arguments.datasource;
this.result = "";
</cfscript>

Então não preciso ficar reescrevendo em cada classe DAO, posso simplesmente implementar o método construtor da classe mãe através do super.init( datasource );

Na camada DAO, pouco utilizamos herança, veremos funcionando melhor futuramente no DTO.

Vimos também que além dos métodos do CRUD básico, implementamos outros métodos como autenticar(), ativarUsuario() e listarUsuarios() … assim como os demais métodos que vão surgindo durante o desenvolvimento.

Vão notar também que não estou usando ORM e sim SQL comum e simples. Realmente não estou usando exemplos com ORM pois a ideia é mostrar uma arquitetura que serve para implementações simples e avançadas sem ajuda de frameworks.

Com a camada DAO implementada, agora temos que ir para a camada de modelo, mas vamos deixar para o próximo artigo.

Aproveitando, percebeu-se também que por definição adoto que minhas classes tenham o nome exato das suas respectivas tabelas no banco e ambas sempre no plural.

E na instância dos objetos utilizo seus nomes no singular.

Ex. Uma instância da classe Usuarios() se chama usuario.


<cfscript>
usuario = Application.instance.dao.Usuarios;
recordSet = usuario.listar();
</cfscript>
Mas isso é uma definição minha, todos tem liberdade de nomear do jeito que achar melhor, até por que muita gente usa prefixo em nomes de tabelas.

Até a próxima.

Paulo Teixeira

Fonte: http://www.pauloteixeira.blog.br/site/index.php/content/2012/implementando-a-camada-dao/

Permanent link to this article: http://ensina.me/coldfusion/implementando-a-camada-dao/

Leave a Reply