Comparación de constructores en Java y en Python

En el curso de python me he liado a explicar cómo construcir excepciones propias de la aplicación. Para ello hay que hereadar de la clase Exception. La clase Exception tiene un constructor que admite n parámetros cualesquiera y se los guarda. Es una forma de guardar información adicional al levantar una excepción.

Pues me encontré una sorpresa. Cree mi propia excepción heredando de Exception de la forma más simple posible

class MyException(Exception):
pass

y resulta que sólo con eso puedo hacer cosas como

error = MyException(param1, param2, param3,…)

es decir, puedo añadir los parámetros que quiera usando el constructor de la clase padre.

Como vengo de Java, donde esto no es así, me llamó poderosamente la atención y me puse a investigar.

En Java sólo puedes llamar a constructores que hayas declarado explicitamente en la clase hija, o al constructor por defecto sin parámetros si no has declarado ninguno. Esto es así porque Java obliga a llamar a un constructor de la clase hija y este a su vez y de forma automática llama a un constructor de la clase padre. Al de defecto si no le hemos indicado explícitamente que haga otra cosa

En Python la resolución de constructores no es así. Si tu instancias una clase hija con un constructor (unos parámetros concretos), Python busca ese constructor en la clase hija. Si lo hay lo llama y no va a llamar de forma automática al de la clase padre. Si no lo hay en la clase hija, busca ese constructor en la clase padre (o en las clases padre en determinado orden, porque Python admite herencia múltiple) y si lo encuentra lo llama.

Así que en Python es perfectamente posible construir una clase hija sin que se llame a ningún constructor de la clase padre o que se se instancia una clase hija sin que se llame a ningún constructor de la clase hija.

En Java esto es más rígido. Si instancias una clase hija, obligatoriamente se tiene que llamar a un constructor de la clase hija y este llamará de forma automática a un constructor de la clase padre.

Sin embargo, para métodos normales y atributos el comportamiento de ambos lenguajes es más similar. Si en una instancia de una clase hija llamas a un método, ambos lenguajes buscan el método en la clase hija y lo llaman si existe. Si no existe, lo buscan en la clase padre y lo llaman si existe.

La diferencia entre ambos lenguajes es solo en los constructores. Java obliga a una llamada en cascada de constructores hijo, padre, abuelo… mientras que Python los trata como si fueran métodos normales.

Publicado en java, python | Etiquetado , , , | Deja un comentario

Bases de datos en python

En el curso de Python le ha tocado al tema de base de datos. La verdad es que no tengo muy claro que orden seguir con el curso, pero le ha tocado esto 😛

Lo primero que me ha llamado la atención es que Python viene, con la instalación estándar, con la base de datos SQLite embebida. Esto me ha venido estupendo porque en el curso. Me ahorro indicar que hay que instalarse una base de datos u otra para poder seguir los ejemplos. Como Python viene con SQLite, pues simplemente usar esa en los ejemplos y cualquiera puede seguirlos sin necesidad de instalarse nada adicional.

El siguiente punto que me ha llamado la atención es el tema de cómo se implementan los módulos de base de datos. En java existen una serie de clases e interfaces predefinidos, lo que se llama la JDBC (Java Database Connectivity). En Python intentan lo mismo, es la Python Database Specification PEP 249. Pero se queda un poco cojo por las características del lenguaje. En Java, al ser de tipado estático, puede «obligar» a que los driver de conexión de base de datos cumplan más o menos a rajatabla la especificación JDBC. En Python, puesto que es de tipado dinámico, la especificación se queda en una especie de descripción de qué métodos y clases tienen que implementar los módulos de conexión a base de datos, pero no puede «obligar» tanto como lo hace java. Esto hace que sí, que quizás todos los módulos cumplen la especificación, más o menos, pero no tienes tantas garantías de que el código sea reutilizable si cambias de base de datos.

Otro punto que me ha llamado la atención y este sí es a favor de Python, es la facilidad de todo esto. Apenas hay un par de clases en la especificación con unos cuantos métodos de los cuales realmente con tres o cuatro podemos hacer prácticamente todo. La JDBC de Java es bastante más compleja.

