Clipper On Line • Ver Tópico - Padrão repository com xHarbour

Padrão repository com xHarbour

Aqui você poderá oferecer suas Contribuições, Dicas e Tutoriais (Texto ou Vídeo) que sejam de interesse de todos.

Moderador: Moderadores

 

Padrão repository com xHarbour

Mensagempor bencz » 10 Jan 2020 16:29

Boas tardes!
Eu estava fazendo alguns testes aqui e resolvi implementar o padrão "repository" para testar a minha ideia...
O código que irei colocar aqui está bem 'feio', foi muito mais para teste do que para implementar algo com padrão de produção... mas, com poucas alterações é possivel colocar o código para padrão de produção...
Para quem não sabe exatamente o que é o padrão repository... basicamente existe uma classe "repository" que faz o acesso aos dados de uma unica tabela, para cada tabela é criada uma classe modelo, contendo as informações necessarias de cada tabela.
Na classe repository você tem acesso aos comandos basicos do SQL, como Update, Insert, GetById, GetAll, Delete ( no caso, implementei somente o insert, getall e o getbyid )

Segue o código:

teste.prg
function start
    LOCAL oConnection := NIL
   
    // Inicializa a conexão, uma unica vez...
    oConnection := ConnectionFactory():New()
   
    TestInsert(oConnection)
    *TestGetAll(oConnection)
    TestGetById(oConnection)

return nil

function TestInsert(oConnection) 
    local i := 1
    local oRepoUsuarios := NIL
    oRepoUsuarios := UsuariosRepository():new(oConnection)

    for i := 1 to 10
      oUsuario := Usuarios():New()
      oUsuario:Nome := 'Alexandre ' + RTRIM(LTRIM(STR(I)))
      oUsuario:SobreNome := 'Bencz ' + RTRIM(LTRIM(STR(I)))
     
      oRepoUsuarios:Insert(oUsuario)
    next

return nil

function TestGetAll(oConnection)
    local aResult := NIL
    local oRepoUsuarios := NIL
   
    oRepoUsuarios := UsuariosRepository():new(oConnection)
   
    aResult := oRepoUsuarios:GetAll()
    for i := 1 to len(aResult)
        ? (aResult[i]):USUARIOID, (aResult[i]):NOME, (aResult[i]):SobreNome
    next
return nil

function TestGetById(oConnection)
        local aResult := NIL
    local oRepoUsuarios := NIL
   
    oRepoUsuarios := UsuariosRepository():new(oConnection)
   
    aResult := oRepoUsuarios:GetById(2)
    for i := 1 to len(aResult)
        ? (aResult[i]):USUARIOID, (aResult[i]):NOME, (aResult[i]):SobreNome
    next
return nil


connectionFactory.prg
#include "hboo.ch"
#include "hbclass.ch"

#include "sqlrdd.ch"
   
REQUEST SQLRDD           
REQUEST SR_ODBC   

#define CONN_TYPE CONNECT_ODBC
#define CONN_DNS  "driver=SQL Server;network=dbmssocn;server=127.0.0.1;database=test;uid=sa;pwd=senha;"

class ConnectionFactory
private:
    DATA nConnection INIT NIL
    DATA oSQL INIT NIL
   
public:
    METHOD New(nConnectionType, cDNS)
    INLINE METHOD GetSql()
        RETURN ::oSQL
    ENDMETHOD
endclass

method new(nConnectionType, cDNS) class ConnectionFactory
    Default(@nConnectionType, CONN_TYPE)
    Default(@cDNS, CONN_DNS)

    ::nConnection := SR_AddConnection(nConnectionType, cDNS)
   
    IF ::nConnection < 0
        Throw(ErrorNew('Falha ao inicializar a conecao', 0, 0, 'ConnectionFactory'))
    ENDIF
   
    ::oSQL := SR_GetConnection(::nConnection)

return self


dataaccess.prg
#include "hboo.ch"
#include "hbclass.ch"       
   
class DataAccess
private:
    data oConnectionFactory init nil
    data cModelClassName init nil

public:
    method new() constructor
   
    method GetAll()
    method GetById(nId)
    method Insert(oModelClass)
   
endclass

method new(cModelClassName, oConnectionFactory) class DataAccess   
    ::cModelClassName := cModelClassName
    ::oConnectionFactory := oConnectionFactory
return self

