Anda di halaman 1dari 13

Trabajando con Store Procedures PL/pgSQL en PostgreSQL

Los lenguajes de procedimientos (procedural languages) han extendido la funcionalidad de las bases de datos proporcionando al lenguaje SQL cierto flujo de control similar al de un lenguaje de programacin. Cada fabricante tiene su propia implementacin de un lenguaje de procedimiento basado en SQL, as Microsoft SQL Server tiene Transact-SQL, Oracle tiene PL/SQL y as sucesivamente una lista de bases de datos con su correspondiente lenguaje de procedimiento. En el caso de PostgreSQL se tienen varios lenguajes que pueden usarse como lenguajes de procedimiento entre ellos tenemos a PL/Tcl, PL/Perl, PL/Python,C y como opcin predeterminada PL/pgSQL.

Las ventajas de ejecutar funciones del lado del servidor o Store Procedures con PL/pgSQL son las siguientes:

Extensibilidad PL/pgsql es como un lenguaje de programacin incluye la asignacin y evaluacin de variables y la posibilidad de hacer iteraciones y clculos ms complejos que con SQL.

Seguridad Ayuda a evitar ciertos ataques con SQL Injection ya que al utilizar parmetros evita la construccin de comandos SQL utilizando la concatenacin de cadenas.

Rapidez Son ejecutados ms rpidamente que las consultas SQL individuales ya que despus de la primera ejecucin el plan de ejecucin se mantiene en memoria y el cdigo no tiene que ser analizado ni optimizado nuevamente.

Programacin modular similar a los procedimientos y funciones en los lenguajes de programacin, lo que evita que se tengan planas de sentencias SQL. Reutilizacin de cdigo una funcin puede ejecutarse dentro de distintos Store Procedures.

Rendimiento un Store Procedure puede contener decenas de sentencias SQL que son ejecutadas como una sola unidad de golpe, a diferencia de las sentencias SQL individuales donde hay que esperar a que cada una sea procesada.

Mediante los siguientes ejemplos mostraremos el uso de Store Procedures utilizando PL/pgSQL. Creamos una base de datos de ejemplo llamada Catalogs con el siguiente comando desde el Shell.
$ createdb Catalogs

Revisamos los lenguajes de procedimiento instalados en la base de datos, revisamos que PL/pgSQL se encuentre instalado con el siguiente comando.
$ createlang -l Catalogs

Si no se encuentra, entonces ejecutamos el siguiente comando para instalarlo, esto siempre como administrador del servidor:
$ createlang U postgres plpgsql Catalogs

Si est instalado entonces abrimos un editor de texto y creamos un archivo llamado catalogs.sql donde escribiremos los comandos para crear las tablas de ejemplo y los Store Procedures para administrar los registros de cada una de las tablas.

Para la creacin de las tablas escribimos lo siguiente:


CREATE TABLE Countries( idcountry char(3) NOT NULL PRIMARY KEY ,name varchar(250) NOT NULL ,created timestamp DEFAULT NOW() NOT NULL ); CREATE TABLE States( idstate serial NOT NULL PRIMARY KEY ,name varchar(250) NOT NULL ,idcountry varchar(3) NOT NULL REFERENCES Countries(idcountry) ,created timestamp DEFAULT NOW() NOT NULL ); CREATE TABLE Cities( idcity serial NOT NULL PRIMARY KEY ,name varchar(250) NOT NULL ,idstate int NOT NULL REFERENCES States(idstate) ,created timestamp DEFAULT NOW() NOT NULL );

En acuerdo con la llave fornea definida en cada tabla, debe existir un pas para poder crear un estado, as mismo debe de existir un estado para poder crear una ciudad, entonces creamos unos registros en la tabla pases

INSERT INSERT INSERT INSERT

INTO INTO INTO INTO

Countries(idcountry,name)VALUES('arg','argentina'); Countries(idcountry,name)VALUES('bra','brasil'); Countries(idcountry,name)VALUES('ury','uruguay'); Countries(idcountry,name)VALUES('mex','mxico');

Supongamos que estas tablas van a utilizarse en un sistema donde sea obligatorio que los nombres de pas, estado y ciudad, se almacenen teniendo la primera letra mayscula o en notacin Camel Case,(en este caso los pases quedaron guardados con letras minsculas de manera intencional, con el fin de mostrar un Store Procedure).

Creamos entonces una funcin para aplicar esta regla a los datos de la tabla countries. La funcin se define con el siguiente cdigo:
CREATE FUNCTION UpperCamelCase(varchar) RETURNS varchar AS 'DECLARE res varchar; BEGIN SELECT INTO res UPPER(SUBSTRING($1,1,1)) || LOWER(SUBSTRING($1,2)); return res; END;' LANGUAGE 'plpgsql';