Y finalmente otro punto, que no sé qué pensar, es el tema de fechas, horas y timestamps. Java tiene soporte para ello, tienen tipos propios java.sql.Date, java.sql.Time y java.sql.Timestamp, que se usan en JDBC y se convierten correctamente a los tipos Date, Time y Timestamp de las columnas de base de datos. Python, hasta donde he visto, no tiene este soporte. Para manejar fechas y horas, debemos hacer las conversiones a los String en los formatos que los soportan las bases de datos. No es costoso, pero no sé si es cómodo.

Publicado en java, python | Etiquetado , , , | Deja un comentario

Python timezone. Módulo pytz

Estaba entretenido mirando todo el tema de datetime en python y me puse a mirar todo el tema de husos horarios o timezone. Como todos sabemos, cada país tiene una hora de su padre y de su madre, según el huso o zona horaria en la que esté.

En el 99% de las aplicaciones de escritorio, sean en python o no, esto no suele dar mayores problemas. La aplicación solo corre en un ordenador y la hora es la del ordenador en el timezone que le toque.

Pero si es una web que se va a publicar en internet que se vaya a ver en varios paises o es una aplicación de escritorio más compleja que se pueda instalar en clientes de varios paises y que vayan contra una base de datos o servidor centralizado, pues es importante tener en cuenta todo esto para presentar a cada usuario su hora correcta en función de su huso horario.

En python he visto una cosa que en parte me ha gustado. Y es que los módulos por defecto de python, como datetime, sólo entienden de hora local y de hora UTC / GMT. No entienden de horas de otros paises de una forma fácil. Siempre tenemos la opción de decir GMT+2 o GMT-4, pero desde luego eso obliga a saber ese incremento respeto a GMT para un país concreto. Y si encima vamos con horarios de verano e invierno, más lio, porque ese incremento cambia según la fecha del año.

¿Por qué digo que me ha gustado esto que aparentemente es una pega?. Porque como he comentado, el 99% de los casos no necesitamos más. La hora local va que chuta.

¿Y si queremos saber las horas de otros paises?. Hay varios módulos, algunos ya instalados en python, como zoneinfo, que de alguna forma ayudan. Pero en una investigación superficial, no me he matado mirando, no los he visto suficientemente cómodos y completos. Pero sí me ha gustado el módulo adicional pytz, abreviatura de Python Timezone.

La instalación es sencilla, python viene con un programa de nombre pip que permite instalar módulos externos desde internet. Este simple comando debería hacer el trabajo.

pyp install pytz

Si todo va bien, se habrá instalado. Luego basta usarlo en cuaqluier función de datetime que admita como parámetro un timezone. Por ejemplo (en azul la salida de los comandos)

>>> datetime.now(pytz.timezone(‘America/New_York’))
datetime.datetime(2022, 7, 30, 2, 43, 56, 551152, tzinfo=<DstTzInfo ‘America/New_York’ EDT-1 day, 20:00:00 DST>)

>>> datetime.now(pytz.timezone(‘Europe/Madrid’))
datetime.datetime(2022, 7, 30, 8, 44, 2, 924587, tzinfo=<DstTzInfo ‘Europe/Madrid’ CEST+2:00:00 DST>)

>>> datetime.now(pytz.timezone(‘CET’))
datetime.datetime(2022, 7, 30, 8, 44, 13, 512663, tzinfo=<DstTzInfo ‘CET’ CEST+2:00:00 DST>)

>>> datetime.now(pytz.timezone(‘Zulu’))
datetime.datetime(2022, 7, 30, 6, 44, 18, 40771, tzinfo=<StaticTzInfo ‘Zulu’>)

Ahí tenemos cuatro ejemplos para obtener la hora actual en New York, en Madrid, en CET (Central European Time, que coincide con la de Madrid) y en hora Zulu (similar a GMT y UTC).

O podemos convertir fechas de un país a otro

>>> zulu = datetime(2022,7,4,5,6,7,123456,pytz.timezone(‘Zulu’))
>>> zulu
datetime.datetime(2022, 7, 4, 5, 6, 7, 123456, tzinfo=<StaticTzInfo ‘Zulu’>)

>>> madrid = zulu.astimezone(pytz.timezone(‘Europe/Madrid’))
>>> madrid
datetime.datetime(2022, 7, 4, 7, 6, 7, 123456, tzinfo=<DstTzInfo ‘Europe/Madrid’ CEST+2:00:00 DST>)

Aquí obtenemos una fecha/hora concreto con timezone Zulu y luego averiguamos qué hora era en Madrid en esa hora Zulu, usando la función datetime.astimezone()

A partir de un timestamp también es sencillo

>>> timestamp = datetime.now().timestamp()
>>> datetime.fromtimestamp(timestamp)
datetime.datetime(2022, 7, 30, 9, 5, 36, 831705)

>>> datetime.fromtimestamp(timestamp, pytz.timezone(‘America/New_York’))
datetime.datetime(2022, 7, 30, 3, 5, 36, 831705, tzinfo=<DstTzInfo ‘America/New_York’ EDT-1 day, 20:00:00 DST>)

La función datetime.fromtimestamp() presupone hora local si no le ponemos parámetro, pero si le ponemos un timezone de parámetro, nos muestra el datetime en ese país.

Para saber qué timezone hay disponibles, la llamada pytz.all_timezones nos da un listado de todas las disponibles.

Publicado en python | Etiquetado , , , , | Deja un comentario

Algunas pegas de Python

Estoy últimamente aprendiendo Python y lo que aprendo lo voy escribiendo en un curso de python en la chuwiki. Ya lo sabía desde hace tiempo y no me gustaba, pero en los ejemplos que he ido haciendo para el curso, me he tropezado con el problema de una forma inesperada.

¿Cual es el problema?. Básicamente que si tienes una clase Python, por ejemplo, esta

class Clase:
pass

Una simple clase, he puesto pass simplemente por no complicarme, pon ahí todo lo que quieras. Pues bien, si en código yo hago esto

Clase.pepe = ‘hola’

simplemente lo admite y crea el atributo pepe en la clase. Bien, esto puede ser estupendo, nos permite crear atributos de una forma dinámica si hace falta. Pero la pega que siempre he encontrado es que somos humanos y como humanos nos equivocamos, sobre todo al teclear. Estoy seguro que la tecla del del teclado es la más usada. ¿Qué pasa si la clase tiene un atributo nombre y nos equivocamos al teclearlo?

class Clase:
nombre = »

Clase.nonbre = ‘Pedro’

Pues fácil, tenemos un atributo nombre sin valor y un nuevo atributo creado al vuelo nonbre con el valor correcto. No hace falta mucha imaginación para darse cuenta la de problemas de depuración que nos puede dar esto si no nos damos cuenta. De hecho, seguro que no habrías visto nada raro si no te digo la estadística de la tecla del antes.

Pero bueno, estoy ya lo sabía e incluso creo que lo comenté en algún post anterior. El problema con el que me he encontrado ahora es el siguiente. Imagina que tenemos la clase con el nombre y hacemos esto

>>> pedro = Clase()
>>> pedro.nombre = ‘Pedro’
>>> print(pedro.nombre)
‘Pedro’
>>> print(Clase.nombre)
»

Pues bien, resulta que hemos creado una instancia pedro, le hemos asignado un nombre y se lo ha asignado. Pero no en el atributo nombre de la clase, sino creando un atributo nombre específico para la instancia y que oculta el atributo nombre de la clase.

Todo correcto, es el comportamiento esperado de Python. Aunque no sé si me convence. Como he comentado, somos humanos. El atributo de clase es compartido por todas las instancias y podemos quizás querer cambiarle el valor en algún momento de la ejecución de nuestro código. La forma correcta de cambiarlo para que afecte a todas las instancias sería usando el nombre de la clase, es decir Clase.nombre=’valor para todas las instancias’. Pero los humanos seguimos siendo humanos y nos equivocamos, tanto programadores novatos como expertos. Los primeros se lo piensan mucho y meten la pata. Los segundo meten la pata directamente. Entra dentro de lo posible que al querer cambiar ese nombre de la clase usemos una instancia como en el ejemplo. Nuevamente un dolor de cabeza de depuración.

Sin quitarle valor a python, que se ha ganado su puesto como lenguaje más utilizado, sobre todo por temas de inteligencia artificial, este es el motivo por el que no me gustan los lenguajes en los que no se declaran las variables previamente para desarrollos en serio. Por desarrollo en serio me refiero a desarrollos grandes en los que hay implicados varios desarrolladores, humanos todos ellos. En un lenguaje con declaración de variables, si cualquier de ellos se equivoca al escribir el nombre de una variable, o la mete en una clase/instancia que no tiene que estar, como no se ha declarado previamente, el error salta según el programador humano hace sus cosas humanas. En lenguajes donde no es necesario declarar las variables, esto no «canta» hasta la ejecución, donde te tocará echar tiempo depurando.

Publicado en python | Etiquetado , , | Deja un comentario

Paquetes Java vs Python

Hace muchos años aprendí Java. Recuerdo que me llamó mucho la atención y me pareció excesivamente rebuscado el tema de los paquetes (package). Había que crear una estructura de directorios similar a la estructura de paquetes que queremos. Es decir, si una clase está en package com.chuidiang.ejemplos, debemos crear un directorio «com», dentro de el un subdirectorio «chuidiang», dentro de él un subdirectorio «ejemplos» y dentro de este, finalmente, el fichero .java con la clase.

Últimamente estoy en proceso de aprender Python y ponerlo en un curso de python. Justamente ayer me entretuve con el tema de módulos y paquetes en Python. Y cual no fue mi sorpresa que he encontrado muchas similitudes con Java.

En Python también debes crear una estructura de directorios similar a cómo quieras organizar los paquetes.  A los ficheros Python que pongas ahí no hace falta ponerles nada similar a package, como en Java. Basta con que esté ubicados en el subdirectorio que corresponda.

Y en Python, al igual que en Java, para usar algo que está en un paquete (en un subdirectorio de una estructura de directorios), tienes un import. Por supuesto, hay diferencias entre Java y Python, pero el concepto es el mismo, hacer un import de lo que quieras usar.

¿Diferencias?. En Java, una vez haces el import, puedes usar las clases de ese paquete en tu código sin necesidad de ponerles el prefijo del paquete. En Python haces el import pero tienes que seguir usando el prefijo del paquete.

¿Qué es mejor?. Bueno, en principio es más cómodo lo de Java, pero en Python tienes posibilidad de hacerlo similar, con un poco más de trabajo. Tienes from <paquete> import as <nombre>. Haciéndolo así, puedes usar <nombre> sin necesidad de prefijo. E incluso puedes elegir un nombre que no sea el original y cambiarlo. En Java, si hay conflicto porque el mismo nombre está en dos paquetes distintos, en  uno de ellos tienes que añadir todo el prefijo. En Python puedes simplemente cambiarlo de nombre sobre la marcha. Requiere más esfuerzo por tu parte para mantener un sistema de nombres coherente, pero luego es más cómodo de usar.

En fin, salvo algunas diferencias de uso, me ha llamado la atención que la idea básica sea la misma: estructura de directorios y subdirectorios para meter las cosas, que a su vez componen el nombre del paquete. Y la necesidad de hacer el import, cosa lógica por otro lado, no va a estar todo visible para todos si no lo necesitan.

Publicado en java, python | Etiquetado , , , | Deja un comentario

Librerías GIS de escritorio con Java

En el curro casi todas nuestras aplicaciones son de escritorio, con javax.swing y casi todas llevan algún mapa donde pintamos «cosas». Habitualmente símbolos que representan barcos, aviones, helicópteros, pero también polígonos, líneas etc.

Por ello, hemos ido probando y pasando de unas librerías Java de mapas (GIS o Geographical Information Service) a otras.

logo nasaUna de las que seguimos usando, gratuita, es WorldWind. Escrita por la NASA, muy completa, pero que ya no tiene soporte y está discontinuada. En la Categoría WorldWind de la Chuwiki tengo algunos ejemplos que hice en su día.

Otra que miramos en su momento, es la de ESRI (ArcGis). El SDK de desarrollo de Java es gratuito, aunque viene con algunas limitaciones. Sólo es necesario el pago si vas a instalar tu aplicación en clientes o si tu aplicación necesita acceder a determinadas funciones que están hospedadas en los servidores de ESRI, como el trazado de rutas entre dos puntos siguiendo los caminos o el acceso a ficheros de mapas que tengan ellos. Una ventaja grande de ESRI es que no sólo tiene SDK de Java, sino que lo tiene en varios lenguajes, para web, para móvil, etc. WorldWind también lo tiene, pero algunas de esas opciones están un poco «a medias».

En plan gratuito también miré Geotools. pero era, cuando lo miré, claramente más cutre y me encontré con fallos gordos. Así que no la miré mucho más. De esto hace ya 8 años y Geotools sigue viva y con actualizaciones, es posible que haya mejorado mucho.

Y finalmente, ya de pago total, pero como paga la empresa nos «da un poco igual», tenemos Luciad LightSpeed. Es una librería cara, sin posiblidad de desarrollo gratuito salvo que contactes con ellos y te concedan una licencia de demo, pero es muy muy completa. Es la que conozco que soporta más formatos de ficheros de mapas (cartas náuticas, simbología militar completa, ficheros meteorológicos, de CAD, etc, etc). Tiene también versión para el lado del servidor en una aplicación Web, Luciad Fusion. Y la parte del navegador, en TypeScript, Luciad RIA. LightSpeed viene también con Lucy, todo un framework para que hagas tu aplicación con menús, docking de paneles, salvado/recuperación del workspace por usuarios, etc, etc.

Tengo algunos ejemplos en github, pero al ser de pago, no he escrito ningún tutorial. Me gusta que la gente que sigue mis tutoriales pueda hacer y jugar con el código, cosa que aquí no es posible.

Mi relación con Luciad es de amor/odio. Amor porque como comento, es muy completo y nos soluciona prácticamente todos los problemas que tenemos relativos a mapas, esos sí, codificando. Odio por el tema de precio sobre todo, aunque paga la empresa, me toca «convencer» a los que pagan que el precio merece la pena. A veces no es fácil.

Publicado en Herramientas | Etiquetado , , , , , , , , | Deja un comentario

Una pequeña buena costumbre de programación

En algunos lenguajes de programación, cuando tenemos que hacer una operación con un literal, como un String o una constante, y una variable, suele ser buena costumbre poner en el lado izquierdo el literal.

Por ejemplo, si en un if ponemos a==11 o bien 11==a, ambas cosas funcionarán correctamente, sin embargo, la segunda forma tiene una ventaja. Imagina que te equivocas y en vez de == pones sólo un =.  De la primera forma, asignas a la variable a el valor 11 y luego ponte a buscar el error. La segunda forma normalmente da un error de sintaxis en cualquier lenguaje de programación que use esta expresión. Según lo escribes, verás el error. Esto sucede en C++. En Java ambas expresiones darían error al escribir si ponemos un solo =. Depende del lenguaje.

En Java, si en una variable de tipo String tenemos un valor y queremos compararlo con una cadena fija, podemos también de dos formas, bien variable.equals(«Hola»), bien «Hola».equals(variable). Nuevamente, la segunda opción tiene una ventaja. Si variable es null, la primera forma falla en ejecución, mientras que la segunda lo hará correctamente.

Lo mismo sucede con los boolean. Boolean.TRUE.equals(variable) siempre funciona, mientas que variable.equals(Boolean.TRUE) puede dar error si variable es null.

Publicado en C++, java | Etiquetado , , | Deja un comentario

GitHub Copilot

Icono GitHubNo hace mucho descubrí GitHub Copilot. Solo he leído sobre ello, no he experimentado. Es de pago 🙁

GitHub tiene una base de datos de código de miles de desarrolladores y miles de proyectos, por lo que se han decidido a «explotarla». GitHub Copilot es el «experimento».

Si usas GitHub Copilot, cuando haces tu propio programa, sólo tienes que poner el comentario de la función, su prototipo … y GitHub Copilot intenta hacer el resto, es decir, rellenarla. Busca por todo el código almacenado en GitHub algo que se parezca  a lo que quieras hacer y te sugiere el código para rellenarla. Puedes aceptarla o pedir siguiente sugerencia. Cuando aceptes alguna, luego puedes modificarla.

Se monta como un plugin en tu IDE favorito y puedes configurar cosas, como por ejemplo, lenguajes de programación en los que quieres las sugerencias.

No sé qué éxito tendrá o si realmente será útil, no la he probado, pero como idea es interesante y es algo que no se me había ocurrido pensar.

Publicado en git | Etiquetado , , | Deja un comentario

escapeSyntaxCallMode en PostgreSQL v11 y superiores

java jdbcA partir de la entrada anterior sobre CallableStatement, he estado jugueteando, con PostgreSQL 12. Y ¿cómo no? problemas

Resulta que usando la sintaxis especial de CallableStatement, llamada JDBC scape syntax, no me funcionaba la llamada a procedimientos desde Java. Es decir, esto

connection.prepareCall("{call insert_data(?,?,?)}")

no me funcionaba, daba error de que insert_data es un procedure y que hay que usar «call». Mientras que esta

connection.prepareCall("{? = call add(?,?)}")

si funcionaba sin problemas. Así que tras mucho darle vueltas y googlear, me encuentro que lo de que el driver Java de postgreSQL haga caso a este tipo de sintaxis o no, depende de una variable escapeSyntaxCallMode, que puede tener tres valores

public enum EscapeSyntaxCallMode {
    SELECT("select"),
    CALL_IF_NO_RETURN("callIfNoReturn"),
    CALL("call");

Por defecto y compatibilidad hacia atrás está en SELECT, que NO funciona para las llamadas a PROCEDURES con CallableStatement. Hay que cambiarlo para su correcto funcionamiento a callIfNoReturn.

Así que la URL de conexión desde Java a través del driver quedaría así

"jdbc:postgresql://localhost/chuidiang-examples?escapeSyntaxCallMode=callIfNoReturn"

o bien usando las properties de conexión.

Todo facilidades.

Publicado en java | Etiquetado , , | Deja un comentario

Statement, PreparedStatement y CallableStatement

java jdbcEn Java JDBC me entró la duda entre Statement, PreparedStatement y CallableStatement. Entre los dos primeros lo tenía claro, pero no tenía muy clara la diferencia entre los dos segundos. Asi que aquí va un resumen de los tres, ventajas e inconvenientes de cada uno y cuando deben usarse.

Statement es la base. Pones la sentencia SQL y la ejecutas. Tiene varias pegas:

  • Cada vez que ejecutas la sentencia, tiene que «compilarla», es decir, revisar la sintaxis que es correcta y ejecutarla. Por ello, no es eficiente si vas a ejecutar la misma sentencia SQL varias veces.
  • La cadena SQL se compone como un String normal. Si usas variables para componer la cadena, tienes que revisar el contenido de esas variables para evitar la SQL injection. Dicho de otra forma, si en tu SQL vas a insertar una columna tipo String con un valor que ha introducido un usuario a mano, tendras en tu código java algo como esto
    «INSERT INTO tabla (nombre) VALUES (‘ » + variable +» ‘)»
    bien así, bien con cualquier otro mecanismo de concatenación de cadenas. ¿Qué pasa si la variable contiene una comilla sencilla igual a la que se usa para delimitar los String en SQL. Pues sí, error al canto, la cadena se cierra antes de lo esperado y el resto de la sentencia SQL no tiene sentido
  • Con este tipo de composición con String de java, no se puede o es muy difícil meter bytes en una columna tipo BLOB o similar.

¿Cuando es útil entonces Statement?. Para sentencias sencillas que solo vamos a usar una vez. Ejemplo típico son la creación o update de las tablas cuando arrancamos nuestra aplicación o algún SELECT al inicio de la aplicación para verificar algo, como el número de versión de las tablas.

PreparedStatement hereda de Statement y le añade ciertas ventajas.

  • Compila el SQL que le pasamos. Así que si lo ejecutamos varias veces, es más eficiente.
  • La SQL se escribe con un String, pero poniendo ? en el lugar donde van las variables. Algo como
    «INSERT INTO tabla(nombre) VALUES (?)»
    no ponemos comillas simples en ningún caso, sea el tipo que sea la columna. Luego PreparedStatement tiene métodos para rellenar esos valores y se encarga él de «escapar» los posibles caracters extraños, por lo que no es posible la SQL injection.
  • Sí se pueden meter arrays de bytes o incluso «flujos» (Stream) de bytes, para no tener que tener todos los bytes todos cargados en memoria y pasarlos de golpe.

Así que PreparedStatement debe ser nuestra opción por defecto salvo quizás los casos que hemos comentado arriba en que Statement es útil. Y los casos que comentarmos a continuación para un CallableStatement.

CallableStatement hereda de PreparedStatement, por lo que tiene todas sus ventajas, pero añade una ventaja adicional

  • Tiene una sintaxis propia, no SQL, para la llamada a procedimientos y funciones de base de datos. Para que sepa que estamos usando esa sintaxis especial y no la específica del SQL de la base de datos concreta que estemos usando, debemos ponerlo entre llaves, así
    «{call un_procedimiento (?,?) }»
    «{? = call una_funcion (?,?,?}»
    por supuesto, con los parámetros que necesitemos puestos como interrogantes. Si no hay parámetros, no hace falta el paréntesis

Así que CallableStatement tiene todas las ventajas de PreparedStatement, pero además nos aisla de la sintaxis específica de nuestra base de datos para la llamada a procedimientos o funciones. Es adecuado por tanto para este tipo de llamadas.

Publicado en java | Etiquetado , | Deja un comentario