method GetAll() class DataAccess
    LOCAL I := 0, J := 0
    LOCAL cQuery := ""
    LOCAL nError := NIL
    LOCAL nErrorPosition := NIL
    LOCAL aReturn := {}
    LOCAL aTmpReturn := {}
    LOCAL aTmpDbStructure := NIL
    LOCAL oTmpObj := NIL

    cQuery := "SELECT * FROM " + ::cModelClassName
   
    apCode := SR_SQLParse(cQuery, @nError, @nErrorPosition)
    ::oConnectionFactory:GetSql():exec( SR_SQLCodeGen(apCode, {}, ::oConnectionFactory:GetSql():nSystemID ), , .t. , @aReturn )
   
    for i := 1 to len(aReturn)   
        aadd(aTmpReturn, &('Create' + ::cModelClassName + 'Instance()') )
        aTmpDbStructure := aTmpReturn[i]:GetDbStructure()
       
        for j := 1 to len(aReturn[i])
            Eval(aTmpDbStructure[j][5], aReturn[i][j])
        next       
    next
   
return aTmpReturn

method GetById(nId) class DataAccess

    LOCAL I := 0, J := 0
    LOCAL cQuery := ""
    LOCAL nError := NIL
    LOCAL nErrorPosition := NIL
    LOCAL aReturn := {}
    LOCAL aTmpReturn := {}
    LOCAL aTmpDbStructure := NIL
    LOCAL oTmpObj := NIL
    LOCAL oModelClass := NIL

    oModelClass := &('Create' + ::cModelClassName + 'Instance()')
    cQuery := "SELECT * FROM " + ::cModelClassName + " WHERE " + oModelClass:GetKeyField() + " = ?"
   
    apCode := SR_SQLParse(cQuery, @nError, @nErrorPosition)
    ::oConnectionFactory:GetSql():exec( SR_SQLCodeGen(apCode, {nId}, ::oConnectionFactory:GetSql():nSystemID ), , .t. , @aReturn )
   
    for i := 1 to len(aReturn)   
        IF(I > 1)   
            aadd(aTmpReturn, &('Create' + ::cModelClassName + 'Instance()') )
        ELSE
            aadd(aTmpReturn, oModelClass )
        ENDIF
       
        aTmpDbStructure := aTmpReturn[i]:GetDbStructure()
        for j := 1 to len(aReturn[i])
            Eval(aTmpDbStructure[j][5], aReturn[i][j])
        next       
    next

return aTmpReturn

method Insert(oModelClass) class DataAccess
    LOCAL I := 1
    LOCAL aObjetos := NIL
    LOCAL tField := NIL
    LOCAL cQuery := ''
    LOCAL nError := NIL
    LOCAL nErrorPosition := NIL
    LOCAL apCode := NIL
    LOCAL oPreparedQuery := NIL
    LOCAL aParameters := {}
   
    aObjetos := __objGetValueList(oModelClass, nil, HB_OO_CLSTP_EXPORTED)

    cQuery := 'INSERT INTO ' + ::cModelClassName + "("
   
    FOR I := 1 TO LEN(aObjetos)
        IF aObjetos[i][HB_OO_DATA_SYMBOL] == oModelClass:GetKeyField()
            LOOP
        ENDIF
       
        IF(I > 1)
            cQuery += ', '
        ENDIF
       
        cQuery += aObjetos[i][HB_OO_DATA_SYMBOL]
       
        // Adiciona os parametros
        aadd(aParameters, aObjetos[i][HB_OO_DATA_VALUE])
    NEXT
   
    cQuery += ') VALUES('
   
    FOR I := 1 TO LEN(aObjetos)
        IF aObjetos[i][HB_OO_DATA_SYMBOL] == oModelClass:GetKeyField()
            LOOP
        ENDIF
   
        IF(I > 1)
            cQuery += ', '
        ENDIF
       
        cQuery += '?'   
    NEXT
   
    cQuery += ')'
           
    apCode := SR_SQLParse(cQuery, @nError, @nErrorPosition)
    ::oConnectionFactory:GetSql():exec( SR_SQLCodeGen(apCode, aParameters, ::oConnectionFactory:GetSql():nSystemID ) )
    ::oConnectionFactory:GetSql():Commit()
   
return nil


Modelos\Usuario.prg
#include "hboo.ch"
#include "hbclass.ch"

function CreateUsuariosInstance() ; return Usuarios():New()

class Usuarios
public:
    DATA USUARIOID INIT NIL
    DATA NOME INIT NIL
    DATA SOBRENOME INIT NIL
       
private:
    DATA KEYFIELD INIT "USUARIOID"
    DATA DBSTRUCTURE INIT {}
   
public:
    METHOD NEW()
   
    INLINE METHOD GetKeyField() ; return ::KEYFIELD ; endmethod
    INLINE METHOD GetDbStructure() ; return ::DBSTRUCTURE ; endmethod
   
    METHOD SetUsuarioID(nUsuarioID) INLINE ( ::USUARIOID := nUsuarioID )
    METHOD SetNome(cNome) INLINE ( ::NOME := cNome )   
    METHOD SetSobreNome(cSobreNome) INLINE ( ::SOBRENOME := cSobreNome )   
endclass

