Página 1 de 1

Dúvida sobre classe

Enviado: 29 Abr 2022 11:23
por JoséQuintas
Se eu passo uma classe por parâmetro, eu estou passando tudo existente dentro da classe, ou somente a referência à classe?

Por exemplo no uso do ADO: se a classe tem o recordset, vou estar passando uma cópia do recordset?

Dúvida sobre classe

Enviado: 29 Abr 2022 11:59
por Itamar M. Lins Jr.
Olá!
Acredito que quando passamos a classe como um parâmetro, vai tudo. Como se fosse um TREEVIEW dentro de outro.
A "bagulhada" da classe fica toda visível na função que recebeu o parâmetro.

Saudações,
Itamar M. Lins Jr.

Dúvida sobre classe

Enviado: 29 Abr 2022 13:21
por alxsts
Olá!

Sim, vai tudo. Se passar por referência, vai passar uma referência ao endereço de memória onde está a instância da classe. Se passar por valor vai uma cópia.
JoséQuintas escreveu:Por exemplo no uso do ADO: se a classe tem o recordset, vou estar passando uma cópia do recordset?
Não exatamente. Vai passar um pointer que referencia o local onde se encontram os dados do recordset.

Dúvida sobre classe

Enviado: 29 Abr 2022 13:28
por JoséQuintas
É que estou pensando no seguinte:

Código: Selecionar todos

rs := cnSQL:Execute( "SELECT ..." )
Não apenas nisso, mas no conjunto também.
Se passar a variável como parâmetro, vai toda resposta do SQL.
E se isso estiver numa classe, também.

E surge um adicional:
Supondo que possa ser um recordset em aberto, fechar a original vai fechar as cópias?

Pode ser esse o meu problema de estourar resource no Windows, o estilo de uso do ADO e não API Windows propriamente dita.

Começar a alterar tudo pra referência.

Dúvida sobre classe

Enviado: 29 Abr 2022 13:30
por alxsts
Olá!

Testar é o melhor remédio...

Dúvida sobre classe

Enviado: 01 Mai 2022 17:47
por dbsh
Reparti em camadas

1 - Selec, insert, updade, delete, etc,..
2 - Verifica se esta conectado com banco de dados
3 - Faz a conexão, se não estiver
4 - Salvar conexão, na primeira conexao
5 - CLOSE - fecha Record set, sem fechar conexão

ADO_OPEN - É chamado quando você faz USE ('tabela'), select, update, etc...
ADO_Connection - Faz a conexão com banco de dados
ADO_GetConnection - Retorna conexão salva caso ja exista
ADO_SetConnection - Salva conexao, se não foi salva ainda
ADO_CLOSE - Fecha RecordSet, sem fechar conexão

Como retirei parte das mudanças que fiz em ADORDD, provavelmente vai da alguns erros de função, retirei o que vi, se der passe, que vejo se é necessária a função ou é só desativar chamada

Aceita um ou mais comando em um mesma string, select,Insert,update,etc,..

EX:
string := "insert into produto (codigo,descricao,valor) value ('001', 'arroz', 23.99);SELECT LAST_INSERT_ID;" //vai retorna um RecordSet com 1 registro e 1 campo last_insert_id
string := "insert into produto (codigo,descricao,valor) value ('001', 'arroz', 23.99)"
string := "SELECT LAST_INSERT_ID;"

*** SENARIO EM QUE ESTEJA COM MAIS DE UMA ESTAÇÃO DE TRABALHO ACESSANDO O MESMO BANCO DE DADOS/TABELA ***

SELECT LAST_INSERT_ID; // RETORNA ULTIMO ID, Quando tabela tem um campo auto_increment
SELECT MAX(ID) FROM produto //faz sentido quando só tem 1 pessoa que utiliza o banco de dados, senão pode vir o id de outra estação de trabalho que fez um insert antes de você executar o comando "select max"

