You are on page 1of 0

D

F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 1
PROGRAMACION WEB CON JSP
(PARTE IV)
1 TECNOLOGIA JAVA SERVLET ....................................................2
1.1 Porqu utilizar Servlets y no solo JSP? .................................... 2
1.2 Como procesamos las solicitudes (cliente-servidor) con Servlets.. 3
1.2.1 Procesamiento de la solicitud............................................. 3
1.3 Procesamiento de solicitudes................................................... 5
1.3.1 El objeto Request ............................................................. 5
1.3.2 El objeto Response........................................................... 5
1.4 Ciclo vital del Servlet.............................................................. 6
2 CONECTIVIDAD DE BASES DE DATOS DE JAVA .........................7
2.1 Controladores........................................................................ 7
2.1.1 Tipos de controladores JDBC.............................................. 7
2.2 Cmo acceder a una base de datos usando JDBC....................... 9
2.2.1 Como cargar el controlador JDBC....................................... 9
2.2.2 URL de JDBC ................................................................... 9
jdbc:<subprotocolo>:<data source identifier>................................ 9
2.2.3 Creacin de instrucciones (objetos Statement)................... 10
2.2.4 Ejecucin de instrucciones (Statement)............................. 10
2.2.5 executeUpdate() ............................................................ 10
2.2.6 executeQuery().............................................................. 11
2.2.7 execute() ...................................................................... 12
2.3 Ejercicio ............................................................................. 14
2.4 Ejercicio ............................................................................. 15
2.5 Uso de ResultSet.getXXX para recuperar tipos JDBC................. 17
2.6 Liberacin de recursos.......................................................... 18
2.6.1 Liberacin Implcita ........................................................ 18
2.7 Detalles de ResultSet ........................................................... 19
2.7.1 ResultSets Actualizables.................................................. 21
2.7.2 Insertar y Borrar Filas en un ResultSet.............................. 22
2.8 Utilizar Transacciones ........................................................... 24
2.9 Ejercicio ............................................................................. 25
D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 2
1 TECNOLOGIA JAVA SERVLET
Un Servlet es un componente o programa que genera contenido
Web dinmico.
Los Servlets por tanto son programas java, con la particularidad de que se
definen por medio del API Java Servlet. Este API lo que hace es modelar el
proceso de peticin-respuesta que gobierna el protocolo http.
Estos Servlets se ejecutan dentro de un servidor compatible con Java (un
contenedor Web) como puede ser Tomcat.
1.1 Porqu utilizar Servlets y no solo JSP?
En la UT anterior vimos como estructurar una aplicacin usando el Modelo
2 o Modelo Vista Controlador. Pues bien, la funcin de un Servlet
principalmente est en la de ejercer de Controlador de nuestra
aplicacin. Es decir nuestra aplicacin debera estar estructurada de la siguiente
forma:
Modelo: Contiene la parte central de la funcionalidad de la aplicacin.
Estaramos hablando de la Base de Datos y de los componentes (Java Beans)
que interaccionan con ella
Vista: Es la forma en la que se presentan los datos al usuario. Normalmente un
diseador Web no tiene porque saber lo que sucede en la base de datos o en el
componente de la lgica de negocio. Podramos identificar la vista con las
pginas JSP y HTML.
Controlador: Es quien reacciona ante las entradas de datos del usuario y en
funcin de stas hace una cosa u otra, es decir puede llamar a otra JSP para
mostrar otra vista o puede acceder a los datos para actualizarlos o consultarlos.
Navegador
Servlet
JSP
Java
Bean
Base de Datos
CONTENEDOR DE SERVLETS
soliciitud
respuesta
controlador
Vista
MODELO
D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 3
Utilizaremos Servlets cuando la lgica sea compleja y no sea conveniente
mezclarla con cdigo HTML pues complica el mantenimiento del software que se
produzca.
1.2 Como procesamos las solicitudes (cliente-servidor) con
Servlets
Todos los Servlets utilizan interfaces y clases definidas en los
paquetes javax.servlet y javax.servlet.http.
Todos los Servlets deben implementar la interfaz
javax.servlet.Servlet. Afortunadamente, hay una clase
javax.servlet.http.HttpServlet que permite implementar la interfaz
mencionada.
Cuando el Servlet acepta una llamada de un cliente, recibe dos
objetos:
- Uno que implementa la clase javax.servlet.ServletRequest
- Otro que implmementa javax.servlet.ServletResponse.
El primero (la clase ServletRequest) contiene la comunicacin
entre el cliente y el servidor, mientras que el segundo (la clase
ServletRequest) incluye la comunicacin que devuelve el Servlet al
cliente.
Como estamos hablando de http, lo visto hasta ahora se traduce en
que los objetos request y response implementan las interfaces
HttpServletRequest y HttpServletRequest
1.2.1 Procesamiento de la solicitud
Como hemos comentado, un Servlet acepta una solicitud de un cliente y la
procesa para crear una respuesta, que ser devuelta de nuevo al cliente.
Existe un mtodo service() que se invoca para cada una de las
solicitudes que el contenedor pasa al Servlet.
A su vez el mtodo service() llama a otros mtodos en funcin del
tipo de solicitud. Estos mtodos son:
- doGet(). Se invoca para procesar solicitudes HTTP GET
- doPost(). Se invoca para procesar solicitudes HTT POST.
Estos mtodos son implementados por una clase abstracta llamada
HttpServlet por lo que tendremos que hacer pocos cambios.
Normalmente al disear un Servlet el programador debe
preocuparse exclusivamente de los mtodos doGet() y doPost() puesto
que cuando se realiza una solicitud de una pgina representada por el
Servlet, el contenedor la reenva al Servlet invocando el mtodo
service() de la clase base javax.servlet.Servlet. La implementacin de
HttpServlet, en funcin del tipo de solicitud, invoca tambin al mtodo
D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 4
doGet() o bien doPost(), por lo que el Servlet debe anular uno de los dos
mtodos (es decir debe reescribirlo).
Ejemplo:
/*
* EjemploServlet.java
*/
package servlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class EjemploServlet extends HttpServlet {

/** Procesa peticiones para HTTP <code>GET</code> and <code>POST</code> metodos.
* @param request servlet request
* @param response servlet response
*/
protected void procesarPeticiones(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8"); //para que el cliente sepa como renderizar la respuesta
PrintWriter out = response.getWriter(); //objeto PrintWriter permite escribir hacia la salida del contenedor
out.println("<html>");
out.println("<head>");
out.println("<title>Ejemplo de Servlet</title>");
out.println("</head>");
out.println("<body>");
//out.println("<h1> Esto e sun ejemplo de Servlet</h1>");
out.println("</body>");
out.println("</html>");
out.close();
}

// <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
/** Maneja the HTTP <code>GET</code> method.
* @param request servlet request
* @param response servlet response
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
procesarPeticiones(request, response);
}

/** Maneja the HTTP <code>POST</code> method.
* @param request servlet request
* @param response servlet response
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
procesarPeticiones(request, response);
}


}
D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 5
1.3 Procesamiento de solicitudes
Los servlets los puede usar el contenedor para procesar
solicitudes.
En cada solicitud se pasa un objeto ServletRequest a los mtodos
doGet() o doPost() del Servlet y un objeto ServletResponse que se
puede utilizar para crear la respuesta que recibir el cliente.
Cuando llega una solicitud el contenedor busca una instancia del Servlet
correspondiente. Si no lo encuentra, carga uno e invoca al mtodo doGet() o
doPost(), en funcin de cmo se enviara la solicitud.
1.3.1 El objeto Request
Podemos usar el objeto request de la misma manera que lo hemos usado
hasta ahora en las pginas JSP. Para ello disponemos, entre otros, de los
mtodos:
- getParameter(), getParameterValues() ya conocidos.
- getQueryString(), que devuelve una cadena de datos del cliente. En
este caso tendremos que escribir personalmente el cdigo que permite
extraer la informacin.
1.3.2 El objeto Response
La funcin de un Servlet consiste en procesar la solicitud y generar la
respuesta adecuada. El objeto response contiene toda la informacin que
el servidor debe devolver al cliente.
El objeto HttpServletResponse ofrece dos formas para devolver los datos
al usuario, lo mtodos getWriter() y getOutputStream(), que
devuelven un objeto Writer y un objeto ServletOutputStream
respectivamente. El primero se usa cuando se trate de devolver texto y el
segundo cuando devolvamos datos binarios.
Otros mtodos adicionales del objeto response son:
- sendRedirect(). Mtodo que redirige al cliente a una direccin URL
distinta, que debe ser absoluta.
- sendError(). Enva un mensaje de error al cliente utilizando para ello
el estado de cdigo de error actual.
D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 6
1.4 Ciclo vital del Servlet
Como hemos visto el contenedor se encarga de este proceso, crea los
objetos Servlet correspondientes as como los mtodos necesarios. En trminos
generales un Servlet tiene el siguiente ciclo:
Inicio Service() doGet() o doPost() Fin
Carga del Servlet Se ejecuta este mtodo Service() llama a doGet o doPost() Se destruye el Servlet
Un servidor carga y crea instancias de un Servlet de forma
dinmica cuando se solicitan sus servicios por primera vez. Tambin se
puede configurar el servidor Web para cargar y crear instancias de
servlets especficos cuando se inicialice el servidor, en especial en
aquellos casos en los que el proceso de inicializacin implica operaciones
de larga duracin como abrir una base de datos.
El mtodo init() el Servlet es el encargado de la inicializacin y se
invoca para cada una de las instancias del Servlet, antes de procesar
cualquier solicitud.
Un Servlet se descarga de la memoria cuando se cierra el
contenedor. El contenedor invoca el mtodo destroy() del Servlet. Se usa
este mtodo en caso de que haya que ejecutar determinadas acciones al
cerrar el contenedor. Se usa por ejemplo, para cerrar conexiones a bases
de datos.
D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 7
2 CONECTI VI DAD DE BASES DE DATOS DE JAVA
El API que nos permite ejecutar instrucciones SQL es distinto en cada
motor de bases de datos, sin embargo los programadores de Java diponen de un
solo API, la Conectividad de ases de datos de Java (JDBC).
Adems el API JDBC 2.0 supera los lmites del lenguaje SQL ya que ofrece
la posibilidad de interactuar con otros tipos de orgenes de datos, como archivos
que contenga tablas de datos.
2.1 Controladores
Un controlador es la implementacin de la interfaz JDBC adecuada
a una Base de Datos concreta.
Con un controlador JDBC se pueden conseguir tres cosas:
- Establecer una conexin con una fuente de datos.
- Enviar consultas e instrucciones UPDATE a la fuente de datos
- Recuperar resultados.
Existen controladores desarrollados por los fabricantes para las distintas
Bases de Datos: Oracle, MySql, Sql*Server, Informix, Adems es posible que
para una misma Base de Datos existan distintos tipos de controladores Java.
Por ejemplo, existen diversos controladores para MySQL. La informacin
de estos controladores se puede encontrar en http://www.mysql.com/downloads
dentro de la seccin JDBC. Un controlador para dicha base de datos puede ser
MM.MySQL, el cual permite desde aplicaciones Java y applets, acceder los datos
de MySQL.
Es aconsejable descargar el binario del controlador y ubicarlo en el sitio
correcto (como puede ser la carpeta Tomcat common/l i b) y asegurarse de que
la variable CLASSPATH apunta hacia dicha carpeta.
En http://industry.java.sun.com/products/jdbc/drivers podemos encontrar
la lista completa de controladores disponibles.
En
http://www.oracle.com/technology/software/tech/java/sqlj_jdbc/htdocs/jdbc901.
html disponemos de los drivers JDBC para la versin 9 de Oracle
2.1.1 Tipos de controladores JDBC
Los controladores JDBC se agrupan en cuatro categoras principales:
Tipo1: Controlador puente JDBC-ODBC.
Ofrece acceso JDBC por medio de controladores estndar ODBC. Se usa
fundamentalmente cuando la base de datos a la que se quiere acceder no cuenta
con un controlador JDBC puro. No es recomendable usarlo en entornos de
produccin ya que el rendimiento es mucho menor.
Tipo2: Controlador Java nativo parcialmente API.
D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 8
Este tipo convierte llamadas JDBC en llamadas a mtodos nativos del API
de la Base de Datos como Oracle, Informix,
Al igual que en el Tipo 1 requiere cdigo binario especfico de un sistema
operativo que se cargue en el equipo de cada cliente por lo que su manejo entre
plataformas no es muy completo. Es ms rpido que el Tipo 1.
Tipo3: Controlador Java puro de protocolo JDBC-Net.
Es la alternativa JDBC ms flexible.
Tipo4 Controlador java puro de protocolo nativo.
Convierte llamadas JDBC directamente en el protocolo de red utilizado por
el DMBS. Si podemos disponer de l es aconsejable su uso.
Tipo I
Puente JDBC/ODBC.
Implementado en el SDK.
Tipo II
Librera cliente JDBC nativa.
Proporcionada por el
fabricante de la DB.
Tipo III
Librera nativa middleware.
Proporcionada por el
fabricante.
Cdigo nativo.
Tipo IV
Librera Pure Java.
Implementada por el
fabricante.
Optimizada para su DB y para
Java.
Oracle ofrece driver tipo II (Oracle OCI JDBC Driver) y driver tipo IV (Oracle Thin
JDBC Driver).
D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 9
2.2 Cmo acceder a una base de datos usando JDBC
Los pasos a seguir para utilizar JDBC y acceder a una base de datos son los
siguientes:
1.- Cargar y registrar el controlador JDBC adecuado con ayuda del
DriverManager(Administrador de controladores)
2.- Determinar el vnculo entre el controlador y la fuente de datos por medio de
una URL JDBC.
3.- Crear un objeto de conexin con la URL JDBC.
4.- Insertar una instruccin SQL en un mtodo de ejecucin para ejecutar la
instruccin.
2.2.1 Como cargar el controlador JDBC
Lo podemos hacer de dos formas:
Instanciando explcitamente el Driver:
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
Cargando en memoria la clase del Driver:
Class.forName(oracle.jdbc.driver.OracleDriver);
En este caso al cargar la clase de controlador, sta crea un objeto
controlador y registra dicha instancia con la clase DriverManager invocando,
automticamente, el mtodo DriverManager.registerDriver().
2.2.2 URL de JDBC
Este paso vincula el controlador con una fuente de datos.
Para ello usamos la clase Connection de la siguiente forma:
Connection conn1 = DriverManager.getConnection(url);
Connection conn2 = DriverManager.getConnection(url, login, password);
La URL tiene la sintaxis siguiente:
jdbc:<subprotocolo>:<data source identifier>
Donde:
jdbc: el protocolo
<subprotocolo>: el nombre del controlador
D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 10
<data source identifier>: el nombre de la fuente de datos
Ejemplo:
jdbc:oracle:thin:@servidor:1521:almacen
Dnde servidor es el nombre del host, 1521 es el puerto por el que
escucha el listener de la BBDD y almacen es el SID de la base de datos a
la que nos conectamos. Es decir es una cadena al estilo de SQL*NET
Ejemplo:
Connection conn = DriverManager.getConnection(
"jdbc:oracle:thin: @servidor:1521:almacen ",
"scott", "tiger");
2.2.3 Creacin de instrucciones (objetos Statement)
Una vez establecida la conexin a una determinada base de datos, se
puede utilizar para enviar instrucciones SQL.
Para ello, es necesario crear un objeto instruccin (Statement) que nos
permita disear un comando SQL y ejecutarlo.
Existen 3 tipos de objetos Statement:
- Objetos Statement (java.sql.Statement)
- Objetos PreparedStatement (java.sql.PreparedStatement)
- Objetos CallableStatement (java.sql.CallableStatement)
La interfaz PreparedStatement ampla la interfaz Statement y la interfaz
CallableStatement ampla a PreparedStatement.
Nos centraremos en los objetos Statement.
Un objeto Statement se crea por medio del mtodo
Connectionl.createStatement(), veamos un ejemplo:
Statement stmt = con.createStatement();
2.2.4 Ejecucin de instrucciones (Statement)
Una vez creado el objeto Statement se puede ejecutar un comando SQL
invocando distintos mtodos de ejecucin.
- La interfaz java.sql.Statement ofrece tres mtodos distintos para
ejecutar instrucciones SQL: executeUpdate(), executeQuery(),
execute()
2.2.5 executeUpdate()
Se usa para ejecutar instrucciones SQL DDL como CREATE TABLE,
DROP TABLE
Devuelve un entero que indica el nmero de filas afectadas, en el caso
de CREATE TABLE y DROP TABLE devuelve 0.
D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 11
2.2.6 executeQuery()
Se usa para instrucciones que devuelven un solo conjunto de
resultados, como instrucciones SELECT.
Dicho mtodo devuelve una instancia que implementa
ResultSet. Esta estructura de datos encapsula al conjunto de registros
devuelto. Para hacernos una idea podemos comparar dicha estructura
con un cursor PL/SQL.
RECUPERACIN DE LOS REGISTROS
1. Para acceder a cada uno de los registros devueltos deberemos
atravesar la estructura (el cursor) con el mtodo next(). Dicho
mtodo devuelve un booleano indicando si todava quedan registros.
2.- Inicialmente el cursor est situado delante del primer
registro, por lo que deberemos invocar primeramente next()
para acceder al primer registro.
3.- Una vez situados en un registro, podremos recuperar cualquier
columna con la familia de mtodos getXXX() indexando la
columna por:
Posicin (posicin en base 1 en la clusula SELECT).
Nombre.
donde XXX especifica alguno de los tipos Java con el que se quiere
recuperar la columna. P. ej. getString()
Ejemplo de uso:
Statement stm = conn.createStatement();
ResultSet rs = stm.executeQuery(SELECT TITLE, AUTHOR, ISBN,PRECIO
FROM BOOKLIST);
while (rs.next()) {
String titulo = rs.getString(1);
Float n = rs.getFloat(2);
...
}
String titulo = rs.getString(1); : obtiene el valor de la
primera columna de la fila actual de rs (columna COF_NAME), convirtindolo a un
objeto String de Java y asignndolo a titulo.
Float n = rs.getFloat(2); : obtiene el valor de la segunda
columna de la fila actual de rs, lo convierte a un float de Java y lo asigna a n.
Recuerda que el nmero de columna se refiere al nmero de columna en la hoja
de resultados (ResultSet) no en la tabla original.
D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 12
Aunque el metodo getString est recomendado para recuperar
tipos CHAR y VARCHAR de SQL, es posible recuperar cualquier tipo
bsico SQL con l.
Obtener un valor con getString puede ser muy til, pero tiene sus
limitaciones. Por ejemplo, si se est utilizando para recuperar un tipo
numrico, getString lo convertir en un String de Java, y el valor tendr
que ser convertido de nuevo a nmero antes de poder operar con l.
CONSIDERACIONES
Si la sentencia SQL ejecutada con el mtodo executeQuery() no
devolviera un conjunto de registros resultado (p. ej. INSERT
INTO ...), obtendramos una excepcin SQLException.
Por defecto, un objeto Statement tiene solamente un
ResultSet abierto. Por tanto si queremos tener ms de un
ResultSet abierto simultneamente, deberemos utilizar distintos
objetos Statement's.
2.2.7 execute()
Nos permite ejecutar instrucciones que devuelven ms de un conjunto
de resultados, ms de un nmero de actualizacin o una combinacin
de ambos.
Valor devuelvo por el mtodo execute():
true
Si la instruccin ejecutada devolvi un conjunto de registros
(Query).
Podremos recuperar el ResultSet mediante el mtodo
getResultSet().
false
Si no fue instruccin de consulta.
Podremos recuperar la cuenta de filas afectadas con el mtodo
getUpdateCount().
Ejemplo de uso:
Statement stm = conn.createStatement();
boolean b = stm.execute(sql);
if (b == true) {
// b es true si se devuelve un ResultSet
D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 13
ResultSet rs = stm.getResultSet();
while (rs.next()) {
...
}
} else {
// b es false si se devuelve una cuenta
int rows = stm.getUpdateCount();
if (rows > 0) {
...
}
}
D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 14
2.3 Ejercicio
Copiar el siguiente cdigo, adaptarlo a nuestra instalacin e interpretarlo.
package ddl;
import java.sql.*;
public class CreaTablas {
public CreaTablas() {
}

public static void main(String[] args) {
Connection con =null;
try
{
Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
System.out.println("Driver JDBC cargado");

con=DriverManager.getConnection("jdbc:oracle:thin:@portatil:1521:francis","francis", "francis");
System.out.println("Se ha establecido la conexin a la Base de Datos");

Statement stmt = con.createStatement();
String mSql;

mSql="CREATE TABLE autores (";
mSql=mSql.concat("ID NUMBER(12) NOT NULL PRIMARY KEY,");
mSql=mSql.concat("NOMBRE VARCHAR2(50))");
System.out.println(mSql);
stmt.executeUpdate(mSql);
System.out.println("Tabla autor creada");
mSql="CREATE TABLE libros (";
mSql=mSql.concat("ISBN VARCHAR2(12) NOT NULL PRIMARY KEY,");
mSql=mSql.concat("TITULO VARCHAR2(50),");
mSql=mSql.concat("ID_AUTOR NUMBER(12),");
mSql=mSql.concat("PRECIO NUMBER(9,2))");
System.out.println(mSql);
stmt.executeUpdate(mSql);
System.out.println("Tabla LIBROS creada");

} catch (ClassNotFoundException cnfe) {
System.out.println("ClassNotFoundException: No se puede localizar el controlador");
} catch (SQLException cnfe) {
System.out.println("SQLException: " + cnfe);
}
catch (Exception e) {
System.out.println("Se ha producido un error desconocido");
} finally {
try {
if (con !=null)
con.close();
} catch(SQLException sqle){
System.out.println("No se puede cerrar la conexin a la base de datos.");
}
}
}

}
D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 15
OJO! Es importante obtener el driver de oracle y hacerlo accesible
desde netbeans (o bien con CLASSPATH para ejecutarlo con java)
2.4 Ejercicio
En este ejercicio vamos a ver cmo realizar la insercin de datos en las
tablas. Copia el cdigo, adptalo e interprtalo.
D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 16
package inserciones;
import java.sql.*;
/**
*
* @author Administrador
*/
public class Insertar {

/** Creates a new instance of CreaTablas */
public Insertar() {
}

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
Connection con =null;
try
{
Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
System.out.println("Driver JDBC cargado");

con=DriverManager.getConnection("jdbc:oracle:thin:@portatil:1521:francis","francis", "francis");
System.out.println("Se ha establecido la conexin a la Base de Datos");

Statement stmt = con.createStatement();
String mSql;

mSql="INSERT INTO AUTORES VALUES(";
mSql=mSql.concat("1,'CEBALLOS F.J.')");
stmt.executeUpdate(mSql);
mSql="INSERT INTO AUTORES VALUES(2,'LUIS JOYANES AGUILAR')";
stmt.executeUpdate(mSql);
mSql="INSERT INTO LIBROS VALUES(8478976922,'INTERFACES GRAFICAS Y APLICACIONES PARA
INTERNET',1,34.90)";
stmt.executeUpdate(mSql);
mSql="INSERT INTO LIBROS VALUES(8448132904,'Programacin en Java 2',2,44.6)";
stmt.executeUpdate(mSql);

} catch (ClassNotFoundException cnfe) {
System.out.println("ClassNotFoundException: No se puede localizar el controlador");
} catch (SQLException cnfe) {
System.out.println("SQLException: " + cnfe);
}
catch (Exception e) {
System.out.println("Se ha producido un error desconocido");
} finally {
try {
if (con !=null)
con.close();
} catch(SQLException sqle){
System.out.println("No se puede cerrar la conexin a la base de datos.");
}
}
}

D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 17
2.5 Uso de ResultSet.getXXX para recuperar tipos JDBC
TINYINT SMALLINT INTEGER BIGINT REAL FLOAT DOUBLE DECIMAL NUMERIC BIT CHAR
get Byt e X x x x x x x x x x x
get Short x X x x x x x x x x x
getI nt x x X x x x x x x x x
get Long x x x X x x x x x x x
get Fl oat x x x x X x x x x x x
get Doubl e x x x x x X X x x x x
get Bi gDeci
mal
x x x x x x x X X x x
get Bool ea
n
x x x x x x x x x X x
get St ri ng x x x x x x x x x x X
get Byt es
get Dat e x
get Ti me x
get Ti mest a
mp
x
get Asci i St r
eam
x
get Uni cod
eSt r eam
x
get Bi naryS
t r eam
get Obj ect x x x x x x x x x x x
LONGVARCHAR BINARY VARBINARY LONGVARBINARY DATE TIME TIMESTAMP VARCHAR
get Byt e x x
get Short x x
getI nt x x
get Long x x
get Fl oat x x
get Doubl e x x
get Bi gDeci
mal
x x
get Bool ean x x
get St ri ng X x x x x x x x
get Byt es X X x
get Dat e x x X x
get Ti me x x X x
get Ti mest a
mp
x x x x X
get Asci i St r
eam
x X x x x
get Uni code x X x x x
D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 18
St r eam
get Bi narySt
r eam
x x X
get Obj ect x x x x x x x x
Una "x" indica que el mtodo getXXX se puede utilizar legalmente para
recuperar el tipo JDBC dado.
Una "X" indica que el mtodo getXXX est recomendado para recuperar
el tipo JDBC dado.
2.6 Liberacin de recursos
Una vez utilizados los recursos de acceso a la base de datos
debern cerrarse convenientemente.
Entendemos aqu por recurso cualquier
instancia que mantenga una sessin abierta
con la base de datos.
Estos recursos son:
Connection
Statement
ResultSet
Cuando existe un objeto de estos tipos,
sabemos que el recurso subyacente en la base
de datos est abierto. Tras ser utilizado, dicho
recurso deber liberarse.
Para ello, dichas clases proporcionan los
mtodos close().
2.6.1 Liberacin Implcita
Al cerrar un recurso, de forma implcita se cierran todos los recursos
dependientes. As por ejemplo si cerramos una conexin, tambin se cerrarn las
sentencias y cursores (ResultSets) dependientes tal y como se muestra en el
ejemplo:
Connection conn = DriverManager.getConnection(...);
Statement stm = conn.createStatement();
ResultSet rs = stm.executeQuery(SQL);
...
D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 19
conn.close();
// De forma implcita tambin quedan cerrados stm y rs
Igualmente al ejecutar siguientes SQL sobre un mismo objeto Statement
tambin se liberar el anterior ResultSet asociado a la sentencia (si lo hubiere):
ResultSet rs1 = stm.executeQuery(SQL1);
...
ResultSet rs2 = stm.executeQuery(SQL2);
// De forma implcita queda liberado el recurso rs1
Y en ltima instancia, el recurso se liberar cuando el recolector de basura
(Garbage Collector) libere el objeto que posee el recurso.
No obstante es una buena recomendacin y una buena tcnica de
programacin cerrar todos los recursos de forma explcita conforme no
se necesiten:
Connection conn = DriverManager.getConnection(...);
Statement stm = conn.createStatement();
ResultSet rs = stm.executeQuery(SQL);
...
rs.close();
stm.close();
conn.close();
NOTA: No hay ningn efecto si se intenta cerrar un recurso ya cerrado.
2.7 Detalles de ResultSet
Para recuperar el valor de una columna se puede hacer tanto por
su nmero de columna como por su nombre:
String s = rs.getString(2);
String s = rs.getString(TITULO);
Si conocemos el nombre de una columna pero no su nmero, podemos
obtener ste mediante el mtodo findColumn().
Tambin es posible obtener informacin sobre las columnas de un
ResultSet (nmero de columnas, tipos, y propiedades), para ello usaremos el
mtodo ResultSet.getMetaData(), que devuelve un objeto ResultSetMetaData
con la informacin antes indicada.
De forma predeterminada el movimiento de un ResultSet es hacia delante
y es el nico movimiento posible con los controladores que implementan el API
JDBC 1.0
Este tipo de ResultSet devuelve el valor ResultSet.TYPE_FORWARD_ONLY
del mtodo getType() y se conoce como conjunto de resultados slo hacia
delante.
D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 20
Los controladores que implementan API 2.0 permiten conjuntos de
resultados desplazables. Es decir es posible desplazar hacia delante y hacia
atrs as como hasta una determinada fila. Los mtodos para realizar estas
acciones son: previous(), first(), last(), absolute(), relative(), afterLast() y
beforeFirst().
El mtodo absolute mover el cursor al nmero de fila indicado en su
argumento. Si el nmero es positivo, el cursor se mueve al nmero dado desde
el principio, por eso llamar a absolute(1) pone el cursor en la primera fila. Si el
nmero es negativo, mueve el cursor al nmero dado desde el final, por eso
llamar a absolute(-1) pone el cursor en la ltima fila. La siguiente lnea de
cdigo mueve el cursor a la cuarta fila de srs.
srs.absolute(4);
Si srs tuviera 500 filas, la siguiente lnea de cdigo movera el cursor a la fila
497.
srs.absolute(-4);
Con el mtodo relative, se puede especificar cuntas filas se mover desde la
fila actual y tambin la direccin en la que se mover. Un nmero positivo mueve
el cursor hacia adelante el nmero de filas dado; un nmero negativo mueve el
cursor hacia atrs el nmero de filas dado. Por ejemplo, en el siguiente
fragmente de cdigo, el cursor se mueve a la cuarta fila, luego a la primera y por
ltimo a la tercera.
srs.absolute(4); // cursor est en la cuarta fila
. . .
srs.relative(-3); // cursor est en la primera fila
. . .
srs.relative(2); // cursor est en la tercera fila
El mtodo getRow permite comprobar el nmero de fila donde est el cursor.
Por ejemplo, se puede utilizar getRow para verificar la posicin actual del cursor
en el ejemplo anterior.
srs.absolute(4);
int rowNum = srs.getRow(); // rowNum debera ser 4
srs.relative(-3);
int rowNum = srs.getRow(); // rowNum debera ser 1
srs.relative(2);
int rowNum = srs.getRow(); // rowNum debera ser 3
Existen cuatro mtodos adicionales que permiten verificar si el cursor se
encuentra en una posicin particular. La posicin se indica en sus
nombres:isFirst, isLast, isBeforeFirst, isAfterLast. Todos estos mtodos
devuelven un boolean y por lo tanto pueden ser utilizados en una sentencia
condicional. Por ejemplo, el siguiente fragmento de cdigo comprueba si el
cursor est despus de la ltima fila antes de llamar al mtodo previous en un
D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 21
bucle whi l e. Si el mtodo isAfterLast devuelve false, el cursor no estar
despus de la ltima fila, por eso se llama al mtodo afterLast. Esto garantiza
que el cursor estra despus de la ltima fila antes de utilizar el mtodo
previous en el bucle whi l e para cubrir todas las filas de srs.
if (srs.isAfterLast() == false) {
srs.afterLast();
}
while (srs.previous()) {
String name = srs.getString("COF_NAME");
float price = srs.getFloat("PRICE");
System.out.println(name + " " + price);
}
Antes de poder aprovechar estas ventajas, necesitamos crear un objeto
ResultSet Scrollable.
Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
ResultSet srs = stmt.executeQuery("SELECT COF_NAME, PRICE FROM COFFEES");
Este cdigo es similar al utilizado anteriormente, excepto en que aade
dos argumentos al mtodo createStatement. El primer argumento es una de
las tres constantes aadidas al API ResultSet para indicar el tipo de un
objeto ResultSet: TYPE_FORWARD_ONLY, TYPE_SCROLL_INSENSITIVE,
y TYPE_SCROLL_SENSITIVE. El segundo argumento es una de las dos
constantes de ResultSet para especificar si la hoja de resultados es de slo
lectura o actualizable:CONCUR_READ_ONLY y CONCUR_UPDATABLE. Lo
que debemos recordar aqu es que si especificamos un tipo, tambin debemos
especificar si es de slo lectura o actualizable
TYPE_FORWARD_ONLY se crea una hoja de resultados no
desplazable, es decir, una hoja en la que el cursor slo se mueve
hacia delante (es el valor por defecto).
TYPE_SCROLL_INSENSITIVE permite crear un Resultset
desplazable. No refleja cambios.
TYPE_SCROLL_SENSITIVE permite crear un Resultset
desplazable. La diferencia con el anterior es que en este segundo
caso si los datos sufren modificaciones despus de tener abierto el
ResultSet, dichos cambios se ven reflejados en el ResultSet
CONCUR_READ_ONLY crea un ResultSet de slo lectura.
CONCUR_UPDATABLE crea unResultSet que permite actualizacin.
2.7.1 ResultSets Actualizables
Una actualizacin es la modificacin del valor de una columna de la fila
actual. Supongamos que queremos aumentar el precio del caf "French Roast
Decaf" a 10.99. utilizando el API JDBC 1.0, la actualizacin podra ser algo como
esto.
stmt.executeUpdate("UPDATE COFFEES SET PRICE = 10.99" +
"WHERE COF_NAME = FRENCH_ROAST_DECAF");
D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 22
El siguiente fragmento de cdigo muestra otra forma de realizar la
actualizacin, esta vez utilizando el API JDBC 2.0.
uprs.last();
uprs.updateFloat("PRICE", 10.99);
Los mtodos updateXXX de ResultSet toman dos parmetros: la
columna a actualizar y el nuevo valor a colocar en ella. Al igual que en
los mtodos getXXX de ResultSet., el parmetro que designa la columna
podra ser el nombre de la columna o el nmero de la columna. Existe un
mtodo updateXXX diferente para cada tipo (updateString,
updateBigDecimal, updateInt, etc.)
Para que la actualizacin tenga efecto en la base de datos y no slo
en la hoja de resultados, debemos llamar al mtodo updateRow de
ResultSet.
Ejemplo:
uprs.last();
uprs.updateFloat("PRICE", 10.99);
uprs.updateRow();
Si hubiramos movido el cursor a una fila diferente antes de llamar al
mtodo updateRow, la actualizacin se habra perdido. Si, por el contrario, nos
damos cuenta de que el precio debera haber sido 10.79 en vez de 10.99
podramos haber cancelado la actualizacin llamando al mtodo
cancelRowUpdates. Tenemos que llamar al mtodo cancelRowUpdates antes
de llamar al mtodo updateRow; una vez que se llama a updateRow, llamar
a cancelRowUpdates no har nada. Observa que cancelRowUpdates cancela
todas las actualizaciones en una fila, por eso, si haba muchas llamadas a
mtodo updateXXX en la misma fila, no podemos cancelar slo una de ellas. El
siguiente fragmento de cdigo primero cancela el precio 10.99 y luego lo
actualiza a 10.79.
uprs.last();
uprs.updateFloat("PRICE", 10.99);
uprs.cancelRowUpdates();
uprs.updateFloat("PRICE", 10.79);
uprs.updateRow();
2.7.2 Insertar y Borrar Filas en un ResultSet
INSERTAR FILAS
Hasta ahora hemos visto como insertar y borrar filas usando el API JDBC 1.0,
recordando el cdigo esto se hara de la siguiente forma:
stmt.executeUpdate("INSERT INTO COFFEES " +
"VALUES ('Kona', 150, 10.99, 0, 0)");
D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 23
Se puede hacer esto mismo sin comandos SQL utilizando los mtodos de
ResultSet del API JDBC 2.0. Bsicamente, despus de tener un objeto
ResultSet con los resultados de la tabla COFFEES, podemos construir una
nueva fila insertndola tanto en la hoja de resultados como en la tabla COFFEES
en un slo paso.
Se construye una nueva fila en una llamada "fila de insercin", una
fila especial asociada con cada objeto ResultSet. Esta fila realmente no
forma parte de la hoja de resultados; podemos pensar en ella como un
buffer separado en el que componer una nueva fila.
El primer paso ser mover el cursor a la fila de insercin, lo que
podemos hacer llamando al mtodo moveToInsertRow. El siguiente paso
ser seleccionar un valor para cada columna de la fila.
Veamos un ejemplo:
Connection con = DriverManager.getConnection("jdbc:mySubprotocol:mySubName");
Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet uprs = stmt.executeQuery("SELECT * FROM COFFEES");
uprs.moveToInsertRow(); //MUEVE A UNA FILA DE INSERCIN
uprs.updateString("COF_NAME", "Kona");
uprs.updateInt("SUP_ID", 150);
uprs.updateFloat("PRICE", 10.99);
uprs.updateInt("SALES", 0);
uprs.updateInt("TOTAL", 0);
uprs.insertRow();
BORRAR FILAS
Borrar una fila es la tercera forma de modificar un objeto ResultSet, y es
la ms simple. Todo lo que tenemos que hacer es mover el cursor a la fila
que queremos borrar y luego llamar al mtodo deleteRow. Por ejemplo, si
queremos borrar la cuarta fila de la hoja de resultados uprs, nuestro cdigo se
parecera a esto.
uprs.absolute(4);
uprs.deleteRow();
La cuarta fila ha sido eliminada de uprs y de la base de datos.
El nico problema con las eliminaciones es lo que ResultSet
realmente hace cuando se borra una fila. Con algunos driver JDBC, una
lnea borrada es eliminada y ya no es visible en una hoja de resultados.
Algunos drives JDBC utilizan una fila en blanco en su lugar pone
(un "hole") donde la fila borrada fuera utilizada. Si existe una fila en
blanco en lugar de la fila borrada, se puede utilizar el mtodo absolute
con la posicin original de la fila para mover el cursor, porque el nmero
de filas en la hoja de resultados no ha cambiado.
D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 24
En cualquier caso, deberamos recordar que los drivers JDBC
manejan las eliminaciones de forma diferente. Por ejemplo, si
escribimos una aplicacin para ejecutarse con diferentes bases de datos,
no deberamos escribir cdigo que dependiera de si hay una fila vaca en
la hoja de resultados.
Se puede utilizar el mtodo refreshRow para obtener los ltimos valores de
una fila en la base de datos. Este mtodo puede utilizar muchos recursos,
especialmente si el controlador de la base de datos devuelve mltiples filas cada
vez que se llama a refreshRow. De todas formas, puede utilizarse cuando es
crtico tener los ltimos datos. Incluso aunque una hoja de resultados sea
sensible y los cambios sean visibles, una aplicacin no podra ver siempre los
ltimos cambios si el driver recupera varias filas a la vez y las almacena. Por eso,
utilizar el mtodo refreshRow es el nico mtodo para asegurarnos que estamos
viendo los ltimos datos.
Ejemplo:
Statement stmt = con.createStatement(
ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet uprs = stmt.executeQuery(SELECT COF_NAME, PRICE FROM
COFFEES);
uprs.absolute(4);
Float price1 = uprs.getFloat("PRICE");
// do something. . .
uprs.absolute(4);
uprs.refreshRow();
Float price2 = uprs.getFloat("PRICE");
if (price2 > price1) {
// do something. . .
}
2.8 Utilizar Transacciones
Hay veces que no queremos que una sentencia tenga efecto a menos que
otra tambin suceda.
Una transacin es un conjunto de una o ms sentencias que se ejecutan
como una unidad, por eso o se ejecutan todas o no se ejecuta ninguna.
Desactivar el modo Auto-entrega
Cuando se crea una conexin, est en modo auto-entrega. Esto
significa que cada sentencia SQL individual es tratada como una
transacin y ser automticamente entregada justo despus de ser
ejecutada. (Para ser ms preciso, por defecto, una sentencia SQL ser
entregada cuando est completa, no cuando se ejecuta. Una sentencia est
completa cuando todas sus hojas de resultados y cuentas de actualizacin han
D
F
S
I
F
r
a
n
c
i
s
c
o

V
i
c
e
n
t
e

L

p
e
z

F
e
r
n

n
d
e
z
Fundamentos de Programacin 2 ASI 2005-2006
IES San Juan Bosco UT7
Profesor: Francisco Vicente Lpez Fernndez 25
sido recuperadas. Sin embargo, en la mayora de los casos, una sentencia est
completa, y por lo tanto, entregada, justo despus de ser ejecutada).
La forma de permitir que dos o ms sentencia sean agrupadas en una
transacin es desactivar el modo auto-entrega. Esto se demuestra en el siguiente
cdigo, donde con es una conexin activa.
con.setAutoCommit(false);
Ejemplo:
con.setAutoCommit(false);
PreparedStatement updateSales = con.prepareStatement(
"UPDATE COFFEES SET SALES = ? WHERE COF_NAME
LIKE ?");
updateSales.setInt(1, 50);
updateSales.setString(2, "Colombian");
updateSales.executeUpdate();
PreparedStatement updateTotal = con.prepareStatement(
"UPDATE COFFEES SET TOTAL = TOTAL + ? WHERE COF_NAME LIKE
?");
updateTotal.setInt(1, 50);
updateTotal.setString(2, "Colombian");
updateTotal.executeUpdate();
con.commit();
con.setAutoCommit(true);
Es bueno desactivar el modo auto-commit slo mientras
queramos estar en modo transacin. De esta forma, evitamos bloquear
la base de datos durante varias sentencias, lo que incrementa los
conflictos con otros usuarios.
2.9 Ejercicio
Modificar el ejercicio de la tienda de vinos virtual realizado en la UT-6
(Ejercicio 5.3) para que el catlogo se cargue de una base de datos.
Adems debemos crear una aplicacin JSP (JSP del vendedor) que permita
crear nuevos Vinos en el catlogo.
Pasos:
1.- Crear la tabla / tablas necesarias en ORACLE para poder guardar el
catlogo
2.- Crear una nueva aplicacin basada en el ejercicio 5.3 que obtenga los
datos de la tabla / tablas creadas en el paso 1
3.- Crear una nueva aplicacin JSP (JSP del vendedor), la cual ser un
mantenimiento del catlogo y permitir: visualizar los vinos, crear nuevos
vinos y eliminar los existentes.

You might also like