Conexiones JDBC Thin a una base de datos Oracle

Hoy vamos a ver una entrada rápida y útil sobre la manera de configurar los clientes JDBC para acceder a los motores de la base de datos. Tradicionalmente los desarrolladores e integradores Java son muy dados a configurar los pooles de conexión hardcodeando los datos de la conexión en el fichero de propiedades de la misma de la manera IP:puerto:SID
Esta codificación podía ser muy buena en los anales de la historia de Java, pero con la versión del jdbc del cliente 10.2.0.1 se introdujeron una serie de mejoras que, desgraciadamente no suelen ser aplicadas por los desarrolladores/integradores.

Veamos pues la maneras que hay de configurar el jdbc

Oracle JDBC Thin usando SID

Desgraciadamente es la mas utilizada de todos, en esta se fija en cada fichero de pool de conexión tanto la Ip como el puerto como el SID de base de datos, lo que además de hacer muy costoso el cambio de alguno de los tres elementos, imposibilita todas las funciones asociadas a los servicios de Oracle.
jdbc:oracle:thin:@//host:puerto:ORACLE_SID
Ejemplo :

 jdbc:oracle:thin:@//192.168.73.6:1521:TEST

Oracle JDBC Thin usando Service Name:

Esta opción es similar a la anterior, solamente que se cambia el ORACLE_SID por el SERVICE_NAME, dista mucho de ser buena, pero al menos nos permite utilizar servicios.
jdbc:oracle:thin:@ //host:puerto:SERVICE_NAME
Ejemplo :

jdbc:oracle:thin:@//192.168.73.6:1521/TEST

Oracle JDBC Thin usando a TNSName:

Este es el método que deberíamos de recomendar, puesto que mantiene centralizado en el TNSNAMES.ora la gestión de la notación de las bases de datos Oracle
jdbc:oracle:thin:@TNSName
Ejemplo :

jdbc:oracle:thin:@TEST 

Esta funcionalidad fue añadida en la versión 10.2.0.1(lleva disponible desde mediados del 2004) por lo que en un sistema bien mantenido no deberíamos de tener ningún problema la compatibilidad el driver jdbc de Oracle que usemos (al contrario debería de ser un problema si usamos un driver mas antiguo de una 10.2.0.1)

A la hora de utilizar el TNSNAMES, nos encontramos con que el servidor de aplicaciones deberá de conocer la ubicación de este fichero. Para ello usamos la variable de entorno del sistema operativo que referencia a este fichero, la variable TNS_ADMIN .unido a la variable de entorno del sistema, el servidor de aplicaciones debe de contar con una propiedad específica para encontrarla , esta propiedad es oracle.net.tns_admin que se le puede pasar en en arranque del java como como java -Doracle.net.tns_admin=$TNS_ADMIN

Mas información como siempre en las notas de soporte

  • Can JDBC Thin Driver Connect Using Tns Entry in tnsnames.ora File (Doc ID 333686.1)
  • JDBC/thin does not Currently Support the IFILE Clause Inside a TNSNAMES.ORA File (Doc ID 1270872.1)

Pasar variables de entorno windows a un código PL/SQL

Nos hemos mudado a bloger!
El contenido actualizado de esta entrada lo tienes en:

    http://dba.pamplona.name/2013/12/pasar-variables-de-entorno-windows-un.html

    Hoy vamos a ver algo muy muy sencillo pero que puede generar un quebradero de cabeza cuando,acostumbrado a entornos Unix se trabaja con windows.

    Veamos como pasar parámetros por variables de entorno en unix

    consulta.sh

    #!/bin/bash
    export RUTA=/opt/oracle/logs
    export ORACLE_SID=miinstancia
    sqlplus -s user/pass @consulta.sql

    consulta.sql

    spool ${RUTA}/${INSTANCIA}.log
    select sysdate from dual;
    spool off 
    exit;

    ¿Como podemos conseguir lo mismo en un entorno windows?

    consulta.bat

    SET RUTA=d:\oracle\logs
    SET ORACLE_SID=miinstancia
    sqlplus -s user/pass @consulta.sql

    consulta.sql

    spool %RUTA%/%INSTANCIA%.log
    select sysdate from dual;
    spool off 
    exit;

    Como podemos ver, la diferencia es bastante pequeña y el funcionamiento final es el mismo, es un simple problema usar las variables de entorno propias de cada sistema operativo.

Truncate table sin permisos TRUNCATE ANY TABLE

Hoy vamos a ver rápidamente un tema muy sencillo que puede traernos algun que otro dolor de cabeza. Intentar truncar una tabla de otro esquema.

Supongamos que tenemos una aplicación que utiliza dos esquemas:

ADMINISTRADOR-> Propietario de los datos y de todos los objetos del esquema
APLICACION-> Usuario de explotación de los datos que no tiene objetos propios y que accede a los datos de ADMINISTRADOR mediante grants