No caso de um INSERT, seguido de um SELECT LAST_INSERT_ID, mesmo se outra estação de trabalho, fazer um INSERT, como você esta na mesma conexão, o resultado será o ID do seu INSERT, se você faz um INSERT, fecha conexão, e faz um SELECT LAST_INSERT_ID, corre o risco de pegar o ID de outro INSERT.

Código: Selecionar todos


STATIC FUNCTION ADO_OPEN( nWA, aOpenInfo )

   LOCAL aWAData := USRRDD_AREADATA( nWA )
   LOCAL aField, nResult
   LOCAL oRecordSet, nTotalFields, n, uRet
   LOCAL aQuery, xx, nPos

   IF Empty( aWAData[ WA_CONNECTION ] ) .and. !Empty(aOpenInfo[ UR_OI_CONNECT ])
     aWAData[ WA_CONNECTION ] := win_oleAuto()
     aWAData[ WA_CONNECTION ]:__hObj := aOpenInfo[ UR_OI_CONNECT ] /* "ADODB.Connection" */
   ENDIF

   ADO_AreaInfo( nWa )

   IF !ADO_Connection( nWA, aOpenInfo )
     RETURN HB_FAILURE
   ENDIF

   select( nWa )

   IF Empty( aWAData[ WA_QUERY ] ) .and. (Empty(aWAData[ WA_TABLENAME ]) .or. Upper(aWAData[ WA_TABLENAME ]) = 'NULL')
      RETURN HB_SUCCESS
   ENDIF

   IF Empty( aWAData[ WA_QUERY ] ) .or. aWAData[ WA_QUERY ] == "SELECT * FROM"
     aWAData[ WA_QUERY ] := "SELECT * FROM " + aWAData[ WA_TABLENAME ]
   ENDIF

   aWAData[ WA_QUERY ] := StrTran(aWAData[ WA_QUERY ], hb_eol(), ' ')
   aQuery := Split(aWAData[ WA_QUERY ], ';')
   FOR xx := 1 TO Len(aQuery)
     IF Empty(aQuery[xx])
       LOOP
     ENDIF
     uRet := {'SELECT ', 'UPDATE ', 'DELETE ', 'INSERT ', 'CREATE ', 'DROP ', 'TRUNCATE ', 'USE ', 'SHOW ', 'VIEW ', 'DESCRIBE ',;
       'REPLACE ', 'LOAD ', 'ALTER ', 'HANDLER ', 'RENAME ', 'USE ', 'START ', 'COMMIT ', 'ROLLBACK', 'SAVEPOINT ', 'LOCK ', 'UNLOCK '}
     t_cQuery := Upper(Left(LTrim(aQuery[xx]), 20))
     nPos := hb_At(' ', t_cQuery)
     IF nPos > 0 .and. AScan(uRet, {|p| Left(t_cQuery, nPos) == p }) = 0 // prever ";" dentro de string
       aQuery[xx] := aQuery[xx] + ';' + aQuery[xx + 1]
       aQuery[xx + 1] := NIL
     ENDIF
   NEXT
   t_cQuery := ''

   FOR xx := Len(aQuery) TO 1 STEP -1
     IF Empty(aQuery[xx])
       aDelSize(aQuery, xx)
     ENDIF
   NEXT

   BEGIN SEQUENCE WITH {|e| Break(e) }
     FOR xx := 1 TO Len(aQuery)
       uRet := AllTrim(aQuery[xx]) + ';'
       //ADO_AddLog(uRet)
       IF Upper(Left(uRet, 7)) == 'SELECT '
         oRecordSet := win_oleCreateObject( "ADODB.Recordset" )
         IF oRecordSet == NIL
            //t_oUltError := BoniErrorNew(hb_langErrMsg( EG_OPEN ) + " (" + hb_langErrMsg( EG_UNSUPPORTED ) + ")", ;
            ///*Args*/, .T./*Candefault*/, .F./*CanRetry*/, .F./*CanSubstitute*/, /*Cargo*/, ;
            //aOpenInfo[ UR_OI_NAME ]/*FileName*/, 0/*GenCode*/, ''/*Operation*/, 0/*Oscode*/, ;
            //0/*Severity*/, 1001/*SubCode*/, ''/*SubSystem*/, 1/*Tries*/)
            t_oUltError := ErrorNew()
            UR_SUPER_ERROR( nWA, t_oUltError )
            Break(t_oUltError)
         ENDIF
         //oRecordSet:CursorLocation = adUseServer
         oRecordSet:CursorLocation := adUseClient
         IF Lower( Right( aOpenInfo[ UR_OI_NAME ], 4 ) ) == ".dbf"
            oRecordSet:CursorType := adOpenStatic
            oRecordSet:LockType := adLockPessimistic
         ELSE
            oRecordSet:CursorType := adOpenDynamic
            oRecordSet:LockType := adLockOptimistic
            //oRecordSet:CommandTimeOut := 600 // seconds
            //oRecordSet:ConnectionTimeOut := 600 // seconds
         ENDIF

         IF Upper( aWAData[ WA_ENGINE ] ) == 'DBF' .or. Upper( aWAData[ WA_ENGINE ] ) == 'DBFOLEDB' ;
           .or. Upper( aWAData[ WA_ENGINE ] ) == 'VFPOLEDB' ;
           .or. Upper( aWAData[ WA_ENGINE ] ) == 'SQLITE' .or. Upper( aWAData[ WA_ENGINE ] ) == 'ADS'
           oRecordSet:CommandTimeOut := 20
         ENDIF

         //qt aguarda 'Conectando ao DB ' + t_cServer to oAtencao

         oRecordSet:CacheSize = 30

         t_Time := Seconds()
         oRecordSet:Open( uRet, aWAData[ WA_CONNECTION ] )
         t_Time := Seconds() - t_time
        //oAtencao:close()

         //ADO_AddLog()

         aWAData[ WA_CATALOG ] := win_oleCreateObject( "ADOX.Catalog" )
         aWAData[ WA_CATALOG ]:ActiveConnection := aWAData[ WA_CONNECTION ]

         IF Empty( aWAData[ WA_CATALOG ] )
           aWAData[ WA_CATALOG ] := aWAData[ WA_CONNECTION ]:OpenSchema( adSchemaIndexes )
         ENDIF
         IF Empty( aWAData[ WA_CATALOG ] )
            //t_oUltError := BoniErrorNew(hb_langErrMsg( EG_OPEN ) + " (" + hb_langErrMsg( EG_UNSUPPORTED ) + ")", ;
            ///*Args*/, .T./*Candefault*/, .F./*CanRetry*/, .F./*CanSubstitute*/, /*Cargo*/, ;
            //aOpenInfo[ UR_OI_NAME ]/*FileName*/, 0/*GenCode*/, ''/*Operation*/, 0/*Oscode*/, ;
            //0/*Severity*/, 1001/*SubCode*/, ''/*SubSystem*/, 1/*Tries*/)
            t_oUltError := ErrorNew()

           UR_SUPER_ERROR( nWA, t_oUltError )
           Break(t_oUltError)
         ENDIF

         aWAData[ WA_RECORDSET ] := oRecordSet
         aWAData[ WA_BOF ] := aWAData[ WA_EOF ] := .F.

         UR_SUPER_SETFIELDEXTENT( nWA, nTotalFields := oRecordSet:Fields:Count )

         FOR n := 1 TO nTotalFields
           aField := Array( UR_FI_SIZE )
           aField[ UR_FI_NAME ] := oRecordSet:Fields( n - 1 ):Name
           aField[ UR_FI_TYPE ] := ADO_GETFIELDTYPE( oRecordSet:Fields( n - 1 ):Type )
           aField[ UR_FI_TYPEEXT ] := 0
           IF aField[ UR_FI_TYPE ] = adLongVarChar
              aField[ UR_FI_LEN ] := 10
           ELSE
              aField[ UR_FI_LEN ] := ADO_GETFIELDSIZE( aField[ UR_FI_TYPE ], oRecordSet:Fields( n - 1 ):DefinedSize )
           ENDIF
           aField[ UR_FI_DEC ] := 0
#ifdef UR_FI_FLAGS
           aField[ UR_FI_FLAGS ] := 0
#endif
#ifdef UR_FI_STEP
           aField[ UR_FI_STEP ] := 0
#endif
           UR_SUPER_ADDFIELD( nWA, aField )
         NEXT

         nResult := HB_SUCCESS
         IF nTotalFields > 0
            nResult := UR_SUPER_OPEN( nWA, aOpenInfo )
            IF nResult == HB_SUCCESS
               ADO_GOTOP( nWA )
            ENDIF
         ELSE
            ADO_CLOSE( nWa )
         ENDIF
       ELSE
         t_Time := Seconds()
         aWAData[ WA_CONNECTION ]:Execute( uRet )
         t_Time := Seconds() - t_Time
         //ADO_AddLog()
       ENDIF
     NEXT
   RECOVER USING t_oUltError
     nResult := HB_FAILURE
     ADO_CLOSE( nWa )
     UR_SUPER_ERROR( nWA, t_oUltError )
     //ADO_AddLog()
   END BEGIN

   Select(nWa)

   RETURN nResult

STATIC FUNCTION ADO_Connection( nWA ) //, aOpenInfo )

   LOCAL aWAData := USRRDD_AREADATA( nWA )
   LOCAL lResult
   LOCAL uRet, oSplashScreen

   BEGIN SEQUENCE WITH {|e| Break(e) }
    /*
     qt aguarda 'AGUARDE;Conectando;Servidor de dados [' + t_cserver + ']' to oSplashScreen button ok .T. button Cancela .T. message Hide()
     oSplashScreen:Ok:SetEnabled(.F.)
     oSplashScreen:Cancela:SetEnabled(.F.)
     QAppGet():ProcessEvents()
     */

     aWAData[ WA_TABLENAME ] := t_cTableName
     aWAData[ WA_QUERY ]     := t_cQuery
     aWAData[ WA_USERNAME ]  := t_cUserName
     aWAData[ WA_PASSWORD ]  := t_cPassword
     aWAData[ WA_SERVER ]    := t_cServer
     aWAData[ WA_ENGINE ]    := t_cEngine

     IF Empty( aWAData[ WA_CONNECTION ] )
       aWAData[ WA_CONNECTION ] := ADO_GetConnection()
     ENDIF

     // prever perda de connecção anteriormente conectado
     IF !Empty( aWAData[ WA_CONNECTION ] ).and. aWAData[ WA_CONNECTION ]:State != adStateOpen
       IF aWAData[ WA_CONNECTION ]:State = adStateClosed
         aWAData[ WA_CONNECTION ] := NIL
       ELSE
         oSplashScreen:Cancela:SetEnabled(.T.)
         oSplashScreen:Mensagem:SetText('AGUARDE;Conexao em andamento;Servidor de dados [' + t_cserver + ']')
         QAppGet():ProcessEvents()
         uRet := 0
         WHILE aWAData[ WA_CONNECTION ]:State != adStateClosed .and. aWAData[ WA_CONNECTION ]:State != adStateOpen .and. !oSplashScreen:Cancelado
           PausaSleep(1000)
           uRet ++
           IF uRet > 9
             EXIT
           ENDIF
         ENDDO
         //oSplashScreen:Cancela:SetEnabled(.F.)
         QAppGet():ProcessEvents()
         IF aWAData[ WA_CONNECTION ]:State != adStateClosed .and. aWAData[ WA_CONNECTION ]:State != adStateOpen
           aWAData[ WA_CONNECTION ]:Cancel()
         ENDIF
         IF aWAData[ WA_CONNECTION ]:State != adStateClosed
           aWAData[ WA_CONNECTION ]:Close()
         ENDIF
         IF aWAData[ WA_CONNECTION ]:State != adStateOpen
           aWAData[ WA_CONNECTION ] := NIL
         ENDIF
       ENDIF
     ENDIF

     IF Empty( aWAData[ WA_CONNECTION ] )
       //oSplashScreen:Mensagem:SetText('AGUARDE;Conectando;Servidor de dados [' + t_cserver + ']')
       //QAppGet():ProcessEvents()
       //ADO_AreaInfo( nWa )

       aWAData[ WA_CONNECTION ] := win_oleCreateObject( "ADODB.Connection" )

       IF Empty(t_cCS)
         t_cCS := NIL
       ENDIF
       //t_cCS := ConnectionString(aWAData[ WA_ENGINE ], 'Open', aWAData[ WA_SERVER ], iif(Empty(t_cDataBase), t_cAlias, t_cDataBase), aWAData[ WA_USERNAME ], aWAData[ WA_PASSWORD ], t_cPorta, aWAData[ WA_TABLENAME ], t_cCS )
      //ex: MYSQL, MariaDB
      //t_cCs :=  "Provider={_PROVIDER_};Driver={MariaDB ODBC 3.1 Driver};Server={_SERVER_};Port={_PORT_};Database={_DATABASE_};uid={_USER_};pwd={_PASSWORD_};Collation=latin1_swedish_ci;AUTO_RECONNECT=1;Option=3;STMT;"
      t_cCs := "Provider={_PROVIDER_};Driver={MySQL ODBC 5.1;Server={_SERVER_};Port={_PORT_};Database={_DATABASE_};uid={_USER_};pwd={_PASSWORD_};Option=3;STMT;"

       //ADO_AddLog(t_cCS)
       t_Time := Seconds()
       aWAData[ WA_CONNECTION ]:Open( t_cCS )
     ENDIF
     aWAData[ WA_CONNOPEN ] := .T.
     lResult := .T.
   RECOVER USING t_oUltError
     aWAData[ WA_CONNECTION ] := NIL
     aWAData[ WA_CONNOPEN ] := .F.
     lResult := .F.
   END BEGIN

   ADO_SetConnection(aWAData[ WA_CONNECTION ])

   t_Time := Seconds() - t_time

   oSplashScreen:Close()
   QAppGet():ProcessEvents()

   //ADO_AddLog()

   IF lResult
     ADO_AreaInfoSet(nWA)
   ENDIF

   RETURN lResult

STATIC FUNCTION ADO_GetConnection(cServer, cPorta, cDataBase, cUser, cPassWord)
LOCAL cChave, uRet

DEFAULT cServer   TO t_cServer
DEFAULT cPorta    TO t_cPorta
DEFAULT cDataBase TO t_cDataBase
DEFAULT cUser     TO t_cUserName
DEFAULT cPassWord TO t_cPassWord

cChave := Lower(cServer + '-' + cPorta + '-' + cDataBase) + '-' + cUser + '-' + cPassWord

IF hb_hPos(t_hConnection, cChave) > 0
  uRet := t_hConnection[cChave]
ENDIF

RETURN uRet


STATIC FUNCTION ADO_SetConnection(oConn, cServer, cPorta, cDataBase, cUser, cPassWord)
LOCAL nPos, cChave, uRet

DEFAULT cServer   TO t_cServer
DEFAULT cPorta    TO t_cPorta
DEFAULT cDataBase TO t_cDataBase
DEFAULT cUser     TO t_cUserName
DEFAULT cPassWord TO t_cPassWord

cChave := Lower(cServer + '-' + cPorta + '-' + cDataBase) + '-' + cUser + '-' + cPassWord

nPos   := hb_HPos(t_hConnection, cChave)

IF nPos = 0
  IF oConn <> NIL
    t_hConnection[cChave] := oConn
    uRet := oConn
  ENDIF
