Clase odbcConn (VFP)

From codeWiki
Revision as of 08:26, 17 December 2015 by VictorEspina (Talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Es muy común ver en los foros de programación VFP preguntas tales como como trabajar con SQL Server?, debo usar una conexión permanente o conectarme solo cuando lo necesito?, y varias mas por el estilo. Aunque estos temas dan para mucha discusión, senti que mucho del problema se solucionaría si existiera una forma fácil de acceder a datos remotos y de manejar las conexiones con estas fuentes, tanto en forma conectada como en forma desconectada. Habiendo enfrentado estos temas decenas de veces en los últimos anos, decidí crear esta clase que resumen muchas de las practicas que yo mismo he venido implementando desde hace mas de 10 anos. La clase odbcConn hace unas pocas cosas:


  • Permite abrir y cerrar una conexión con una fuente de datos ODBC
  • Permite enviar comandos a esa conexión y obtener resultados (si los hubiera)
  • Permite ejecutar esos comandos tanto en forma conectada como en forma desconectada (es decir, con conexión por demanda)
  • Permite crear y cerrar transacciones sobre esa conexión.


He aquí el código; ojalá les sea de tanta utilidad como a mi.

* odbcConn (Clase)
* Implementacion basica de un asistente para conexion con
* fuentes de datos odbc y ejecucion de comandos tanto en
* escenario conectados como desconectados
*
* Autor: V. Espina
* Fecha: Feb 2014
*
* CODIGO LIBRE - USELO BASO SU PROPIA RESPONSABILIDAD
*
* EJEMPLOS:
* a) Establecer una conexion con un origen de datos:
*
*    oConn = CREATEOBJECT("odbcConn", "string de conexion")
*
*
* b) Obtener el contenido de una tabla en un cursor, usando modo desconectado. En este modo
*    la clase abre y cierra la conexion automaticamente:
*
*    IF NOT oConn.Execute("SELECT * FROM clientes", "QCLIENTES")
*     MESSAGEBOX(oConn.lastError)
*     RETURN .F.
*    ENDIF
*    SELECT QCLIENTES
*    BROWSE
*    USE
*
* c) Obtener el contenido de un registro en una tabla, usando modo conectado. En este modo
*    la conexion se abre explicitamente y se mantiene abierta durante el tiempo necesario:
*
*    IF NOT oConn.Open()
*     MESSAGEBOX(oConn.lastError)
*     RETURN .F.
*    ENDIF
*    oRow = oConn.executeRow("SELECT * FROM Clientes WHERE codigo = ?cCodigo")
*    ?oRow.codigo, oRow.nombre
*    oConn.Close()
*   
* d) Manejo de transacciones:
*
*    oConn.Open()
*    oConn.beginTransaction()
*    oConn.executeNonQuery(...)
*    oConn.executeNonQuery(...)
*    oConn.Commit()
*    oConn.Close()
*
* e) Insert a new row in a table:
*
*    PRIVATE cCodigo, cNombre
*    cCodigo = "0001"
*    cNombre = "JHON SMITH"
*    oConn.executeNonQuery("INSERT INTO clientes (codigo, nombre) VALUES (?cCodigo, ?cNombre)")
*


#DEFINE TRUE	.T.
#DEFINE FALSE	.F.