Lo aplicamos de la siguiente manera para actualizar los registros.

Una vez creada en el servidor se encuentra disponible para cualquier transformacin que queramos aplicar sobre cualquier cadena.

Ahora escribiremos las funciones para insertar registros en cada una de las tablas.
CREATE FUNCTION InsertState(varchar,varchar) RETURNS int AS 'DECLARE regs record; res numeric; BEGIN SELECT INTO regs count(name) FROM States WHERE name = UpperCamelCase($1); IF regs.count = 0 THEN INSERT INTO States(name,idcountry)VALUES(UPPER($1,$2)); SELECT INTO res idstate FROM States WHERE name = UpperCamelCase($1); RETURN res; END IF; IF regs.count > 0 THEN SELECT INTO res idstate FROM States WHERE name = UpperCamelCase($1); RETURN res; END IF; END; ' LANGUAGE 'plpgsql'; CREATE FUNCTIONInsertCity (varchar,numeric) RETURNS int AS 'DECLARE regs record; res numeric; BEGIN SELECT INTO regs count(name) FROM Cities WHERE name = UpperCamelCase($1); IF regs.count = 0 THEN

INSERT INTO Cities(name,idstate)VALUES(UPPER($1,$2)); SELECT INTO res idcity FROM Cities WHERE name = UpperCamelCase($1); RETURN res; END IF; IF regs.count > 0 THEN SELECT INTO res idcity FROM Cities WHERE name = UpperCamelCase($1); RETURN res; END IF; END; ' LANGUAGE 'plpgsql';

Bsicamente la estructura de un Store Procedure es la siguiente:


CREATE FUNCTION [nombre de la funcin] ([parmetros separados por comas]) RETURNS [el tipo de dato que regresa] AS 'DECLARE aqu se definen las variables que se usarn. BEGIN indica el inicio de la funcin. SELECT INTO este comando permite que los resultados de las consultas sean asignados a variables. (no debe de confundirse con SELECT [columnas] INTO) RETURN Sale de la funcin y regresa el tipo de dato que se declaro despus de la palabra RETURNS del CREATE FUNCTION END indica el fin de la funcin ' LANGUAGE indica con que lenguaje esta escrita la funcin, puede ser un lenguaje de procedimiento (plpgsql) o de consulta (SQL).

Vemos que en cada Store Procedure, reutiliza la funcin UpperCamelCase(varchar) que habamos previamente creado. Esto porque un Store Procedure puede llamar a otro Store Procedure que se encuentre disponible. En las siguientes imgenes los resultados de la ejecucin de cada Store Procedure.

import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; /** * @web http://www.jc-mouse.net * @author Mouse */ public class pgFunctions { /* DATOS PARA LA CONEXION */ /** Nombre de la base de datos */ private String db = "dbTemp"; /** Usuario postgreSQL */ private String user = "postgres"; /** Contrasea postgreSQL */ private String password = ""; /** Cadena de conexin */ private String url = "jdbc:postgresql://localhost:5432/"+db; /** Conexion a base de datos */ private Connection conn = null; /** Para obtener los resultados de las consultas SQL de la base de datos */ private ResultSet resultSet = null; /** Para enviar comandos SQL a la base de datos */ private Statement statement = null; /** Constructor de la clase * Realiza una conexin a la base de datos de PostgreSQL * @exception SQLException Los datos son incorrectos * @exception ClassNotFoundException No existe libreria JDBC:Postgresql */ public pgFunctions(){ this.url = "jdbc:postgresql://localhost:5432/"+db;

try{ //obtenemos el driver de para mysql Class.forName("org.postgresql.Driver"); //obtenemos la conexin conn = DriverManager.getConnection(this.url, this.user , this.password ); }catch(SQLException e){ System.err.println( e.getMessage() ); }catch(ClassNotFoundException e){ System.err.println( e.getMessage() ); } } /** * Ejecuta la instruccion SQL para llamar a la funcin en postgreSQL * @param Cedula_Identidad String que es el identificador de la persona * @return String el resultado de la funcin */ public String CallFunction( String Cedula_Identidad ) { String res=""; try { statement = conn.createStatement(); resultSet = statement.executeQuery("SELECT registrar('"+Cedula_Identidad+"'); "); while (resultSet.next()) { res=resultSet.getString(1); } } catch (SQLException ex) { System.err.println( ex.getMessage() ); } return res; } }

Explicacin: En el constructor de clase, se realiza la conexin a PostreSQL y se almacena en la variable conn, el mtodo CallFunction nos sirve para ejecutar la funcin y devolver el resultado en un String. Por dems esta decir que para ejecutarlo en tu pc, debes cambiar los datos de conexin, base de datos, usuario y contrasea, por los tuyos. Ejecutamos esta clase de la siguiente manera:
pgFunctions pgf = new pgFunctions(); System.out.println( pgf.CallFunction("1234567") );

Obteniendo como resultado:

Para colocar un JCheckBox dentro una celda de un JTable debemos sobre escribir algunas clases para que el componente pueda ser pintado en el JTable, si se desea colocar cualquier otro componente, el procedimiento es similar, teniendo cuidado claro, en los mtodos propios de cada componente swing. Lo primero que debe hacerse, es crear una nueva clase que se extienda de DefaultCellEditor e implemente TableCellRenderer, esto para indicarle al JTable que tipo de componente debe utilizar al momento de introducir la informacin.
import java.awt.Color; import java.awt.Component; import javax.swing.DefaultCellEditor; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JTable; import javax.swing.table.TableCellRenderer; /** * @web http://www.jc-mouse.net * @author Mouse */ public class Celda_CheckBox extends DefaultCellEditor implements TableCellRenderer { private JComponent component = new JCheckBox(); private boolean value = false; // valor de la celda /** Constructor de clase */ public Celda_CheckBox() { super( new JCheckBox() ); } /** retorna valor de celda */ @Override public Object getCellEditorValue() { return ((JCheckBox)component).isSelected(); } /** Segun el valor de la celda selecciona/deseleciona el JCheckBox */ @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {

//Color de fondo en modo edicion ( (JCheckBox) component).setBackground( new Color(200,200,0) ); //obtiene valor de celda y coloca en el JCheckBox boolean b = ((Boolean) value).booleanValue(); ( (JCheckBox) component).setSelected( b ); return ( (JCheckBox) component); } /** cuando termina la manipulacion de la celda */ @Override public boolean stopCellEditing() { value = ((Boolean)getCellEditorValue()).booleanValue() ; ((JCheckBox)component).setSelected( value ); return super.stopCellEditing(); } /** retorna componente */ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (value == null) return null; return ( (JCheckBox) component ); } }

Para que el JTable pinte el componente, creamos otra clase, esta vez que se extienda de JCheckBox, el componente que queremos pintar e implemente TableCellRenderer, la clase es la siguiente.
import java.awt.Color; import java.awt.Component; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JTable; import javax.swing.table.TableCellRenderer; /** * @web http://www.jc-mouse.net * @author Mouse */ public class Render_CheckBox extends JCheckBox implements TableCellRenderer { private JComponent component = new JCheckBox(); /** Constructor de clase */ public Render_CheckBox() { setOpaque(true); } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { //Color de fondo de la celda ( (JCheckBox) component).setBackground( new Color(0,200,0) ); //obtiene valor boolean y coloca valor en el JCheckBox boolean b = ((Boolean) value).booleanValue(); ( (JCheckBox) component).setSelected( b ); return ( (JCheckBox) component); } }

Resumiendo, para pintar un componente swing dentro una celda de una tabla JTable, debemos crear dos clases una para manipular los valores de la celda (Celda_CheckBox) con el propio componente y otra para pintar el componente. Para implementar estas clases en el JTable, debemos indicarle que columna es la que contendr el componente swing en el modo edicin, de la siguiente manera:
jTable1.getColumnModel().getColumn( 0 ).setCellEditor( new Celda_CheckBox() );

y tambin indicar que columna pintara el JCheckBox en todo momento.


jTable1.getColumnModel().getColumn( 0 ).setCellRenderer(new Render_CheckBox());

Un ejemplo completo que se inicia en el constructor de una clase JFrame llamado interfaz el cual tiene un JTable, ademas se hace uso de un TableModel para colocar algunos datos:
public interfaz() { initComponents(); this.setTitle("jTable con jCheckBox - [ http://www.jcmouse.net/ ]"); //se crea un TableModel con algunos datos y se asigna al JTable DefaultTableModel TableModel = new DefaultTableModel(); TableModel.setDataVector(new Object[][] { { false, "Juan Perez", "12", "Hombre" }, { false, "Homero J. Simpsons", "40", "Hombre" }, { true, "Ned Flanders", "35", "Hombre" }, { true, "Asuka Langley", "15", "Si gracias" }, { false, "Rei Ayanami", "16", "Mujer" }, { true, "shinji ikari", "15", "No se sabe" } }, new Object[] { "CheckBox", "Nombre", "Edad", "Sexo" }); jTable1.setModel(TableModel); //Se crea el JCheckBox en la columna indicada en getColumn, en este caso, la primera columna jTable1.getColumnModel().getColumn( 0 ).setCellEditor( new Celda_CheckBox() ); //para pintar la columna con el CheckBox en la tabla, en este caso, la primera columna jTable1.getColumnModel().getColumn( 0 ).setCellRenderer(new Render_CheckBox()); }

Resultado

Anda mungkin juga menyukai