ELSE
  uRet := t_hConnection[cChave]
  IF oConn = NIL
    hb_HDel(t_hConnection, cChave)
  ENDIF
ENDIF

RETURN uRet

STATIC FUNCTION ADO_CLOSE( nWA )

   LOCAL aWAData := USRRDD_AREADATA( nWA )
   LOCAL oRecordSet := USRRDD_AREADATA( nWA )[ WA_RECORDSET ]

   BEGIN SEQUENCE WITH {|e| Break(e) }
      IF !Empty(oRecordSet)
        oRecordSet:Close()
      ENDIF
   RECOVER USING t_oUltError
     // faz nada
   END SEQUENCE

   BEGIN SEQUENCE WITH {|e| Break(e) }
      IF !Empty( aWAData[ WA_CONNOPEN ] ) .AND. !Empty( aWAData[ WA_CONNECTION ] )
         IF aWAData[ WA_CONNECTION ]:State != adStateClosed
            IF aWAData[ WA_CONNECTION ]:State != adStateOpen
               aWAData[ WA_CONNECTION ]:Cancel()
            ENDIF
         ENDIF
      ENDIF
   RECOVER USING t_oUltError
     // faz nada
   END SEQUENCE

   RETURN UR_SUPER_CLOSE( nWA )


Dúvida sobre classe

Enviado: 01 Mai 2022 23:22
por vailton
Se eu passo uma classe por parâmetro, eu estou passando tudo existente dentro da classe, ou somente a referência à classe?[
Um objeto é tratado como um ARRAY: é enviado sempre só a referencia dele como argumento. Ele só ocupa uma única posição da memória, mas esta referencia permite acessar sempre todas as propriedades contidas na classe (respeitando as suas próprias regras de referencia sempre que aplicável).

Dúvida sobre classe

Enviado: 02 Mai 2022 10:04
por JoséQuintas
vailton escreveu:Um objeto é tratado como um ARRAY: é enviado sempre só a referencia dele como argumento.
Legal.

Apenas pra lembrar:

Código: Selecionar todos

aList := { "a", "b", "c" }
funcao( aList )
funcao( @aList )
No primeiro caso, o ARRAY é por referência, mas no segundo caso a VARIÁVEL é por referência.
Parece a mesma coisa mas não é.
Se por acaso a sub-rotina fizer aList := { "c", "d" }
- no primeiro deixa de existir a referência, e o array original NÃO vai assumir esse valor.
- no segundo caso, onde a variável é por referência, o valor é assumido.

Fico imaginando se o objeto/classe não pode cair numa situação dessas, difícil mas não impossível.

Dúvida sobre classe

Enviado: 04 Mai 2022 16:03
por vailton

Código: Selecionar todos

aList := teste():new()
AAdd(aList:aFaltas, date() )
AAdd(aList:aFaltas, date()-45 )
AAdd(aList:aFaltas, date()-50 )

funcao( aList )
? toString(aList)

funcao( @aList )
? toString(aList)
quit

class teste
   data nSalario init 0
   data aFaltas  init {}
endclass

function funcao(aList)
   aList:aFaltas[1] := CtoD('')
   aList := { "c", "d" }
   return nil
Produzirá a seguinte saida na tela (minha funcao toString() faz um dump do valor passado pra depuracao):

Código: Selecionar todos

TESTE():New() {"NSALARIO" => 0,
"AFALTAS" => {CTOD('  /  /    '),CTOD('20/03/2022'),CTOD('15/03/2022')}
}
{"c","d"}
Isto demonstra que se o valor for um ARRAY, HASH ou OBJETO o valor só será instanciado uma vez e quando enviamos este valor como argumento de uma função, ele aponta sempre para o item original economizando recursos.
Se eu passo uma classe por parâmetro, eu estou passando tudo existente dentro da classe, ou somente a referência à classe?
Ele envia uma referencia ao objeto original (mesmo que voce nao passe o parametro como referencia) para economizar memoria, ganhar desempenho e com isto disponibiliza à voce todas as informações da classe instantaneamente.