DEFINE CLASS odbcConn AS Custom

 HIDDEN _nHandle    && Handle de la conexion ODBC
 HIDDEN _cConnStr   && String de conexion (interno)
 HIDDEN _cErrorMsg  && Texto del ultimo error ocurrido
  
 connString = ""       && String de conexion (Propiedad, S/L)
 Opened = .F.          && Estado de la conexion (Propiedad, S/L)
 lastError = ""        && Ultimo error ocurrid (Propiedad, S/L)
 handle = 0            && ID de la conexion (Propiedad, S/L) 

 PROCEDURE connString_Access   && ConnString Getter/Setter
  RETURN THIS._cConnStr
 ENDPROC
 PROCEDURE connString_Assign(vNewVal)

 PROCEDURE handle_Access   && Handle Getter/Setter
  RETURN THIS._nHandle
 ENDPROC
 PROCEDURE handle_Assign(vNewVal)
 
 PROCEDURE Opened_Access       && Opened Getter/Setter
  RETURN (THIS._nHandle <> 0)
 ENDPROC
 PROCEDURE Opened_Assign(vNewVal)
 
 PROCEDURE lastError_Access    && LastError Getter/Setter
  IF EMPTY(THIS._cConnStr)
   RETURN "No se indico una cadena de conexion"
  ENDIF
  RETURN THIS._cErrorMsg
 ENDPROC
 PROCEDURE lastError_Assign(vNEwVal)
 
 
 
 * Constructor
 *
 PROCEDURE Init(pcConnString)
  THIS._cConnStr = pcConnString
  THIS._nHandle = 0 
 ENDPROC
 
 
 * Open
 * Abre una conexion 
 *
 PROCEDURE Open
  IF THIS.Opened
   RETURN TRUE
  ENDIF
  IF EMPTY(THIS.connString)
   RETURN FALSE
  ENDIF
  LOCAL nResult
  nResult = SQLSTRINGCONNECT(THIS.connString)
  IF nResult > 0
   THIS._nHandle = nResult
  ELSE
   THIS._cErrorMsg = THIS.getLastError()
  ENDIF
  RETURN (nResult > 0)
 ENDPROC
 
 
 * Close
 * Cierra una conexion
 *
 PROCEDURE Close
  IF THIS.Opened
   SQLDISCONNECT(THIS._nHandle)
   THIS._nHandle = 0 
  ENDIF
 ENDPROC
 
 
 
 * Execute
 * Ejecuta una instruccion SQL. Si no hay una conexion
 * abierta, se abre y se cierra automaticamente.
 *
 PROCEDURE Execute(pcSQL, pcCursor)
  LOCAL lAutoConn
  lAutoConn = (!THIS.Opened)
  IF lAutoConn 
   IF NOT THIS.Open()
    RETURN FALSE
   ENDIF
  ENDIF
  LOCAL nResult
  IF !EMPTY(pcCursor)
   nResult = SQLEXEC(THIS._nHandle, pcSQL, pcCursor)
  ELSE
   nResult = SQLEXEC(THIS._nHandle, pcSQL)  
  ENDIF
  IF nResult < 0
   THIS._cErrorMsg = THIS.getLastError()
  ENDIF
  IF lAutoConn
   THIS.Close()
  ENDIF
  RETURN (nResult > 0)
 ENDPROC


 * executeNonQuery
 * Ejecuta una instruccion que no devuelve resultados
 *
 PROCEDURE executeNonQuery(pcSQL)
  RETURN THIS.Execute(pcSQL)
 ENDPROC


 * executeScalar
 * Ejecuta una instruccion y devuelve el valor de la primera
 * columna en el primer registro. Si el comando da un error
 * o no se encontraron resultados, se devuelve NULL
 *
 PROCEDURE executeScalar(pcSQL)
  LOCAL cCursor,uResult,nWkArea
  cCursor = SYS(2015)
  uResult = NULL
  nWkArea = SELECT()
  IF NOT THIS.Execute(pcSQL, cCursor)
   RETURN NULL
  ENDIF
  SELECT (cCursor)
  IF RECCOUNT() > 0
   uResult = EVAL(FIELD(1))
  ENDIF
  USE
  SELECT (nWkArea)
  RETURN uResult
 ENDPROC
 

 * executeRow
 * Ejecuta una instruccion y devuelve un objeto con los valores
 * del primer registro. Si el comando da un error o no se 
 * encontraron resultados, se devuelve NULL
 *
 PROCEDURE executeRow(pcSQL)
  LOCAL cCursor,oResult,nWkArea
  cCursor = SYS(2015)
  oResult = NULL
  nWkArea = SELECT()
  IF NOT THIS.Execute(pcSQL, cCursor)
   RETURN NULL
  ENDIF
  SELECT (cCursor)
  IF RECCOUNT() > 0
   SCATTER NAME oResult MEMO
  ENDIF
  USE
  SELECT (nWkArea)
  RETURN oResult
 ENDPROC
 
 
 * setDatabase
 * Establece una BD por omision en la conexion
 *
 PROCEDURE setDatabase(pcDBName)
  IF NOT THIS.Opened
   RETURN FALSE
  ENDIF
  RETURN THIS.executeNonQuery("USE " + pcDBName)
 ENDPROC
  
 
 * beginTransaction
 * Iniciar una transaccion en la conexion actual
 *
 PROCEDURE beginTransaction
  IF NOT THIS.Opened
   RETURN FALSE
  ENDIF
  RETURN THIS.executeNonQuery("BEGIN TRANSACTION")
 ENDPROC
 

 * Commit
 * Cierra una transaccion y guarda los cambios realizados
 *
 PROCEDURE Commit
  IF NOT THIS.Opened
   RETURN FALSE
  ENDIF
  RETURN THIS.executeNonQuery("COMMIT")
 ENDPROC
 

 * Rollback
 * Cierra una transaccion y descarta los cambios realizados
 *
 PROCEDURE Rollback
  IF NOT THIS.Opened
   RETURN FALSE
  ENDIF
  RETURN THIS.executeNonQuery("ROLLBACK")
 ENDPROC
 
 
 
 * getLastError (Hidden)
 * Devuelve la descripcion del ultimo error ocurrido
 * 
 HIDDEN PROCEDURE getLastError
  LOCAL ARRAY aErrInfo[1]
  AERROR(aErrInfo)
  RETURN aErrInfo[2]
 ENDPROC
 
ENDDEFINE
Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox