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

Avatar do usuário
bencz
Usuário Nível 4
Usuário Nível 4
Mensagens: 524
Registrado em: 28 Abr 2012 17:36
Contato:

Padrão repository com xHarbour

Mensagem por bencz »

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

Código: Selecionar todos

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

Código: Selecionar todos

#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

Código: Selecionar todos

#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

Código: Selecionar todos

#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

Código: Selecionar todos

#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 é:

Código: Selecionar todos

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 do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Padrão repository com xHarbour

Mensagem por JoséQuintas »

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

Código: Selecionar todos

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

Código: Selecionar todos

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 mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"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 do usuário
bencz
Usuário Nível 4
Usuário Nível 4
Mensagens: 524
Registrado em: 28 Abr 2012 17:36
Contato:

Padrão repository com xHarbour

Mensagem por bencz »

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 do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Padrão repository com xHarbour

Mensagem por JoséQuintas »

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 mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"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 do usuário
bencz
Usuário Nível 4
Usuário Nível 4
Mensagens: 524
Registrado em: 28 Abr 2012 17:36
Contato:

Padrão repository com xHarbour

Mensagem por bencz »

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
Responder