Este modelo de aplicación es muy común ya que nos garantiza que , el esquema APLICACION nunca va a modificar la estructura del modelo de datos, su funcionamiento se base en dar los grants
DELETE, INSERT, SELECT, UPDATE para todas las tablas del esquema ADMINISTRADOR, lo que permite a el usuario APLICACION borrar todos los datos de una tabla de ADMINISTRADOR, pero no es posible que lleve a cabo una acción de truncado, ya que , para Oracle el truncado es una acción distinta a el borrado (no lo hace sobre los datos sino sobre la propia tabla).

El problema con el que nos encontramos es que, oracle no contempla el otorgar un permiso «TRUNCATE TABLE» a un usuario sobre los objetos de otro usuario, el único permiso que contempla es el de «TRUNCATE ANY TABLE»; lo que es una brecha de seguridad ya que permitiría al usuario APLICACION el truncar cualquier tabla de cualquier otro esquema de la base datos.

¿Como se soluciona el problema?

La solución es muy sencilla, y pasa por hacer una funcion en el esquema ADMINISTRADOR que trunque la tabla que le pasamos por parámetro.
EL procedure sería tal que


create or replace procedure truncartabla(tabla_a_truncar varchar2) 
is
 begin
    execute immediate 'truncate table ' || tabla_a_truncar ;
 end;


Despues de eso , deberemos de dar permisos de ejecución sobre este procedimiento al usuario APLICACION (seguimos como usuario ADMINISTRADOR)


 grant execute on truncartabla to APLICACION

Ahora ya podremos truncar tablas desde el usuario expfin, el único cambio que habrá que hacer es, en el código SQL donde pone


TRUNCATE TABLE ADMINISTRADOR.TABLA1

por


execute ADMINISTRADOR.truncartabla('TABLA1');


Introduccion a los servicios de Oracle

Uno de los elementos mas potentes que introdujo Oracle en la versión 10g fué el uso de servicios.
Los servicios de Oracle no son otra cosa que una abstracción lógica de una instancia de base de datos. Aunque los servicios tienen mas sentido en entorno RAC, hoy vamos a ver como se configuran y para que pueden servir en un entorno de «single instance».

Supongamos tenemos una base de datos produccion en la que tenemos consolidados 4 entornos distintos ( Webfotos,Cargas,Contabilidad y Desarrollo ) que acceden a nuestra instancia con el mismo esquema,y desde servidores de aplicaciones que comparten máquina entre ellos, en el momento en que la base de datos tiene problemas nos es muy difícil el saber quien es quien.
Si pudiésemos discriminar las conexiones por una agrupación lógica, sería mas fácil el verlo, y , mas aún, si el EMC fuese capaz de separar las gráficas y estadísticas por esa agrupación.

Pues esto es exactamente lo que nos proporcionan los servicios Oracle.
En nuestro caso ficticio, vamos a crear los distintos servicios:

  • Webfotos
  • Cargas
  • Contabilidad
  • Desarrollo

De esta forma, cada conexión de estos 4 entornos usara un service_name distinto en su TNS_NAMES de cliente, y , la base de datos podrá identificar ( y limitar) a cada uno de ellos de manera separada.

Lo primero que tendremos que hacer es crear los servicios. Para esto tenemos dos maneras, o bien desde el srvctl ( en caso de RAC,Grid control u Oracle restart), o mediante el paquete DBMS_SERVICES , como nuestro caso es el de una «single instance», no nos va a quedar mas remedio que usar este paquete.
Mediante la función CREATE_SERVICE crearemos los servicios de la manera:

exec dbms_service.CREATE_SERVICE(SERVICE_NAME=>'webfotos', NETWORK_NAME=>'webfotos')
exec dbms_service.CREATE_SERVICE(SERVICE_NAME=>'cargas', NETWORK_NAME=>'cargas')
exec dbms_service.CREATE_SERVICE(SERVICE_NAME=>'contabilidad', NETWORK_NAME=>'contabilidad')
exec dbms_service.CREATE_SERVICE(SERVICE_NAME=>'desarrollo', NETWORK_NAME=>'desarrollo')

Una vez creados los servicios, los arrancaremos con la función

exec dbms_service.START_SERVICE('webfotos')
exec dbms_service.START_SERVICE('cargas')
exec dbms_service.START_SERVICE('contabilidad')
exec dbms_service.START_SERVICE('desarrollo')

Para comprobar si la creación de nuestros servicios ha funcionado, podemos chequear el parámetro service_names

SQL> select value from v$parameter where NAME='service_names';
VALUE
---------------------------
produccion,webfotos,cargas,contabilidad,desarrollo

O bien el listener con lsnrctl services

[oracle@blog] [$lsnrctl services

LSNRCTL for Linux: Version 11.2.0.2.0 - Production on 17-MAY-2013 13:31:06

Copyright (c) 1991, 2011, Oracle.  All rights reserved.

Connecting to (ADDRESS=(PROTOCOL=tcp)(HOST=)(PORT=1521))
Services Summary...
Service "PLSExtProc" has 1 instance(s).
  Instance "PLSExtProc", status UNKNOWN, has 1 handler(s) for this service...
    Handler(s):
      "DEDICATED" established:0 refused:0
         LOCAL SERVER
Service "produccion" has 1 instance(s).
  Instance "XE", status READY, has 1 handler(s) for this service...
    Handler(s):
      "DEDICATED" established:34 refused:0 state:ready
         LOCAL SERVER
Service "webfotos" has 1 instance(s).
  Instance "XE", status READY, has 1 handler(s) for this service...
    Handler(s):
      "DEDICATED" established:34 refused:0 state:ready
         LOCAL SERVER
Service "contabilidad" has 1 instance(s).
  Instance "XE", status READY, has 1 handler(s) for this service...
    Handler(s):
      "DEDICATED" established:34 refused:0 state:ready
         LOCAL SERVER
Service "desarrollo" has 1 instance(s).
  Instance "XE", status READY, has 1 handler(s) for this service...
    Handler(s):
      "DEDICATED" established:34 refused:0 state:ready
         LOCAL SERVER
Service "cargas" has 1 instance(s).
  Instance "XE", status READY, has 1 handler(s) for this service...
    Handler(s):
      "DEDICATED" established:34 refused:0 state:ready
         LOCAL SERVER
The command completed successfully

Ahora, solamente tendremos que modificar los respectivos TNSNAMES de los distntos entornos para que se conecten mediante SERVICE_NAME y no mediante SID y tendremos identificados cada una de la sesiones de oracle con el servicio.

¿que beneficios nos aporta todo esto?

  • Trazabilidad: Nos va a ser sencillísimo encontrar quien es el que esta haciendo algo ya que en un primer vistazo encontraremos al culpable «logico» del problema, una vez tenemos el origen del problema es mas sencillo abordarlo.
  • Accounting: Vamos a poder ser capaces de ver los consumos de cada aplicacion/grupo lógico en la base de datos, lo que nos puede ser muy bueno a la hora de derivar costes o limitar recursos
  • control de accesos: Si en un momento específico queremos asegurarnos de que un elemento lógico no acceda a la aplicacion, podemos detener el servicio y el resto funcionaría correctamente.Esto puede ser muy útil por ejemplo, para evitar cargas en horario diurno, o para controlar los equipos de desarrollo

Hasta ahora lo hemos visto todo muy fácil, pero .. ¿que ocurre cuando reinicias la base de datos?.
Para que la instancia levante los servicios al arrancar deberán de estar en el init.ora con la sintaxsis:


service_names='produccion','webfotos','cargas','desarrollo'

Si no es así , cuando se levante nuestra base de datos no se levantan los servicios, y esto hace que no funcione nada de lo que apunta a ellos.
La solución como os decía la principio es hacerlo desde el srvctl, pero …
¿como lo hacemos si no tenemos RAC,Grid u Oracle restart?
La respuesta es brutalmente sencilla.

[oracle@blog] [$sqlplus "/as sysdba"
SQL*Plus: Release 11.2.0.2.0 Production on Vie May 17 13:09:11 2013
Copyright (c) 1982, 2011, Oracle.  All rights reserved.

Connected to:
Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production
SQL> alter system set service_names='produccion,webfotos,cargas,contabilidad,desarrollo';
System altered.

Tan sencillo como acabáis de ver, simplemente hemos de conectarnos desde el sqlplus y hacer un ALTER SYSTEM para el parámetro service_names poniendo nuestros servicios separados por comas.

Como siempre, para mas información, tenemos la documentación de Oracle del paqueteDBMS_SERVICES

Problemas con los requerimientos del cliente 11gr2 en windows

Volvemos tras las vacaciones navideñas con una pequeña entrada de esas tremendamente simples, pero que pueden ser evitarnos una gran pérdida de tiempo.

Una de las cosas mas engorrosas de las instalaciones de Oracle es la instalación del cliente, en primer lugar, porque pocas veces nos especifican que es exactamente los componentes del cliente que necesitan, y en segundo lugar, por que, hasta estas últimas versiones la instalación ( especialmente la desinstalación) del cliente de windows era muy engorrosa.

Pues bien, el otro día en la instalación de un cliente 11gr2 en windows me encontré con un error nuevo:

error_instalacion_XP

Mi windows XP detectaba un error en los requerimientos de la instalacion del cliente (no instantclient), cuando, todos los requerimientos eran correctos.

La solución es tan sencilla como el habilitar el uso compartido de C$. Parece ser que el instalador utiliza «\\< servidor >\C$\temp» , con lo que si no está habilitado el recurso, la instalación falla.

La información completa del caso está (como siempre) en metalink, en la nota «Installation of 11gR2 on Windows Fails Checking Requirements [ID 1133495.1]»