Página 1 de 3
Retornar a Conexão do banco de Dados
Enviado: 27 Jun 2016 15:36
por Antonio
Boa tarde pessoal.
Estou tentando implementar um classe de conexão com um BD.
Código: Selecionar todos
#include "adodb.ch"
#include "hbclass.ch"
CLASS TConexao
DATA dbRs INIT "ADODB.Connection"
DATA dbDriver INIT "{PostgreSQL UNICODE}"
DATA dbDataBase INIT "Plainning"
DATA dbServer INIT "127.0.0.1"
DATA dbPort INIT 5432
DATA dbUser INIT "postgres"
DATA dbPassword INIT "ccccccc"
METHOD conectar() CONSTRUCTOR
METHOD desconectar()
ENDCLASS
/*
METHOD novo() CLASS TConexao
::dbUser := NIL
::dbPassword := NIL
RETURN Self
*/
METHOD conectar( cUser, cPass ) CLASS TConexao
public dbConn
Try
dbConn:= win_oleCreateObject( "ADODB.Connection" )
dbConn:Open("Driver={PostgreSQL UNICODE};Server=127.0.0.1;Port=5432;Database=plainning;Uid=postgres;Pwd=ccccccc;" )
MsgBox ( "CONETADO", "Plainning",,)
Catch err
MsgBox( "Deu Ruim")
dbConn:Close()
end
RETURN Self
METHOD desconectar() CLASS TConexao
dbRecSet:Close()
RETURN dbConn
Estou fazendo a chamada assim:
Código: Selecionar todos
#include <hmg.ch>
declare window Main
Function main_button_teste_action
oConn:= TConexao():conectar("postgres", "mypassword")
dbCommand :="select * FROM tab_ctspagar;"
dbRecSet := oConn:EXECUTE ( dbCommand )
do while .not. dbRecSet:eof()
msgbox( str( dbRecSet:fields["cpo_numero"]:VALUE ) +" "+ sTr( dbRecSet:fields["cpo_data"]:VALUE ) +" "+ str( dbRecSet:fields["cpo_valor"]:VALUE ), "splain" )
dbRecSet:MoveNext()
enddo
Return Nil
A conexão com o BD é feita normalmente mas ocorre o problema que esta na imagem:
O que eu queria é instanciar a classe fazendo com que o oBjeto da conexão ficasse disponivel/visivel a todo o sistema para assim, executar os métodos.
Agradeço a ajuda.
Obrigado
Retornar a Conexão do banco de Dados
Enviado: 27 Jun 2016 15:46
por JoséQuintas
Uso assim em multithread:
Código: Selecionar todos
STATIC AppcnMySqlLocal := NIL
FUNCTION AppcnMySqlLocal()
IF AppcnMySqlLocal == NIL
AppcnMySqlLocal := MySqlConnection( "xxx", , "xxx" )
ENDIF
RETURN AppcnMySqlLocal
E nas rotinas:
Código: Selecionar todos
LOCAL cnMySql := ADOClass():New( AppcnMySqlLocal() )
cnMySql:cSql := "SELECT * FROM CLIENTES"
cnMySql:Execute()
DO WHILE .NOT. cnMySql:Eof()
? cnMySql:Value( "CODIGO" )
cnMySql:MoveNext()
ENDDO
cnMySql:Rs:Close()
Minha classe já tem recordset e tudo mais junto.
Por isso acabo criando instâncias da classe, e não da conexão.
Retornar a Conexão do banco de Dados
Enviado: 27 Jun 2016 18:29
por JoséQuintas
Só pra curiosidade, sem tratamento de erros algo mais ou menos assim:
Código: Selecionar todos
CREATE CLASS ADOClass
VAR Connection
VAR Rs
VAR cSql
METHOD New( oCn ) INLINE ::Connection := oCn
METHOD Value( cCampo ) INLINE ::Rs:Fields( cCampo ):Value
METHOD Eof() INLINE ::Rs:Eof()
METHOD MoveFirst() INLINE ::Rs:MoveFirst()
METHOD MoveLast() INLINE ::Rs:MoveLast()
METHOD RecordCount() INLINE ::Rs:RecordCount()
METHOD Execute( cSql ) INLINE ::Rs := ::Connection:Execute( iif( cSql == NIL, ::cSql, cSql ) )
METHOD ExecuteCmd( cSql ) INLINE ::Connection:Execute( iif( cSql == NIL, ::cSql, cSql ) )
ENDCLASS
Por falar nisso... como não fecho a conexão na classe, mais fácil criar um close do recordset nela, pra evitar esquecimento e fechar a conexão por engano.
Retornar a Conexão do banco de Dados
Enviado: 27 Jun 2016 22:27
por Vlademiro
Você não criou o método EXECUTE. É isso que a mensagem está dizendo.
Faz tempo que não uso ADODB, mas eu tentaria mais ou menos assim :
1 ) Não criaria uma variável public dbConn, eu faria assim :
2 ) Apagaria a linha public dbConn e alteraria a referencia dbConn por ::dbConn no método conectar, já que ele agora é interno a classe
3 ) Criaria o meu método execute
Código: Selecionar todos
METHOD Execute( cSuaQuery ) CLASS TConexao
RETURN ::dbConn.Execute( cSuaQuery )
Agora sim, o Execute é visível.
Você pode evoluir a sua classe mais adiante, criando outros métodos. A vantagem é que ela pode abranger a vários banco de dados. Evite usar variáveis públicas dentro de objetos.
Retornar a Conexão do banco de Dados
Enviado: 28 Jun 2016 08:25
por asimoes
Vlademiro
É isso
::dbConn.Execute( cSuaQuery )
Ou isso: ?
::dbConn:Execute( cSuaQuery )
Retornar a Conexão do banco de Dados
Enviado: 28 Jun 2016 08:58
por Vlademiro
Com dois pontos asimoes
::dbConn:Execute( cSuaQuery )
Valeu.
Aproveito para acrescentar que a solução do Quintas é a mais completa, e sugiro ao Antonio não ir logo usando ela pois seria melhor para ele aprender esses pequenos detalhes e ir ele mesmo desenvolvendo a sua classe. Ele pode ir desenvolvendo, postando suas duvidas aqui e ir olhando as classes já desenvolvidas, como a do Quintas. Seria mais proveitoso assim...
Retornar a Conexão do banco de Dados
Enviado: 28 Jun 2016 10:07
por Antonio
Muito obrigado a todos!
Jose Quintas é bem sofisticada a sua metodologia.
Eu estou usando um arquivo de cabeçalho que encontrei em um exemplo da minigui. adodb.ch
Parece que voces não fazem o uso deste.
Não me encontrei ainda.
Grato
Retornar a Conexão do banco de Dados
Enviado: 28 Jun 2016 10:25
por JoséQuintas
Só voltando um pouco atrás:
A conexão é... uma conexão. Uma classe de conexão parece não trazer nenhum recurso adicional pra conexão.
Basta uma variável.
O que meu aplicativo faz: cria no início, fecha no final (exceto as de uso temporário)
Minha função da conexão está assim, pode até criar uma pra cada tipo de banco de dados:
Código: Selecionar todos
FUNCTION MySqlConnection( cServer, nPort, cDatabase, cUser, cPassword, nVersion )
LOCAL cnConnection
hb_Default( @cServer, "nomeservidor" )
hb_Default( @cDatabase, "nomebancodedados" )
hb_Default( @nPort, 3306 )
hb_Default( @cUser, "usuario" )
hb_Default( @cPassword, "senha" )
hb_Default( @nVersion, 3 )
cnConnection:= win_OleCreateObject( "ADODB.Connection" )
cnConnection:ConnectionString := "Driver={MySQL ODBC " + iif( nVersion == 3, "3.51", "5.3 ANSI" ) + " Driver};Server=" + cServer + ";" + "Port=" + Ltrim( Str( nPort ) ) + ;
";Stmt=;Database=" + cDatabase + ";User ID=" + cUser + ";Password=" + cPassword + ";Collation=latin1;" + ;
"AUTO_RECONNECT=1;COMPRESSED_PROTO=0;PAD_SPACE=1" // usando compactação impede certas checagens // Option=131072;
cnConnection:CursorLocation := AD_USE_CLIENT
cnConnection:CommandTimeOut := 200 // seconds
cnConnection:ConnectionTimeOut := 200 // seconds
// cnConnection:ConnectionString := "Driver={MySQL ODBC 5.3 ANSI Driver};Server=" + cServer + ";" + "Port=" + Ltrim( Str( nPort ) ) + ;
RETURN cnConnection
Retornar a Conexão do banco de Dados
Enviado: 28 Jun 2016 10:31
por JoséQuintas
E a classe contém o recordset embutido, vou colocando coisas conforme vou achando que pode facilitar.
Tratamento de campo nulo, geração pra dbf, no execute testar se conexão está aberta, etc.
Código: Selecionar todos
CREATE CLASS ADOClass
VAR Cn
VAR Rs
VAR cSql
VAR oStructure
METHOD New( oConnection ) INLINE ::CN := oConnection, SELF
METHOD Open( lError )
METHOD Close()
METHOD Execute( cSql, lError ) // Atualiza Rs com retorno
METHOD ExecuteCmd( cSql, lError ) // Despreza retorno
METHOD ExecuteDbf( cSql )
METHOD StringSql( xField, nLen )
METHOD NumberSql( xField )
METHOD DateSql( xField )
METHOD Value( xField )
METHOD ValueSQL( xField ) INLINE ::Value( xField )
METHOD Replace( cFrom, cTo )
METHOD Eof() INLINE iif( ::Rs:RecordCount() == 0, .T., ::Rs:Eof() )
METHOD MoveFirst() INLINE ::Rs:MoveFirst()
METHOD MoveNext() INLINE ::Rs:MoveNext()
METHOD SQLToDBF( lStruAuto ) // usar ExecuteDbf()
METHOD Count( cSql )
METHOD TableList()
METHOD TableExists( cTable )
METHOD FieldList( cTable )
METHOD FieldExists( cField, cTable )
METHOD AddField( cField, cTable, cSql )
METHOD DeleteField( cField, cTable, cDbf )
METHOD IndexList( cTable )
METHOD RecordCount() INLINE ::Rs:RecordCount()
END CLASS
Nota:
o INLINE é só uma economia de fonte. Quando a função é muito pequena, usando INLINE coloca direto na declaração, sem função separada para o método.
Exemplo:
Código: Selecionar todos
FUNCTION Teste() ; RETURN "ok"
METHOD Teste() INLINE "ok"
ou comparando:
Código: Selecionar todos
CREATE CLASS ...
METHOD Teste()
END CLASS
METHOD Teste() CLASS ...
RETURN "ok"
//---------------------------
CREATE CLASS ...
METHOD Teste() INLINE "ok"
END CLASS
Retornar a Conexão do banco de Dados
Enviado: 28 Jun 2016 10:48
por JoséQuintas
Acréscimo:
Algumas aí foram temporárias e não eliminei.
Porque eu errava o nome, então coloquei dois nomes, como Value() e ValueSQL()
Ou porque decidi alterar o nome no meio do caminho, e deixei os dois nomes até terminar de alterar todos os fontes
Ou porque dependendo do ODBC, o tipo não é convertido corretamente, então StringSql() garante que vai ser convertido pra string.
Como tenho ODBC 3.51 e 5.3 nos clientes, e cada um se comporta diferente, algumas coisas vão ficar aí até terminar de trocar todos.
SqlToDBf e ExecuteToDbf também, são a mesma coisa, mas talvez altere o nome novamente.... rs
Retornar a Conexão do banco de Dados
Enviado: 28 Jun 2016 11:14
por Vlademiro
Quintas, nas minhas classes eu até comecei fazendo assim (conexão e recordset na mesma classe), mas me deparei com um problema : nos CRUDs que eu criava eu primeiro abria um Grid para depois chamar o Formulário. Alguns métodos do Grid (Recordset) não tem relação com o Formulário. Por exemplo: nos forms eu tenho o método Insert, Update e Delete. No Grid eu tenho o método NextPage, PreviousPage, etc.
Ficou assim :
Documentação da TVladSQL :
http://sistema.lia.ufc.br/robodoc/TVlad ... ml#robo116
Documentação da TVladSQLBrowser :
http://sistema.lia.ufc.br/robodoc/TVlad ... tml#robo77
Documentação da TVladSQLFrm :
http://sistema.lia.ufc.br/robodoc/TVlad ... tml#robo61
Retornar a Conexão do banco de Dados
Enviado: 28 Jun 2016 11:18
por Vlademiro
Retornar a Conexão do banco de Dados
Enviado: 28 Jun 2016 12:15
por JoséQuintas
Você trabalha diferente do jeito que eu trabalho.
Considero o recordset igual a um arquivo temporário.
Não me preocupo em manter posição, ou seja lá o que for.
No caso do browse uso um recordset só pra ele, e até converto pra DBF.
Ao alterar num browse, por exemplo, a partir do ID atualizo o MySQL.
Achei mais prático assim, do que enfrentar particularidades.
É isso que indica lá na minha conexão o CursorLocation AD_USE_CLIENT
É praticamente como se o aplicativo estivesse desconectado da base, mesmo mantendo conexão aberta.
Retornar a Conexão do banco de Dados
Enviado: 28 Jun 2016 13:03
por Vlademiro
Entendi, os arquivos temporários que vc trabalha são DBFs, e na hora de pegar os dados do servidor fica mais pratico fazer com uma classe com tudo junto (Conexão e Recordset). Quando os dados chegam você os trata localmente usando os DBFs. Quando você vai gravar os dados você os converte de volta para o banco de dados.
Retornar a Conexão do banco de Dados
Enviado: 28 Jun 2016 13:06
por Vlademiro
Vou postar um teste de sistema que fiz usando HMG. Lá tem as classes que fiz até agora, os formulários e o banco em postgreSQL.
Vou postar na seção Minigui do fórum. Lá da pra ver como eu fiz o CRUD gravando no postgreSQL.