METHOD NEW() CLASS Usuarios

    aadd(::DBSTRUCTURE, { 'USUARIOID', 'N', -1, .T. , {| n | ::SetUsuarioID(n)  } })
    aadd(::DBSTRUCTURE, { 'NOME',      'C', 50, .F. , {| c | ::SetNome(c)       } })
    aadd(::DBSTRUCTURE, { 'SOBRENOME', 'C', 50, .F. , {| c | ::SetSobreNome(c)  } })

RETURN SELF


repository\UsuariosRepository.prg
#include "hbclass.ch"

class UsuariosRepository INHERIT DataAccess
public:
    method new(oConnectionFactory) inline ( Super:New('Usuarios', IIF( oConnectionFactory == NIL, ConnectionFactory():NEW(), oConnectionFactory) ) )
endclass 


A tabela de testes que utilizei no MS-SQL é:
CREATE TABLE [dbo].[Usuarios](
   [UsuarioID] [bigint] IDENTITY(1,1) NOT NULL,
   [Nome] [nvarchar](50) NOT NULL,
   [SobreNome] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_Usuarios] PRIMARY KEY CLUSTERED
(
   [UsuarioID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO


Dentro da classe repository da tabela, é possivel adicionar metodos para fazer selects mais elaborados ou inserts mais elaborados... o lado bom desse padrão é que fica tudo organizadinho em seu lugar....
Imagem
Avatar de usuário

bencz
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 517
Data de registro: 28 Abr 2012 17:36
Curtiu: 6 vezes
Mens.Curtidas: 34 vezes

Padrão repository com xHarbour

Mensagempor JoséQuintas » 10 Jan 2020 17:40

Sei lá se entendi o uso....
Mais prático usar ADO, ou direto, ou através de uma classe.

WITH OBJECT cnMySql
   :QueryCreate()
   :QueryAdd( "CODIGO",   mCodigo )
   :QueryAdd( "NOME",     mNome )
   :QueryAdd( "ENDERECO", mEndereco )
   :QueryExecuteInsert( "CLIENTES" )


Leitura

WITH OBJECT cnMySql
   :cSql := "SELECT * FROM CLIENTES LIMIT 1"
   :Execute()
   mCodigo   := :Fields( "CODIGO" )
   mNome     := :Fields( "NOME" )
   mEndereco := :Fields( "ENDERECO" )
   :CloseRecordset()
ENDWITH
José M. C. Quintas
Harbour 3.2, mingw, gtwvg, multithread, dbfcdx, ADO+MySql, PNotepad
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar de usuário

JoséQuintas
Membro Master

Membro Master
 
Mensagens: 18011
Data de registro: 26 Fev 2007 11:59
Cidade/Estado: São Paulo-SP
Curtiu: 15 vezes
Mens.Curtidas: 1206 vezes

Padrão repository com xHarbour

Mensagempor bencz » 11 Jan 2020 10:07

Pois é... eu sei que o Harbour já fornece coisas mais interessantes... mas, como falei, foi apenas um teste!
Espero que sirva de estudos para alguém meu amigo! (:
Imagem
Avatar de usuário

bencz
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 517
Data de registro: 28 Abr 2012 17:36
Curtiu: 6 vezes
Mens.Curtidas: 34 vezes

Padrão repository com xHarbour

Mensagempor JoséQuintas » 11 Jan 2020 10:18

bencz escreveu:Pois é... eu sei que o Harbour já fornece coisas mais interessantes...


ADO é Microsoft, não é Harbour.
Pode ser usado no XHarbour também.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg, multithread, dbfcdx, ADO+MySql, PNotepad
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar de usuário

JoséQuintas
Membro Master

Membro Master
 
Mensagens: 18011
Data de registro: 26 Fev 2007 11:59
Cidade/Estado: São Paulo-SP
Curtiu: 15 vezes
Mens.Curtidas: 1206 vezes

Padrão repository com xHarbour

Mensagempor bencz » 11 Jan 2020 13:56

Sim, eu sei que o ADO é da Microsoft...
O que quis dizer, é que o (x)Harbour já fornece ferramentas 'nativamente' para facilitar isso...
foi apenas uma maneira de dizer (:
Imagem
Avatar de usuário

bencz
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 517
Data de registro: 28 Abr 2012 17:36
Curtiu: 6 vezes
Mens.Curtidas: 34 vezes




Retornar para Contribuições, Dicas e Tutoriais

Quem está online

Usuários vendo este fórum: Google Adsense [Bot] e 12 visitantes


Ola Amigo, espero que meu site e forum tem lhe beneficiado, com exemplos e dicas de programacao.
Entao divulgue o link da Doacao abaixo para seus amigos e redes sociais ou faça uma doacao para o site forum...
MUITO OBRIGADO PELA SUA DOACAO!
Faça uma doação para o forum
cron
v
Olá visitante, seja bem-vindo ao Fórum Clipper On Line!
Efetue o seu login ou faça o seu Registro