Mostrando entradas con la etiqueta Programación. Mostrar todas las entradas
Mostrando entradas con la etiqueta Programación. Mostrar todas las entradas

sábado, 21 de abril de 2012

Rich Snippets. Introducción y ejemplo

Los "rich snippets" (fragmentos enriquecidos) son líneas de texto que aparecen debajo de cada resultado de búsqueda. La forma más clara de verlo, es con un ejemplo. Buscamos, por ejemplo, una receta (arroz con leche) y algunos de los resultados que aparecen son estos:


Uno de los resultados, el que está rodeado, vemos que tiene una línea en la que aparece la valoración de la receta, el número de opiniones y la duración de la misma. Esto es un fragmento enriquecido.

Para obtener un fragmento enriquecido debemos seguir estos pasos:
  1. Elegir un formato de marcado:
    • Microdatos
    • (recomendado por Google). A mí me ha parecido el más sencillo, ya que sólo tienes que añadir algunos atributos a las etiquetas html (normalmente, <span> o <div>).
    • Microformatos.
    • RDFa.
  2. Marcar el contenido. Google ofrece fragmentos enriquecidos para estos tipos. Pero en schema.org, puedes encontrar muchos más y también son comprendidos por Google, entre otros buscadores.
  3. Probar el marcado. Google ofrece una herramienta para probar tus fragmentos enriquecidos. De esta manera te puedes asegurar de que Google puede leer los datos marcados.
Si después de haber marcado tu sitio web y haber esperado algunas semanas, los fragmentos enriquecidos siguen sin aparecer en el buscador, puedes visitar este enlace.

PRUEBA - Persona (contacto)
Voy a hacer una prueba para el tipo de dato "persona" que ofrece Google, que puede tener las siguientes propiedades:
  • name (nombre).
    • LISA MARIE SIMPSON
  • nickname (apodo).
    • LISA
  • photo (foto).
  • title (cargo de la persona).
    • ESTUDIANTE
  • role (función de la persona).
  • url (enlace a una página web).
  • affiliation (nombre de una organización a la que está afiliada la persona).
    • ESCUELA PRIMARIA DE SPRINGFIELD
  • friend (identifica una relación social entre la persona descrita y otra).
    • MILHOUSE
  • contact (identifica una relación social entre la persona descrita y otra).
  • acquaintance (identifica una relación social entre la persona descrita y otra).
  • address (dirección de la persona).
    • street-address
      • EVERGREEN TERRACE, 742
    • locality
      • SPRINGFIELD
    • region
    • postal-code
    • country-name
      • USA
CÓDIGO
<div itemscope itemtype="http://data-vocabulary.org/Person">
  <img src="http://mickydenys.galeon.com/images/dibu4.gif" itemprop="photo" />
  Soy <span itemprop="name">Lisa Marie Simpson</span>, 
  pero todos me llaman <span itemprop="nickname">Lisa</span>.
  Esta es la web de mi familia: 
  <a href="http://www.thesimpsons.com/" itemprop="url">LOS SIMPSON</a>.
  Vivo en la calle 
  <span itemprop="address" itemscope itemtype="http://data-vocabulary.org/Address">
    <span itemprop="street-address">Evergreen Terrace, 742</span> en
    <span itemprop="locality">Springfield</span>, 
    <span itemprop="country-name">USA</span> 
  </span>
    y soy <span itemprop="title">estudiante</span>
  en la <span itemprop="affiliation">Escuela Primaria de Springfield</span>
  Mi amigo es:
   <a href="http://www.milhouse.com" rel="friend">Milhouse</a>
</div>

En la primera línea encontramos los atributos "itemscope" e "itemtype", incluidos en el <div>. "itemscope" indica que el contenido del <div> describe un elemento y con "itemtype" definimos de qué tipo de elemento se trata, en este caso, una persona. En ese <div> se describen las propiedades de la persona, para ello, a los elementos que contengan alguna de esas propiedades se les añade la etiqueta "itemprop", indicando la propiedad correspondiente, por ejemplo: <span itemprop="title">.

Si nos fijamos en la descripción de la dirección, propiedad "address", vemos que se trata de otro elemento. Es decir, un elemento puede, a su vez, estar formado por otros elementos.

RESULTADO DE LA PRUEBA DEL MARCADO


Podemos ver que en la tercera línea salen algunos de los datos que habíamos indicado y así es como se mostraría el resultado en un buscador. También cabe destacar que no se muestran todos los datos que hemos etiquetado, como por ejemplo la foto. Supongo que el buscador determina, en función del tipo que hayamos elegido y de las propiedades que le hayamos indicado, los datos que muestra.

domingo, 11 de marzo de 2012

Ejercicios MCD y MCM

EJERCICIO 1 - Máximo Común Divisor
Realizar una aplicación que calcule el máximo común divisor de dos números introducidos por teclado.

Module Module1

    Sub Main()
        Dim numero1, numero2, a As Integer
        Dim n1, n2 As Integer

        Console.Write("Número 1: ")
        numero1 = Console.ReadLine
        Console.Write("Número 2: ")
        numero2 = Console.ReadLine

        n1 = numero1
        n2 = numero2

        If (numero1 < numero2) Then
            a = numero2
            numero2 = numero1
            numero1 = a
        End If

        ' Algoritmo de Euclides
        While (numero1 Mod numero2 <> 0)
            a = numero1
            numero1 = numero2
            numero2 = a Mod numero2
        End While

        Console.WriteLine("El MCD de " & n1 & " y " & n2 & " es: " & numero2)

        Console.ReadKey()

    End Sub

End Module

EJERCICIO 2 - Mínimo Común Múltiplo
Realizar una aplicación que calcule el mínimo común múltiplo de dos números introducidos por teclado.

Module Module1

    Sub Main()
        Dim numero1, numero2, mcm As Integer

        Console.Write("Número 1: ")
        numero1 = Console.ReadLine
        Console.Write("Número 2: ")
        numero2 = Console.ReadLine

        mcm = (numero1 / MCD(numero1, numero2)) * numero2

        Console.WriteLine("El mcm de " & numero1 & " y " & numero2 & " es: " & mcm)

        Console.ReadKey()

    End Sub

    Function MCD(ByVal num1 As Integer, ByVal num2 As Integer) As Integer
        Dim a As Integer
        If (num1 < num2) Then
            a = num2
            num2 = num1
            num1 = a
        End If

        ' Algoritmo de Euclides
        While (num1 Mod num2 <> 0)
            a = num1
            num1 = num2
            num2 = a Mod num2
        End While

        Return num2
    End Function

End Module

Ejercicios de búsqueda en tablas

EJERCICIO 1
Realizar una aplicación que contenga la función "Buscar", a la que se le pasará un array de números enteros y el elemento a buscar. Debe devolver un Boolean indicando si lo ha encontrado (true) o no (false).

Module Module1

    Sub Main()
        Dim numeros() As Integer = {32, 5, 3, 2, 66, 2, 7, 45, 90}
        Dim num As Integer = 91
        Dim encontrado As Boolean = Buscar(numeros, num)

        Console.WriteLine(encontrado)

        Console.ReadKey()

    End Sub

    Function Buscar(ByVal numeros() As Integer, ByVal num As Integer) As Boolean
        Dim encontrado As Boolean = False

        For Each n As Integer In numeros
            If (n = num) Then
                encontrado = True
            End If
            'Si lo encuentra, sale del "for"
            If encontrado Then Exit For
        Next

        Return encontrado
    End Function

End Module

EJERCICIO 2
Modificar la función anterior para que implemente el algoritmo de búsqueda dicotómica. En este caso se le pasará un array ordenado de menor a mayor.

Module Module1

    Sub Main()
        Dim numeros() As Integer = {2, 2, 3, 5, 7, 32, 45, 66, 90}
        Dim num As Integer = 4
        Dim encontrado As Boolean = Buscar(numeros, num)

        Console.WriteLine(encontrado)

        Console.ReadKey()

    End Sub

    Function Buscar(ByVal numeros() As Integer, ByVal num As Integer) As Boolean
        Dim izquierda As Integer = 0
        Dim derecha As Integer = numeros.Length - 1
        Dim centro As Integer

        While (izquierda <= derecha)
            centro = (izquierda + derecha) / 2
            If (numeros(centro) = num) Then
                Return True
            End If
            If (numeros(centro) > num) Then
                derecha = centro - 1
            ElseIf (numeros(centro) < num) Then
                izquierda = centro + 1
            End If
        End While

        Return False

    End Function

End Module

EJERCICIO 3
Modificar de nuevo la función para que en lugar de un Boolean indicando si ha encontrado o no el elemento buscado, devuelva un entero indicando la posición donde se encuentra dicho elemento. Si el array no contiene ese elemento, devolverá -1.

Module Module1

    Sub Main()
        Dim numeros() As Integer = {2, 2, 3, 5, 7, 32, 45, 66, 90}
        Dim num As Integer = 90
        Dim posicion As Integer = Buscar(numeros, num)

        If (posicion = -1) Then
            Console.WriteLine("No encontrado")
        Else
            Console.WriteLine("Encontrado en la posicion: " & posicion)
        End If

        Console.ReadKey()

    End Sub

    Function Buscar(ByVal numeros() As Integer, ByVal num As Integer) As Integer
        Dim izquierda As Integer = 0
        Dim derecha As Integer = numeros.Length - 1
        Dim centro As Integer

        While (izquierda <= derecha)
            centro = (izquierda + derecha) / 2
            If (numeros(centro) = num) Then
                Return centro
            End If
            If (numeros(centro) > num) Then
                derecha = centro - 1
            ElseIf (numeros(centro) < num) Then
                izquierda = centro + 1
            End If
        End While

        Return -1

    End Function

End Module

viernes, 9 de marzo de 2012

Ejercicio ordenar (método de la burbuja)

Realizar una aplicación que solicite la cantidad de números que el usuario quiere introducir, después los vaya pidiendo uno a uno y finalmente los ordene utilizando el algoritmo de ordenación de la burbuja y los muestre en consola.

NOTA: enlace a una página de algoritmos de ordenación http://www.cs.ubc.ca/~harrison/Java/sorting-demo.html

Module Module1

    Sub Main()
        Dim temporal As Double
        Dim cuantos As Integer
        Console.WriteLine("¿Cuántos números quiere introducir?")
        cuantos = Console.ReadLine

        cuantos -= 1
        Dim numeros(cuantos) As Double
        For i As Integer = 0 To cuantos
            Console.Write("Número: ")
            numeros(i) = Console.ReadLine
        Next

        For i As Integer = 0 To (cuantos - 1)
            For j As Integer = (i + 1) To cuantos
                If (numeros(i) > numeros(j)) Then
                    temporal = numeros(i)
                    numeros(i) = numeros(j)
                    numeros(j) = temporal
                End If
            Next
        Next

        Console.WriteLine("Números ordenados de menor a mayor:")
        For Each num As Double In numeros
            Console.WriteLine(num)
        Next

        Console.ReadKey()

    End Sub

End Module

lunes, 5 de marzo de 2012

Varios ejercicios

EJERCICIO 1
Realizar una aplicación que recoja 10 números enteros desde teclado y al final muestre el mayor y el menor.

Module Module1

    Sub Main()
        Dim numero, mayor, menor As Integer

        Console.Write("Número: ")
        mayor = Console.ReadLine
        menor = mayor

        For i As Integer = 1 To 9
            Console.Write("Número: ")
            numero = Console.ReadLine
            If (numero > mayor) Then
                mayor = numero
            ElseIf (numero < menor) Then
                menor = numero
            End If
        Next

        Console.WriteLine("El número mayor es: " & mayor)
        Console.WriteLine("El número menor es: " & menor)

        Console.ReadKey()

    End Sub

End Module

EJERCICIO 2
Realizar una aplicación que genere un array de números aleatorios y los muestre. Para ello se utilizará la clase Random. El array también tendrá tamaño aleatorio.

Module Module1

    Sub Main()
        Dim azar As Random = New Random
        ' El método Next(int1, int2) de la clase Random devuelve un número aleatorio dentro de ese intervalo
        Dim cuantos As Integer = azar.Next(1, 100)
        Dim numeros(cuantos) As Integer

        For i As Integer = 0 To cuantos
            numeros(i) = azar.Next(-200, 200)
        Next

        For Each num As Integer In numeros
            Console.WriteLine(num)
        Next

        Console.ReadKey()

    End Sub

End Module

Ejercicio "Cambio"

Realizar una aplicación que recoja por teclado la cantidad total a pagar y la cantidad que se ha entregado. La aplicación debe calcular el cambio correspondiente con el menor número de monedas y/o billetes posibles.

Module Module1

    Sub Main()
        Dim total, entregado, vuelta As Double
        Dim billetes() As Single = {500, 200, 100, 50, 20, 10, 5}
        Dim monedas() As Single = {200, 100, 50, 20, 10, 5, 2, 1}
        Dim resultado(14) As Integer

        Console.Write("Introduzca la cantidad total: ")
        total = Console.ReadLine
        Console.Write("Introduzca la cantidad entregada: ")
        entregado = Console.ReadLine

        Console.WriteLine()

        'Comprobamos las cantidades
        If (total > entregado) Then
            Console.WriteLine("Las cantidades no son correctas.")
            Console.WriteLine("La cantidad entregada debe ser mayor o igual al total.")
        ElseIf (total = entregado) Then
            Console.WriteLine("Ha entregado la cantidad exacta. No tiene cambio.")
        Else
            'Calculamos la vuelta
            vuelta = entregado - total

            Console.WriteLine("Su vuelta es: " & vuelta & " Euros.")

            ' 1º con los billetes
            Dim i As Integer = 0
            For Each cantidad As Single In billetes
                ' \ división entera
                resultado(i) = vuelta \ cantidad
                ' Lo que queda es el resto
                vuelta = vuelta Mod cantidad
                i += 1
            Next

            ' Me daba error si cogía el valor de las monedas con decimales
            ' Así que multipliqué tanto el valor de estas como lo que queda de vuelta por 100
            vuelta = vuelta * 100

            ' 2º con las monedas
            For Each cantidad As Single In monedas
                resultado(i) = vuelta \ cantidad
                vuelta = vuelta Mod cantidad
                i += 1
            Next

            Dim j As Integer = 0
            For i = 0 To 6
                If (resultado(j) <> 0) Then
                    Console.WriteLine(resultado(j) & " billetes de " & billetes(i) & " Euros.")
                End If
                j += 1
            Next

            For i = 0 To 7
                If (resultado(j) <> 0) Then
                    Console.WriteLine(resultado(j) & " monedas de " & (monedas(i) / 100) & " Euros.")
                End If
                j += 1
            Next

        End If

        Console.ReadKey()
    End Sub

End Module

sábado, 3 de marzo de 2012

"While... End" y "Do... Loop"

A continuación se muestra un sencillísimo ejemplo de todas las variantes de estos bucles:

Module Module1

    Sub Main()
        DoLoop()
        Console.ReadKey()
    End Sub

    Sub DoLoop()
        Dim a As Integer = 1

        ' While
        While (a < 10)
            a += 1
        End While

        ' Equivalente al while
        Do While (a < 10)
            a += 1
        Loop

        ' Evalúa la condición al final
        ' Por lo tanto el bloque se ejecutará al menos una vez
        Do
            a += 1
        Loop While (a <= 10)

        ' Se ejecuta "hasta que", no "mientras"
        Do Until (a > 10)
            a += 1
        Loop

        ' Lo mismo, pero el bloque se ejecuta al menos una vez
        Do
            a += 1
        Loop Until (a > 10)
    End Sub

End Module

Ejercicio "Ecuación de 2º grado"

Realizar una aplicación que calcule las raíces de una ecuación de segundo grado y las muestre, una vez que ha recogido los coeficientes por teclado.

Imports System.Math

Module Module1

    Sub Main()

        Dim a, b, c As Integer
        Dim discriminante As Integer

        Console.WriteLine("ax^2 + bx + c = 0")
        Console.WriteLine("Introduce los valores de los coeficientes.")
        Console.Write("a: ")
        a = Console.ReadLine
        Console.Write("b: ")
        b = Console.ReadLine
        Console.Write("c: ")
        c = Console.ReadLine

        If (a = 0) Then
            Console.WriteLine("La ecuación no es de segundo grado.")
        Else
            discriminante = b ^ 2 - (4 * a * c)

            If (discriminante = 0) Then
                Dim raiz As Double
                raiz = -b / (2 * a)
                Console.WriteLine("La ecuación tiene un único resultado.")
                Console.WriteLine("Raíz: " & raiz)
            ElseIf (discriminante > 0) Then
                Dim raiz1, raiz2 As Double
                raiz1 = (-b + (Sqrt(discriminante))) / (2 * a)
                raiz2 = (-b - (Sqrt(discriminante))) / (2 * a)
                Console.WriteLine("La ecuación tiene dos resultados.")
                Console.WriteLine("Raíz 1: " & raiz1)
                Console.WriteLine("Raíz 2: " & raiz2)
            Else
                Console.WriteLine("La ecuación no tiene resultado para esos valores.")
            End If
        End If

        Console.ReadKey()

    End Sub

End Module

Instrucción Select... Case

La instrucción Select... Case es similar al Switch de Java, pero bastante más potente, ya que permite indicar intervalos, expresiones y varios valores en el mismo "Case".

EJEMPLO DE SELECT... CASE

Module Module1

    Sub Main()
        Menu()
        Console.ReadKey()
    End Sub

    Sub Menu()
        Dim opcion As Integer = 1
        While (opcion <> 0)
            Console.Clear()
            Console.WriteLine("Menú")
            Console.WriteLine("====")
            Console.WriteLine("1. Si introduce uno se ejecuta esta opción")
            Console.WriteLine("2-5. Si introduce un número entre 2 y 5 se ejecuta esta")
            Console.WriteLine("6, 9, 10. Si introduce 6, 9 o 10 se ejecuta esta")
            Console.WriteLine("Mayor de 15. Si es mayor de 15 se ejecuta esta")
            Console.WriteLine("0. Salir")
            Console.WriteLine()
            Try
                opcion = CInt(Console.ReadLine())
            Catch ex As InvalidCastException
                ' Sólo se ve desde el Visual Studio
                Debug.WriteLine("Error: " & ex.Message)
                opcion = 7
            Catch e As OverflowException
                ' lo que queramos hacer
            End Try

            Select Case opcion
                Case 1
                    Console.WriteLine("Ha seleccionado la opción 1")
                Case 2 To 5 ' Intervalo
                    Console.WriteLine("Ha seleccionado una opción entre 2 y 5")
                Case 6, 9, 10 ' Varios valores
                    Console.WriteLine("Ha seleccionado 6, 9 o 10")
                Case Is > 15 ' Expresión
                    Console.WriteLine("Ha seleccionado una opción mayor que 15")
                Case 0
                    Console.WriteLine("Ha elegido salir, ¡adiós!")
                Case Else ' Si no entra por las anteriores...
                    Console.WriteLine("Ha elegido una opción incorrecta")
            End Select

            ' vbCrLf -> constante que representa retorno de carro más salto de línea
            Console.WriteLine(vbCrLf & "Pulse ENTER para continuar")
            Console.ReadKey()

        End While
    End Sub

End Module

EJERCICIO
Realizar una aplicación que tenga un menú que permita elegir entre calcular el área de un círculo o un rectángulo. Dependiendo de lo que se elija, se pedirán los datos correspondientes y se mostrará el resultado.

Module Module1

    Sub Main()
        Const PI As Double = 3.14159265
        Dim opcion As Integer
        Dim ancho, alto As Double
        Dim radio As Double

        Do
            Menu()
            Console.Write("Elija una opción: ")
            opcion = Console.ReadLine
            Console.WriteLine()

            Select Case opcion
                Case 1
                    Console.Write("Ancho: ")
                    ancho = Console.ReadLine
                    Console.Write("Alto: ")
                    alto = Console.ReadLine
                    Console.WriteLine("Área del rectángulo: " & (ancho * alto) & " uds cuadradas")
                Case 2
                    Console.Write("Radio: ")
                    radio = Console.ReadLine
                    Console.WriteLine("Área del círculo: " & (PI * (radio ^ 2)) & " uds cuadradas")
                Case 3
                    Console.WriteLine("Ha elegido salir del programa.")
                Case Else
                    Console.WriteLine("Opción incorrecta.")
            End Select

            Console.WriteLine(vbCrLf & "Pulse ENTER para continuar")
            Console.ReadKey()

        Loop While opcion <> 3

    End Sub

    'Procedimiento que muestar el menú
    Sub Menu()
        Console.Clear()
        Console.WriteLine("CALCULAR ÁREA")
        Console.WriteLine()
        Console.WriteLine("1. Rectángulo")
        Console.WriteLine("2. Círculo")
        Console.WriteLine("3. Salir")
    End Sub

End Module

miércoles, 29 de febrero de 2012

Varios ejercicios

EJERCICIO 1
Hacer una aplicación que pida un nombre por teclado y luego salude a esa persona.

Module Module1

    Sub Main()
        Dim nombre As String

        Console.WriteLine("¿Cómo te llamas?")
        nombre = Console.ReadLine
        Console.WriteLine()
        Console.WriteLine("Hola " & nombre)

        Console.ReadKey()

    End Sub

End Module

EJERCICIO 2
Hacer una aplicación que recoja números desde teclado hasta que se introduzca vacío (cadena ""). Se deben sumar estos números y mostrar el resultado.

Module Module1

    Sub Main()
        Dim Numero As String
        Dim Resultado As Integer = 0

        Do
            Console.Write("Número: ")
            Numero = Console.ReadLine()
            Try
                Resultado = Resultado + CInt(Numero)
            Catch ex As InvalidCastException

            End Try
        Loop While Numero <> ""

        Console.WriteLine("Resultado final: " & Resultado)

        Console.ReadKey()

    End Sub

End Module

NOTA: por defecto, se realizan conversiones implícitas de datos, si quisiésemos desactivar esta opción (recomendable), tendremos que poner la siguiente línea al principio de nuestro código (antes de Module Nombre_del_Módulo)

Option Explicit On

En este caso, para realizar las conversiones, se utilizarán las funciones de conversión. Algunas de estas funciones son:

  • CDate(expresión): para convertir a tipo Date
  • CDbl(expresión): para convertir a tipo Double
  • CInt(expresión): para convertir a tipo Integer
  • CStr(expresión): para convertir a tipo String

EJERCICIO 3
Hacer una aplicación que pida por teclado el radio de una circunferencia y muestre su área y su perímetro.

Module Module1

    Sub Main()
        Dim Radio, Area, Perimetro As Double
        ' Definición de constantes en VB
        Const PI As Double = 3.14159265

        Console.Write("Radio de la circunferencia: ")
        Radio = Console.ReadLine

        Area = PI * (Radio ^ 2)
        Perimetro = 2 * PI * Radio

        Console.WriteLine("Área: " & Area & " uds cuadradas")
        Console.WriteLine("Perímetro: " & Perimetro & "uds")

        Console.ReadKey()

    End Sub

End Module

martes, 28 de febrero de 2012

Hola Mundo - Comenzando con VB.NET

Como no puede ser de otra manera, empezamos Visual Basic .NET con un "Hola Mundo":

Module MiModulo

    Sub Main()
        'Mostrar un mensaje en consola
        Console.WriteLine("Hola mundo")

        'Para que la consola se quede abierta,
        'A la espera de que pulsemos una tecla
        Console.ReadKey()
    End Sub

End Module

sábado, 14 de enero de 2012

J2EE. Aplicaciones Web con Java

Tras el parón navideño, regreso... Dentro de nada terminaré con Java, por lo menos durante un tiempo. Falta aplicaciones web (con las que empiezo ahora), servicios web y applets.

------

J2EE son las siglas de Java 2 Enterprise Edition. Se trata de una versión ampliada de J2SE, con más API, pensada para crear aplicaciones distribuidas multicapa basadas en componentes, que se ejecutan sobre uno o varios servidores de aplicaciones.

Servidores J2EE. Son el "middleware" que une todos los componentes para que funcionen juntos. Algunos de ellos son:
  • JBoss. Implementa toda la especificación J2EE (Contenedor Web + Contenedor EJB)
  • GlassFish
  • Apache Tomcat. Sólo implementa el Contenedor Web.

Los servidores J2EE estarán formados por dos contenedores y a cada componente le corresponderá uno de ellos:
  1. Contenedor Web
    1. Servlets. Permiten la comunicación con el servidor vía HTTP.
    2. Páginas JSP. Páginas HTML dinámicas que incluyen código Java.
  2. Contenedor EJB
    1. EntityBeans
    2. SessionBeans
    3. MessageDrivenBeans

Despliegue de aplicaciones J2EE. Desplegar una aplicación es instalarla en un servidor de aplicaciones J2EE.

Los desplegables o módulos J2EE son archivos .zip que contienen ficheros .class, diferentes recursos y el descriptor de despliegue (documento XML).
  • WAR. Web ARchive. Sólo contiene componentes del contenedor web, es decir, servlets y páginas JSP y su descriptor de despliegue es el fichero "web.xml".
  • EAR. Enterprise ARchive.
  • JAR. Java ARchive.

APLICACIONES WEB

Las aplicaciones web se implementan mediante los componentes de la capa web (servlets y páginas JSP). Se distribuyen en forma de WAR que se despliegan en servidores J2EE.

------

De momento, sólo veremos esta última parte de J2EE, por lo que es suficiente con el servidor Apache Tomcat para realizar nuestras pruebas.

En próximas entradas pondré algunos ejemplos de servlets, páginas JSP y filters.

sábado, 3 de diciembre de 2011

Formularios en Java. Componentes (III)

Para ir terminando con los componentes de los formularios, o mejor dicho, para terminar de explicar tan sólo algunos de los más importantes y frecuentes, he hecho otro ejemplo, en el que aparecen fundamentalmente los siguientes componentes:
  • JMenu y JMenuItem
  • JToolBar
  • JFileChoser
  • JColorChoser
El ejemplo pretende ser un editor de texto, pero muy, muy sencillo, ya que la idea es ver la funcionalidad de los componentes anteriores. Las clases que he implementado han sido dos, una para el formulario (editor) y otra para el selector de color. A continuación dejo el código, así como unas capturas de pantalla de como ha quedado.

Los iconos de los botones los he cargado con una clase que nos había pasado el profesor con esta finalidad.

EDITOR

@SuppressWarnings("serial")
public class EditorForm extends JFrame {

 private JTextArea texto;
 private JButton colorButton, negritaButton, cursivaButton;
 
 public EditorForm() {
  initForm();
  initComponents();
  setVisible(true);
 }

 private void initForm() {
  setTitle("Editor");
  setSize(640, 480);
  setResizable(false);
  setLocationRelativeTo(null);
  setDefaultCloseOperation(DISPOSE_ON_CLOSE);
 }

 private void initComponents() {
  crearMenu();
  crearBarraHerramientas();
  
  texto = new JTextArea();
  texto.setMargin(new Insets(10, 10, 10, 10));
  
  getContentPane().add(new JScrollPane(texto), BorderLayout.CENTER);
 }

 private void crearMenu() {
  JMenuItem nuevoMenuItem = new JMenuItem("Nuevo");
  nuevoMenuItem.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) { onNuevoMenuItemActionPerformed(e); }
  });
  JMenuItem guardarMenuItem = new JMenuItem("Guardar");
  JMenuItem guardarComoMenuItem = new JMenuItem("Guardar como...");
  guardarComoMenuItem.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) { onGuardarComoMenuItemActionPerformed(e); }
  });
  JMenuItem salirMenuItem = new JMenuItem("Salir");
  salirMenuItem.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) { onSalirMenuItemActionPerformed(e);
   }
  });
  
  JMenu archivoMenu = new JMenu("Archivo");
  archivoMenu.add(nuevoMenuItem);
  archivoMenu.addSeparator();
  archivoMenu.add(guardarMenuItem);
  archivoMenu.add(guardarComoMenuItem);
  archivoMenu.addSeparator();
  archivoMenu.add(salirMenuItem);
  
  JMenuBar barraMenu = new JMenuBar();
  barraMenu.add(archivoMenu);
  
  setJMenuBar(barraMenu);
 }

 protected void onNuevoMenuItemActionPerformed(ActionEvent e) {
  texto.setText("");
 }

 protected void onGuardarComoMenuItemActionPerformed(ActionEvent e) {
  JFileChooser guardarDialog = new JFileChooser();
  int resultado = guardarDialog.showSaveDialog(this);
  
  //Si la opción elegida ha sido "Guardar", le han dado a ese botón
  if (resultado == JFileChooser.APPROVE_OPTION) {
   //Donde se va a guardar
   File fichero = guardarDialog.getSelectedFile();

   //TODO Aquí implementaríamos el código necesario para guardar el texto
  }
 }

 protected void onSalirMenuItemActionPerformed(ActionEvent e) {
  dispose();
 }

 private void crearBarraHerramientas() {
  colorButton = new JButton(Iconos.COLOR);
  colorButton.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) { onColorButtonActionPerformed(e); }
  });
  negritaButton = new JButton(Iconos.NEGRITA);
  negritaButton.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) { onNegritaButtonActionPerformed(e); }
  });
  cursivaButton = new JButton(Iconos.CURSIVA);
  cursivaButton.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) { onCursivaButtonActionPerformed(e); }
  });
  
  JToolBar barraHerramientas = new JToolBar();
  barraHerramientas.add(colorButton);
  barraHerramientas.add(negritaButton);
  barraHerramientas.add(cursivaButton);
  //Esta propiedad indica si el usuario puede mover la barra de herramientas o no
  barraHerramientas.setFloatable(false);
  barraHerramientas.setOrientation(JToolBar.HORIZONTAL);
  getContentPane().add(barraHerramientas, BorderLayout.NORTH);
 }

 protected void onColorButtonActionPerformed(ActionEvent e) {
  ColorDialog dialogo = new ColorDialog();
  dialogo.setVisible(true);
  texto.setForeground(dialogo.getColor());
  dialogo.dispose();
 }

 protected void onNegritaButtonActionPerformed(ActionEvent e) {
  // TODO Aquí implementaríamos la funcionalidad de este botón
 }

 protected void onCursivaButtonActionPerformed(ActionEvent e) {
  // TODO Aquí implementaríamos la funcionalidad de este botón
 }
 
}




SELECTOR DE COLOR

@SuppressWarnings("serial")
public class ColorDialog extends JDialog {
 
 private Color color;

 private JColorChooser colorChooser;
 private JButton seleccionarButton, cancelarButton;
 
 public ColorDialog() {
  initForm();
  initComponents();
 }

 private void initForm() {
  setTitle("Seleccionar color");
  setSize(450, 300);
  setDefaultCloseOperation(DISPOSE_ON_CLOSE);
  setLocationRelativeTo(null);
  setModal(true);
 }

 private void initComponents() {
  colorChooser = new JColorChooser();
  
  seleccionarButton = new JButton("Seleccionar");
  seleccionarButton.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) { onSeleccionarButtonActionPerformed(e); }
  });
  cancelarButton = new JButton("Cancelar");
  cancelarButton.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) { onCancelarButtonActionPerformed(e); }
  });
  
  JPanel botonesPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
  botonesPanel.add(seleccionarButton);
  botonesPanel.add(cancelarButton);
  
  getContentPane().add(colorChooser, BorderLayout.CENTER);
  getContentPane().add(botonesPanel, BorderLayout.SOUTH);
 }

 protected void onSeleccionarButtonActionPerformed(ActionEvent e) {
  this.color = colorChooser.getColor();
  //No hacemos dispose() porque tenemos que recuperar el color seleccionado
  setVisible(false);
 }

 protected void onCancelarButtonActionPerformed(ActionEvent e) {
  setVisible(false);
 }
 
 public Color getColor() {
  return color;
 }
 
}

martes, 29 de noviembre de 2011

Formularios en Java. Componentes (II)

Continúo con ejemplos de componentes Swing para formularios. Para realizar estos ejemplos, he implementado un sencillo formulario que nos permita "gestionar" nuestros hobbies, por ejemplo, que nos permita ver el listado de libros que tenemos, añadir alguno nuevo, etc.

Los nuevos componentes utilizados han sido:
  • JTable. A su vez utiliza una clase que implementa TableModel.
  • JTabbedPane
  • JOptionPane
  • JCheckBox
  • JComboBox<E>
  • JTextArea
  • JRadioButton y ButtonGroup
Además, he utilizado algunos de los layouts vistos anteriormente, lo que le da mucho mejor aspecto a los fomrularios, sin necesidad de estar cuadrando coordenadas.

FORMULARIO INICIAL
Es el que se muestra al abrir la aplicación. Consiste en un panel con varias pestañas para cada uno de los hobbies que queramos gestionar. En la primera, se muestra un listado de libros.
Si hacemos clic en "Salir", se muestra un cuadro de diálogo creado con la clase JOptionPane, tal y como se puede ver en el código.

@SuppressWarnings("serial")
public class ComponentesFormII extends JFrame {

 JButton salirButton, nuevoDiscoButton, nuevoLibroButton;
 JTable discosTable, librosTable;
 LibrosTableModel librosTableModel;
 
 public ComponentesFormII() {
  initForm();
  initComponents();
  setVisible(true);
 }

 private void initForm() {
  setTitle("Mis hobbies");
  setSize(640, 480);
  setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
  addWindowListener(new WindowAdapter() {
   public void windowClosing(WindowEvent e) { onWindowClosing(e); }
  });
  setLocationRelativeTo(null);
 }

 private void initComponents() {
  salirButton = new JButton("Salir");
  salirButton.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) { onSalirButtonActionPerformed(e); }
  });
  salirButton.setMnemonic(KeyEvent.VK_S);
  
  JPanel botonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
  botonPanel.add(salirButton);
  
  JTabbedPane hobbiesPanel = new JTabbedPane();
  hobbiesPanel.add("Libros", librosPanel());
  //Paneles vacíos
  hobbiesPanel.add("Música", new JPanel());
  hobbiesPanel.add("Viajes", new JPanel());
  
  getContentPane().add(hobbiesPanel, BorderLayout.CENTER);
  getContentPane().add(botonPanel, BorderLayout.SOUTH);
 }

 protected void onWindowClosing(WindowEvent e) {
  salir();
 }

 protected void onSalirButtonActionPerformed(ActionEvent e) {
  salir();
 }

 private void salir() {
  BaseDatos.desconectar();
  //La clase JOptionPane nos permite mostrar diferentes cuadros de diálogo
  //Estableciendo las opciones en el constructor
  int respuesta = JOptionPane.showConfirmDialog(this, "¿Está seguro de que quiere salir?", "Salir", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
  if (respuesta == JOptionPane.YES_OPTION) {
   dispose();
  }
 }
 
 private JPanel librosPanel() {
  nuevoLibroButton = new JButton("Añadir libro");
  nuevoLibroButton.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) { onNuevoLibroButtonActionPerformed(e); }
  });
  nuevoLibroButton.setMnemonic(KeyEvent.VK_L);
  
  JPanel botonPanel = new JPanel();
  botonPanel.add(nuevoLibroButton);
  
  librosTableModel = new LibrosTableModel();
  librosTable = new JTable(librosTableModel);
  
  JPanel tablaPanel = new JPanel(new BorderLayout());
  tablaPanel.setBorder(BorderFactory.createTitledBorder(" Libros: "));
  //Debemos poner la tabla dentro de un panel de tipo JScrollPane
  //Para que se muestre correctamente
  tablaPanel.add(new JScrollPane(librosTable), BorderLayout.CENTER);
  tablaPanel.add(botonPanel, BorderLayout.SOUTH);
  
  return tablaPanel;
 }

 protected void onNuevoLibroButtonActionPerformed(ActionEvent e) {
  new LibrosForm();
  librosTableModel.refrescar();
 }
 
 
}

LibrosTableModel

public class LibrosTableModel implements TableModel {

 Hobbies hobbies;
 List<Libro> libros;
 private TableModelListener listener;
 
 public LibrosTableModel() {
  hobbies = new Hobbies();
  libros = hobbies.recuperarLibros();
 }
 
 @Override
 public void addTableModelListener(TableModelListener arg0) {
  listener = arg0;
 }

 @Override
 public Class<?> getColumnClass(int arg0) {
  switch (arg0) {
   case 0: return Long.class;
   case 1: return String.class;
   case 2: return Boolean.class;
   case 3: return Boolean.class;
   case 4: return String.class;
  }
  return null;
 }

 @Override
 public int getColumnCount() {
  return 5;
 }

 @Override
 public String getColumnName(int arg0) {
  switch (arg0) {
   case 0: return "ISBN";
   case 1: return "Título";
   case 2: return "Leído";
   case 3: return "Lo tengo";
   case 4: return "Autor";
  }
  return null;
 }

 @Override
 public int getRowCount() {
  return libros.size();
 }

 @Override
 public Object getValueAt(int arg0, int arg1) {
  Libro libro = libros.get(arg0);
  switch (arg1) {
   case 0: return libro.getIsbn();
   case 1: return libro.getTitulo();
   case 2: return libro.getLeido();
   case 3: return libro.getTengo();
   case 4: return libro.getAutor().getNombre();
  }
  return null;
 }

 @Override
 public boolean isCellEditable(int arg0, int arg1) {
  return false;
 }

 @Override
 public void removeTableModelListener(TableModelListener arg0) {
  // TODO Auto-generated method stub

 }

 @Override
 public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
  // TODO Auto-generated method stub

 }
 
 //Método que nos permite actualizar el listado de libros cuando hemos añadido alguno
 public void refrescar() {
  libros = hobbies.recuperarLibros();
  listener.tableChanged(new TableModelEvent(this));
 }

}


JOPTIONPANE


CUADRO DE DIÁLOGO "CREAR LIBRO"
Si en el formulario anterior hacemos clic sobre "Añadir libro", se abre un cuadro de diálogo en el que podremos indicar los diferentes datos del ejemplar que queramos. Además, si en el JComboBox, no está el autor que queremos, podemos hacer clic en "Nuevo autor" para añadirlo.

@SuppressWarnings("serial")
public class LibrosForm extends JDialog {

 private JButton guardarButton, cancelarButton, nuevoAutorButton;
 private JLabel isbnLabel, tituloLabel, autorLabel, sinopsisLabel, generoLabel;
 private JTextField isbnText, tituloText;
 private JCheckBox leidoCheck, tengoCheck;
 private JComboBox<String> autores;
 private JTextArea sinopsisTextArea;
 private JRadioButton terrorRadio, cienciaFiccionRadio, romanceRadio, fantasiaRadio;
 private ButtonGroup generoGroup;
 
 private Hobbies hobbies;
 private List<Autor> listado;
 
 public LibrosForm() {
  hobbies = new Hobbies();
  listado = hobbies.recuperarAutores();
  initForm();
  initComponents();
  setVisible(true);
 }

 private void initForm() {
  setTitle("Crear libro");
  setSize(480, 400);
  setLocationRelativeTo(null);
  setDefaultCloseOperation(DISPOSE_ON_CLOSE);
  setResizable(false);
  setModal(true);
 }

 private void initComponents() {
  guardarButton = new JButton("Guardar");
  guardarButton.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) { onGuardarButtonActionPerformed(e); }
  });
  cancelarButton = new JButton("Cancelar");
  cancelarButton.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) { onCancelarButtonActionPerformed(e); }
  });
  nuevoAutorButton = new JButton("Nuevo autor");
  nuevoAutorButton.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) { onNuevoAutorButtonActionPerformed(e); }
  });
  isbnLabel = new JLabel("ISBN:");
  tituloLabel = new JLabel("Título:");
  autorLabel = new JLabel("Autor:");
  sinopsisLabel = new JLabel("Sinopsis:");
  sinopsisLabel.setVerticalAlignment(JLabel.TOP);
  generoLabel = new JLabel("Género:");
  generoLabel.setVerticalAlignment(JLabel.TOP);
  isbnText = new JTextField();
  tituloText = new JTextField();
  sinopsisTextArea = new JTextArea();
  //Debemos indicar el número de filas que queremos que tenga el JTextArea
  sinopsisTextArea.setRows(3);
  leidoCheck = new JCheckBox("Leído");
  tengoCheck = new JCheckBox("Lo tengo");
  terrorRadio  = new JRadioButton("Terror");
  //El método setActionCommand(), nos permite saber a través del JButtonGroup, cuál es el elemento seleccionado
  terrorRadio.setActionCommand("Terror");
  cienciaFiccionRadio = new JRadioButton("Ciencia ficción");
  cienciaFiccionRadio.setActionCommand("Ciencia ficción");
  romanceRadio = new JRadioButton("Romance");
  romanceRadio.setActionCommand("Romance");
  fantasiaRadio = new JRadioButton("Fantasía");
  fantasiaRadio.setActionCommand("Fantasía");
  //El ButtonGroup realiza una agrupación lógica de los JRadioButton, que permite saber cuál es el seleccionado
  generoGroup = new ButtonGroup();
  generoGroup.add(terrorRadio);
  generoGroup.add(cienciaFiccionRadio);
  generoGroup.add(romanceRadio);
  generoGroup.add(fantasiaRadio);
  autores = new JComboBox<String>();
  
  for (Autor autor : listado) {
   autores.addItem(autor.getNombre());
  }
  
  JPanel botonesPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
  botonesPanel.add(guardarButton);
  botonesPanel.add(cancelarButton);
  
  //Ponemos el JTextArea dentro de un JScrollPane, para que podamos ver todas las filas si se escribe más de las indicadas
  JScrollPane sinopsisPanel = new JScrollPane(sinopsisTextArea);
  
  JPanel checkPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
  checkPanel.add(leidoCheck);
  checkPanel.add(tengoCheck);
  
  JPanel generoPanel = new JPanel(new GridLayout(2, 2));
  generoPanel.add(terrorRadio);
  generoPanel.add(cienciaFiccionRadio);
  generoPanel.add(romanceRadio);
  generoPanel.add(fantasiaRadio);
  
  JPanel datosPanel = new JPanel(new GridBagLayout());
  datosPanel.add(isbnLabel, new GridBagConstraints(0, 0, 1, 1, 0.25, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0));
  datosPanel.add(isbnText, new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0));
  datosPanel.add(tituloLabel, new GridBagConstraints(0, 1, 1, 1, 0.25, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0));
  datosPanel.add(tituloText, new GridBagConstraints(1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0));
  datosPanel.add(autorLabel, new GridBagConstraints(0, 2, 1, 1, 0.25, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0));
  datosPanel.add(autores, new GridBagConstraints(1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0));
  datosPanel.add(nuevoAutorButton, new GridBagConstraints(1, 3, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 0, 0));
  datosPanel.add(sinopsisLabel, new GridBagConstraints(0, 4, 1, 1, 0.25, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0));
  datosPanel.add(sinopsisPanel, new GridBagConstraints(1, 4, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0));
  datosPanel.add(generoLabel, new GridBagConstraints(0, 5, 1, 1, 0.25, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0));
  datosPanel.add(generoPanel, new GridBagConstraints(1, 5, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0));
  datosPanel.add(checkPanel, new GridBagConstraints(1, 6, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0));
  
  getContentPane().setLayout(new BorderLayout());
  getContentPane().add(datosPanel, BorderLayout.NORTH);
  getContentPane().add(botonesPanel, BorderLayout.SOUTH);
 }

 protected void onGuardarButtonActionPerformed(ActionEvent e) {
  Libro libro = new Libro();
  libro.setIsbn(Long.parseLong(isbnText.getText()));
  libro.setTitulo(tituloText.getText());
  libro.setLeido(leidoCheck.isSelected());
  libro.setTengo(tengoCheck.isSelected());
  libro.setAutor(listado.get(autores.getSelectedIndex()));
  libro.setGenero(generoGroup.getSelection().getActionCommand());
  libro.setSinopsis(sinopsisTextArea.getText());
  
  hobbies.insertarLibro(libro);
  dispose();
 }

 protected void onCancelarButtonActionPerformed(ActionEvent e) {
  dispose();
 }

 protected void onNuevoAutorButtonActionPerformed(ActionEvent e) {
  new AutorForm();
  
  //Para actualizar el listado de autores, eliminamos primero todos los elementos
  autores.removeAllItems();
  //Y los volvemos a añadir (así estará el que se acaba de añadir)
  listado = hobbies.recuperarAutores();
  for (Autor autor : listado) {
   autores.addItem(autor.getNombre());
  }
  //Para que aparezca seleccionado el autor que acabamos de añadir
  autores.setSelectedIndex(listado.size()-1);
 }
}


CUADRO DE DIÁLOGO "NUEVO AUTOR"

@SuppressWarnings("serial")
public class AutorForm extends JDialog {
 
 private JButton guardarButton, cancelarButton;
 private JLabel nombreLabel;
 private JTextField nombreText;

 public AutorForm() {
  initForm();
  initComponents();
  setVisible(true);
 }

 private void initForm() {
  setTitle("Nuevo autor");
  setSize(350, 100);
  setDefaultCloseOperation(DISPOSE_ON_CLOSE);
  setLocationRelativeTo(null);
  setResizable(false);
  setModal(true);
 }

 private void initComponents() {
  guardarButton = new JButton("Guardar");
  guardarButton.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) { onGuardarButtonActionPerformed(e); }
  });
  cancelarButton = new JButton("Cancelar");
  cancelarButton.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) { onCancelarButtonActionPerformed(e); }
  });
  nombreLabel = new JLabel("Nombre:");
  nombreText = new JTextField();
  
  JPanel botonesPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
  botonesPanel.add(guardarButton);
  botonesPanel.add(cancelarButton);
  
  JPanel datosPanel = new JPanel(new GridBagLayout());
  datosPanel.add(nombreLabel, new GridBagConstraints(0, 0, 1, 1, 0.1, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0));
  datosPanel.add(nombreText, new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0));
  
  getContentPane().setLayout(new BorderLayout());
  getContentPane().add(datosPanel, BorderLayout.NORTH);
  getContentPane().add(botonesPanel, BorderLayout.SOUTH);
 }

 protected void onGuardarButtonActionPerformed(ActionEvent e) {
  Hobbies hobbies = new Hobbies();
  Autor autor = new Autor();
  autor.setNombre(nombreText.getText());
  
  hobbies.insertarAutor(autor);
  dispose();
 }

 protected void onCancelarButtonActionPerformed(ActionEvent e) {
  dispose();
 }
}

martes, 22 de noviembre de 2011

Formularios en Java. Layout

En la entrada anterior puse un ejemplo del uso de varios componentes Swing que podemos usar en formularios. En ese ejemplo no utilicé ningún layout, por lo que para colocar los componentes donde queramos tenemos que hacerlo mediante coordenadas. Esto resulta bastante complicado, sobre todo a medida que aumenta la complejidad del formulario, por lo que se suele hacer es trabajar con layout.

Los layout indican la forma de organizar los componentes dentro de un contenedor. De manera que crearemos el contenedor, que puede ser un JPanel, JFrame, JDialog, etc., estableceremos el layout que más nos interese en cada caso y añadiremos los elementos

Algunos de los layout más importantes, de los que se muestra posteriormente un sencillo ejemplo de código y una imagen, son:
  • FlowLayout. Es el layout por defecto de los paneles (JPanel). En él los componentes se van añadiendo uno junto a otro en filas (de izquierda a derecha y de arriba a abajo). Se creará una nueva fila cuando sea necesario y se redistribuye el contenido al cambiar las dimensiones del contenedor.
  • BoxLayout. Organiza los componentes en una línea horizontal o vertical, sin dejar espacio entre ellos. Un ejemplo de su uso podría ser una barra de herramientas, donde hay varios botones uno junto a otro.
  • GridLayout. Este layout es similar a una tabla, ya que los elementos se distribuirán en sus celdas, que son de tamaño idéntico. Los elementos se añadirán a las celdas siguiendo el orden: de izquierda a derecha y de arriba a abajo.
  • GridBagLayout. Es el layout más complejo, pero también el más flexible. Coloca los componentes dentro de una rejilla, pero gracias al objeto "GridBagConstraints", el componente puede ocupar varias celdas, centrarse a uno u otro lado, que su columna sea más ancha, etc.
  • BorderLayout. Utiliza 5 áreas para colocar los componentes (norte, sur, este, oeste y centro), por lo que sólo se pueden añadir 5 componentes (si los componentes son paneles pueden tener a su vez varios componentes dentro).
Estos ejemplos sólo pretenden dar una noción de "cómo" son los distintos layout. Se han asignado los mismos a un JFrame y se les han añadido algunos componentes para hacerse una idea más fácil de cómo quedan. Normalmente en un formulario se utilizan distintos paneles (JPanel), cada uno con el layout más apropiado, y en ellos se distribuyen los componentes.

FLOWLAYOUT

@SuppressWarnings("serial")
public class EjemploFlowLayout extends JFrame {

 private JLabel unoLabel, dosLabel, tresLabel, cuatroLabel;
 
 public EjemploFlowLayout() {
  initForm();
  initComponents();
  setVisible(true);
 }

 private void initForm() {
  setTitle("Ejemplo de FlowLayout");
  setSize(400, 300);
  setLocationRelativeTo(null);
  setDefaultCloseOperation(DISPOSE_ON_CLOSE);
 }

 private void initComponents() {
  unoLabel = new JLabel("UNO");
  unoLabel.setOpaque(true);
  unoLabel.setBackground(Color.CYAN);
  dosLabel = new JLabel("DOS");
  dosLabel.setOpaque(true);
  dosLabel.setBackground(Color.RED);
  tresLabel = new JLabel("TRES");
  tresLabel.setOpaque(true);
  tresLabel.setBackground(Color.YELLOW);
  cuatroLabel = new JLabel("CUATRO");
  cuatroLabel.setOpaque(true);
  cuatroLabel.setBackground(Color.GREEN);
  
  //Añadimos el layout
  //Si en el constructor del FlowLayout no ponemos nada, los elementos se colocarán centrados
  getContentPane().setLayout(new FlowLayout(FlowLayout.RIGHT));
  getContentPane().add(unoLabel);
  getContentPane().add(dosLabel);
  getContentPane().add(tresLabel);
  getContentPane().add(cuatroLabel);
 }
}

En la imagen se puede apreciar la redistribución de los componentes:


BOXLAYOUT

@SuppressWarnings("serial")
public class EjemploBoxLayout extends JFrame {

 private JLabel unoLabel, dosLabel, tresLabel, cuatroLabel;
 
 public EjemploBoxLayout() {
  initForm();
  initComponents();
  setVisible(true);
 }

 private void initForm() {
  setTitle("Ejemplo de BoxLayout");
  setSize(400, 300);
  setLocationRelativeTo(null);
  setDefaultCloseOperation(DISPOSE_ON_CLOSE);
 }

 private void initComponents() {
  unoLabel = new JLabel("UNO");
  unoLabel.setOpaque(true);
  unoLabel.setBackground(Color.CYAN);
  dosLabel = new JLabel("DOS");
  dosLabel.setOpaque(true);
  dosLabel.setBackground(Color.RED);
  tresLabel = new JLabel("TRES");
  tresLabel.setOpaque(true);
  tresLabel.setBackground(Color.YELLOW);
  cuatroLabel = new JLabel("CUATRO");
  cuatroLabel.setOpaque(true);
  cuatroLabel.setBackground(Color.GREEN);
  
  //Añadimos el layout, en este caso los componentes seguirán el eje Y
  getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
  getContentPane().add(unoLabel);
  getContentPane().add(dosLabel);
  getContentPane().add(tresLabel);
  getContentPane().add(cuatroLabel);
 }
}


GRIDLAYOUT

@SuppressWarnings("serial")
public class EjemploGridLayout extends JFrame {

 private JLabel unoLabel, dosLabel, tresLabel, cuatroLabel;
 
 public EjemploGridLayout() {
  initForm();
  initComponents();
  setVisible(true);
 }

 private void initForm() {
  setTitle("Ejemplo de GridLayout");
  setSize(400, 300);
  setLocationRelativeTo(null);
  setDefaultCloseOperation(DISPOSE_ON_CLOSE);
 }

 private void initComponents() {
  unoLabel = new JLabel("UNO");
  unoLabel.setOpaque(true);
  unoLabel.setBackground(Color.CYAN);
  dosLabel = new JLabel("DOS");
  dosLabel.setOpaque(true);
  dosLabel.setBackground(Color.RED);
  tresLabel = new JLabel("TRES");
  tresLabel.setOpaque(true);
  tresLabel.setBackground(Color.YELLOW);
  cuatroLabel = new JLabel("CUATRO");
  cuatroLabel.setOpaque(true);
  cuatroLabel.setBackground(Color.GREEN);
  
  //Añadimos el layout
  //En el constructor he indicado el número de filas y columnas
  //Si el número de filas es 0, se irán creando a medida que se añaden elementos
  getContentPane().setLayout(new GridLayout(2, 2));
  getContentPane().add(unoLabel);
  getContentPane().add(dosLabel);
  getContentPane().add(tresLabel);
  getContentPane().add(cuatroLabel);
 }
}


GRIDBAGLAYOUT

@SuppressWarnings("serial")
public class EjemploGridBagLayout extends JFrame {

 private JLabel unoLabel, dosLabel, tresLabel;
 
 public EjemploGridBagLayout() {
  initForm();
  initComponents();
  setVisible(true);
 }

 private void initForm() {
  setTitle("Ejemplo de GridBagLayout");
  setSize(400, 300);
  setLocationRelativeTo(null);
  setDefaultCloseOperation(DISPOSE_ON_CLOSE);
 }

 private void initComponents() {
  unoLabel = new JLabel("UNO");
  unoLabel.setOpaque(true);
  unoLabel.setBackground(Color.CYAN);
  dosLabel = new JLabel("DOS");
  dosLabel.setOpaque(true);
  dosLabel.setBackground(Color.RED);
  tresLabel = new JLabel("TRES");
  tresLabel.setOpaque(true);
  tresLabel.setBackground(Color.YELLOW);
  
  //Creamos la constraint para el primer elemento
  GridBagConstraints constraint = new GridBagConstraints();
  //Coordenadas, celda en la que se colocará el componente "unoLabel"
  constraint.gridx = 0;
  constraint.gridy = 0;
  //Número de celdas que ocupa a lo ancho y a lo alto
  constraint.gridwidth = 1;
  constraint.gridheight = 1;
  //Peso. Cuando los componentes no ocupan todo el espacio, el espacio sobrante se reparte
  //Si lo ponemos a cero cada componente cogerá lo que necesite
  //En este caso se extiende a lo ancho
  constraint.weightx = 1.0;
  constraint.weighty = 0.0;
  //Anclaje. Posición en la que se coloca el componente dentro de la propia celda. Depende del fill
  constraint.anchor = GridBagConstraints.CENTER;
  //Cómo se coloca el componente dentro de la celda. Both (se estira en todo su espacio), Vertical, Horizontal
  constraint.fill = GridBagConstraints.BOTH;
  //Margen con los bordes de la celda
  constraint.insets = new Insets(5, 5, 5, 5);
  
  //Añadimos el layout
  getContentPane().setLayout(new GridBagLayout());
  getContentPane().add(unoLabel, constraint);
  //Con otro de los constructores de la clase GridBagConstraints, podemos instanciarla directamente
  //al añadir el elemento:
  getContentPane().add(dosLabel, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHEAST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 0, 0));
  //El tercer componente ocupa dos celdas y se extiende a lo alto
  getContentPane().add(tresLabel, new GridBagConstraints(0, 1, 2, 1, 0.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0));
 }
 
}


BORDERLAYOUT

@SuppressWarnings("serial")
public class EjemploBorderLayout extends JFrame {
 
 private JLabel norteLabel, surLabel, esteLabel, oesteLabel, centroLabel;
 
 public EjemploBorderLayout() {
  initForm();
  initComponents();
  setVisible(true);
 }

 private void initForm() {
  setTitle("Ejemplo de BorderLayout");
  setSize(400, 300);
  setLocationRelativeTo(null);
  setDefaultCloseOperation(DISPOSE_ON_CLOSE);
 }

 private void initComponents() {
  norteLabel = new JLabel("NORTE");
  norteLabel.setOpaque(true);
  norteLabel.setBackground(Color.CYAN);
  norteLabel.setHorizontalAlignment(JLabel.CENTER);
  surLabel = new JLabel("SUR");
  surLabel.setOpaque(true);
  surLabel.setBackground(Color.RED);
  surLabel.setHorizontalAlignment(JLabel.CENTER);
  esteLabel = new JLabel("ESTE");
  esteLabel.setOpaque(true);
  esteLabel.setBackground(Color.YELLOW);
  oesteLabel = new JLabel("OESTE");
  oesteLabel.setOpaque(true);
  oesteLabel.setBackground(Color.GREEN);
  centroLabel = new JLabel("CENTRO");
  centroLabel.setOpaque(true);
  centroLabel.setBackground(Color.WHITE);
  centroLabel.setHorizontalAlignment(JLabel.CENTER);
  centroLabel.setVerticalAlignment(JLabel.CENTER);
  
  //Este paso no sería necesario, ya que por defecto, los JFrame tienen este layout
  getContentPane().setLayout(new BorderLayout());
  //Añadimos los elementos indicando en que posición
  getContentPane().add(norteLabel, BorderLayout.NORTH);
  getContentPane().add(surLabel, BorderLayout.SOUTH);
  getContentPane().add(esteLabel, BorderLayout.EAST);
  getContentPane().add(oesteLabel, BorderLayout.WEST);
  //Lo que se añade en el centro ocupará el mayor espacio posible
  //Deja al resto sólo lo que necesitan.
  getContentPane().add(centroLabel, BorderLayout.CENTER);
 }

}

lunes, 21 de noviembre de 2011

Formularios en Java. Componentes (I)

En esta entrada os dejo un sencillo ejemplo del uso de varios componentes Swing que podemos añadir a nuestros formularios. Los componentes usados son:
  • JButton.
  • JLabel.
  • JTextField.
  • JPasswordField.
He usado los métodos más comunes de estos componentes, tienen mucho más, así que si quieres hacer algo en particular, simplemente hay que investigar un poco a ver si se puede con todas las opciones que ofrecen. Además, en este ejemplo no he trabajado con "Layouts" (hablaré brevemente de ellos y pondré ejemplos de varios en la próxima entrada), sino que los componentes se han colocado en el formulario mediante coordenadas.

@SuppressWarnings("serial")
public class ComponentesFormI extends JFrame {
 
 //Componentes del formulario
 private JButton cerrarButton, activarButton;
 private JLabel nombreLabel, passwordLabel;
 private JTextField nombreText;
 private JPasswordField password;
 
 public ComponentesFormI() {
  initForm();
  initComponents();
  setVisible(true);
 }

 private void initForm() {
  setTitle("Componentes de los formularios (I)");
  setSize(640, 480);
  setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
  addWindowListener(new WindowAdapter() {
   //Cuando se "cierre", se ejecutará el método onWindowClosing(e)
   public void windowClosing(WindowEvent e) { onWindowClosing(e); }
  });
  setLocationRelativeTo(null);
  //Para que no se pueda redimensionar
  setResizable(false);
 }

 private void initComponents() {
  cerrarButton = new JButton("Cerrar");
  //Indicamos las coordenadas y las dimensiones del componente
  //Cuando veamos los layouts no será necesario indicar las coordenadas
  cerrarButton.setBounds(500, 380, 100, 30);
  cerrarButton.setToolTipText("Al pulsarlo, se da la opción de cerrar el formulario.");
  //Especificamos la tecla que puede activar este botón: Alt + C
  cerrarButton.setMnemonic(KeyEvent.VK_C);
  cerrarButton.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) { onCerrarButtonActionPerformed(e); }
  });
  
  activarButton = new JButton("Activar");
  activarButton.setBounds(250, 135, 100, 30);
  activarButton.setToolTipText("Al pulsarlo, activa los cuadros de texto.");
  activarButton.setMnemonic(KeyEvent.VK_A);
  activarButton.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) { onActivarButtonActionPerformed(e); }
  });
  
  nombreLabel = new JLabel("Nombre:");
  nombreLabel.setBounds(50, 50, 150, 50);
  //Para que se vea el color de fondo
  nombreLabel.setOpaque(true);
  nombreLabel.setBackground(Color.MAGENTA);
  nombreLabel.setVerticalAlignment(JLabel.CENTER);
  nombreLabel.setHorizontalAlignment(JLabel.CENTER);
  
  passwordLabel = new JLabel("Contraseña:");
  passwordLabel.setBounds(50, 200, 150, 50);
  passwordLabel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
  passwordLabel.setVerticalAlignment(JLabel.TOP);
  passwordLabel.setHorizontalAlignment(JLabel.CENTER);
  
  nombreText = new JTextField();
  nombreText.setText("Pulsa el botón \"Activar\" para escribir tu nombre y contraseña.");
  nombreText.setBounds(250, 50, 350, 50);
  nombreText.setEditable(false);
  
  password = new JPasswordField();
  password.setBounds(250, 200, 350, 50);
  password.setEditable(false);
  
  //Los JFrame tienen por defecto el BorderLayout, se lo quitamos para añadir los componentes
  //y que estos se muestren en las coordenadas que hemos indicado
  getContentPane().setLayout(null);
  //Añadimos todos los componentes al formulario
  getContentPane().add(cerrarButton);
  getContentPane().add(activarButton);
  getContentPane().add(nombreLabel);
  getContentPane().add(passwordLabel);
  getContentPane().add(nombreText);
  getContentPane().add(password);
 }

 protected void onActivarButtonActionPerformed(ActionEvent e) {
  nombreText.setEditable(true);
  nombreText.requestFocus();
  nombreText.selectAll();
  password.setEditable(true);
 }

 protected void onCerrarButtonActionPerformed(ActionEvent e) {
  cerrar();
 }

 protected void onWindowClosing(WindowEvent e) {
  cerrar();
 }

 private void cerrar() {
  //Cuadro de diálogo para preguntar al usuario si desea salir
  int respuesta = JOptionPane.showConfirmDialog(this, "¿Está seguro de que quiere salir?", "Salir", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
  
  if (respuesta == JOptionPane.YES_OPTION) {
   dispose();
  }
 }

}

Y... ¡tachán! Así es como ha quedado el formulario:


Después de pulsar el botón "Activar":

domingo, 20 de noviembre de 2011

Formularios en Java

El tema de los formularios da para mucho. Intentaré resumirlo un poco mediante ejemplos de los distintos componentes que se pueden utilizar en los mismos y cómo podemos utilizarlos.

En esta entrada me centraré en algunas de las distintas opciones que tenemos para implementar la interfaz gráfica. Estas opciones las encontramos en el paquete javax.swing y son:
  • JWindow. Ventana sin barra de título ni botones para cerrar, maximizar, etc. Es la típica ventana que encontramos muchas veces al iniciar un programa, mientras este se está cargando.
  • JFrame. Ventana con barra de título y botones.
  • JDialog. Cuadro de diálogo, que sólo tiene el botón de cerrar.
A continuación dejo un ejemplo de código de cada una de ellas, junto con una captura de pantalla del resultado final y un ejemplo real del que podría ser su uso.

JWINDOW

@SuppressWarnings("serial")
public class EjemploJWindow extends JWindow {
 
 public EjemploJWindow() {
  initForm();
  initComponents();
  setVisible(true);
 }

 private void initForm() {
  setSize(400, 300);
  getContentPane().setBackground(Color.PINK);
  setLocationRelativeTo(null);
 }

 private void initComponents() {
  // Aquí se inicializarían los componentes de la ventana.
 }

}


Aunque parezca un rectángulo rosa hecho en el Paint, es el resultado de ejecutar el código anterior. Ahí va un ejemplo de lo que se podría hacer con JWindow, la pantalla de inicio de un programa:


JFRAME

@SuppressWarnings("serial")
public class EjemploJFrame extends JFrame {
 
 public EjemploJFrame() {
  initForm();
  initComponents();
  setVisible(true);
 }

 //Método en el que se inicializa el formulario
 private void initForm() {
  setTitle("Ejemplo de JFrame");
  setSize(400, 300);
  //Al indicar "null", el formulario aparecerá en el centro de la pantalla.
  setLocationRelativeTo(null);
  //Especificamos la operación que ocurrirá por defecto cuando se "cierre" el formulario.
  //En este caso, se cerrará
  setDefaultCloseOperation(DISPOSE_ON_CLOSE);
 }

 //Método en el que se inicializan los componentes del formulario
 private void initComponents() {
  // Aquí inicializaremos todos los componentes que tenga nuestro formulario.  
 }

}



JDIALOG

@SuppressWarnings("serial")
public class EjemploJDialog extends JDialog {

 public EjemploJDialog() {
  initForm();
  initComponents();
  setVisible(true);
 }

 private void initForm() {
  setTitle("Ejemplo de JDialog");
  setSize(400, 300);
  setLocationRelativeTo(null);
  setDefaultCloseOperation(DISPOSE_ON_CLOSE);
  //Esta opción hace que se bloque la ventana desde donde se invoca al cuadro de diálogo
  //Para que no esté disponible hasta que se haga algo en éste
  setModal(true);
 }

 private void initComponents() {
  //Aquí inicializaremos todos los componentes que tenga nuestro cuadro de diálogo.
 }
}


lunes, 14 de noviembre de 2011

Acceso a bases de datos SQL Server

Para acceder a bases de datos SQL Server, simplemente tenemos que instalar el correspondiente driver JDBC y modificar el fichero de configuración del acceso a bases de datos, por lo que resulta bastante más sencillo que en el caso de bases de datos Access (puente JDBC-ODBC).

El driver (sqljdbc4.jar) lo encontrarás en este enlace: Controlador JDBC de Microsoft SQL Server. Os recomiendo descargar el archivo .tar.gz, dentro de él está el driver sqljdbc4.jar.

Cuando te lo has descargado, simplemente debes incluirlo en todos los proyectos en los que vayas a acceder a una base de datos SQL Server. Lo puedes copiar directamente dentro del directorio del proyecto en Eclipse.


Una vez copiado, debes añadirlo a las librerías, para ello: clic con el botón derecho del ratón sobre el .jar, vamos a Build Path > Add to Build Path

Además, debes modificar el fichero de configuración, que quedará de la siguiente manera:

# Fichero de propiedades para el acceso a la base de datos

# Driver de la base de datos:
db.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver

# Url de conexión:
db.url=jdbc:sqlserver://localhost:1433;databaseName=faltometro

# Usuario y contraseña de SQL Server:
db.username=sa
db.password=1234

Nosotros hemos utilizado el inicio de sesión de SQL Server "sa", que es el usuario administrador del sistema.

PROBLEMAS

En clase surgieron varios problemas a la hora de conectar a la base de datos, ya que no reconocía el usuario que habíamos indicado. Ahí van los pasos que seguimos para solucionarlo:

** En SQL Server, activamos el usuario "sa" y le damos contraseña. Lo puedes hacer fácilmente ejecutando la siguiente consulta:

alter login sa enable;
go
alter login sa with password='1234';
go

** En las propiedades del servidor, debemos indicar que la autenticación sea tanto de Windows como de SQL Server (ventana Seguridad).

** También detuvimos el servicio SQL Express, en el Administrador de Configuración de SQL Server.

domingo, 6 de noviembre de 2011

Ejercicio "Efemérides" (base de datos)

Crear una aplicación que permita gestionar una base de datos de efemérides. Todas las operaciones se realizarán mediante consola, ya que el tema de formularios lo hemos empezado a ver después del de acceso a bases de datos.

Para empezar creamos en Access la base de datos "efemerides", que contendrá una única entidad: "efemeride", con los siguientes atributos:
  • id. De tipo entero.
  • texto. De tipo texto.
  • fecha. De tipo fecha.
Una vez creada la base de datos, implementamos el puente JDBC-ODBC y empezamos a trabajar.

Dentro de nuestro proyecto, crearemos la clase "BaseDatos" y el fichero de configuración, que como vimos en la entrada anterior nos facilitan todas las gestiones que realicemos con respecto al acceso a bases de datos.

Además, para que todo sea más realista y más sencillo de entender para el programador, crearemos la clase "Efemeride", que tendrá los atributos propios de una efeméride y, como métodos, los correspondientes "getters" y "setters".

CLASE EFEMERIDE

public class Efemeride {
 private Integer id;
 private String texto;
 private Date fecha;

 public Integer getId() {
  return id;
 }

 public void setId(Integer id) {
  this.id = id;
 }

 public String getTexto() {
  return texto;
 }

 public void setTexto(String texto) {
  this.texto = texto;
 }

 public Date getFecha() {
  return fecha;
 }

 public void setFecha(Date fecha) {
  this.fecha = fecha;
 }
}

TRUCO: una vez que has especificado todos los atributos de una clase, en Eclipse es muy sencillo generar los métodos getAtributo() y setAtributo(Dato dato). Sólo tienes que hacer clic con el botón derecho y en "Source" elegir "Generate Getters and Setters". En el cuadro de diálogo que se abre eliges los atributos de los que quieres generar estos métodos, haces clic en "OK" y ¡listo!

Para controlar las excepciones de nuestra aplicación, crearemos nuestra propia excepción. En nuestro caso esta clase fue facilitada por el profesor, así como una interfaz ("IEfemerides") que debe implementar la clase "Efemerides". Esta clase es la encargada de realizar todas las gestiones con la base de datos (insertar datos, actualizar, recuperar, etc.).

CLASE EFEMERIDEEXCEPTION

public class EfemerideException extends Exception {
 private static final long serialVersionUID = 1L;

 public EfemerideException(String mensaje) {
  super(mensaje);
 }
 
}

CLASE EFEMERIDES

public class Efemerides implements IEfemerides {

 @Override
 public void insertar(Efemeride efemeride) throws EfemerideException {
  try {
   Connection conexion = BaseDatos.getConnection();
   String sql = "insert into efemerides (texto, fecha) values (?, ?)";
   PreparedStatement insertar = conexion.prepareStatement(sql);

   insertar.setString(1, efemeride.getTexto());
   insertar.setDate(2, DateUtils.convertir(efemeride.getFecha()));

   insertar.executeUpdate();
   insertar.close();

  } catch (SQLException e) {
   e.printStackTrace();
   throw new EfemerideException("No se ha podido insertar la efeméride.");
  }
 }

 @Override
 public void eliminar(Integer id) throws EfemerideException {
  try {
   int eliminado;
   Connection conexion = BaseDatos.getConnection();
   String sql = "delete from efemerides where id=?";
   PreparedStatement eliminar = conexion.prepareStatement(sql);

   eliminar.setInt(1, id);

   eliminado = eliminar.executeUpdate();
   if (eliminado == 0) {
    throw new EfemerideException("La efeméride no existe.");
   }
   eliminar.close();

  } catch (SQLException e) {
   e.printStackTrace();
   throw new EfemerideException("No se ha podido eliminar la efeméride.");
  }
 }

 @Override
 public void actualizar(Efemeride efemeride) throws EfemerideException {
  try {
   int actualizado;
   Connection conexion = BaseDatos.getConnection();
   String sql = "update efemerides set texto=?, fecha=? where id=?";
   PreparedStatement actualizar = conexion.prepareStatement(sql);

   actualizar.setString(1, efemeride.getTexto());
   actualizar.setDate(2, DateUtils.convertir(efemeride.getFecha()));
   actualizar.setInt(3, efemeride.getId());

   actualizado=actualizar.executeUpdate();
   if (actualizado == 0) {
    throw new EfemerideException("La efeméride no existe, por lo que no se ha podido actualizar.");
   }
   actualizar.close();

  } catch (SQLException e) {
   e.printStackTrace();
   throw new EfemerideException("No se ha podido actualizar la efeméride.");
  }

 }

 @Override
 public Efemeride recuperar(Integer id) throws EfemerideException {
  Efemeride efemeride = new Efemeride();
  try {
   Connection conexion = BaseDatos.getConnection();
   String sql = "select * from efemerides where id=?";
   PreparedStatement recuperar = conexion.prepareStatement(sql);

   recuperar.setInt(1, id);

   ResultSet resultado = recuperar.executeQuery();
   if (!resultado.next()) {
    throw new EfemerideException("La efeméride no se ha recuperado porque no existe.");
   } else {
    efemeride.setId(id);
    efemeride.setTexto(resultado.getString("texto"));
    efemeride.setFecha(resultado.getDate("fecha"));
   }
  } catch (SQLException e) {
   e.printStackTrace();
   throw new EfemerideException("No se ha podido recuperar la efeméride.");
  }
  return efemeride;
 }

 @Override
 public List<Efemeride> hoy() throws EfemerideException {
  Date hoy = new Date();

  return recuperarPorFecha(hoy);
 }

 @Override
 public List<Efemeride> recuperarTodas() throws EfemerideException {
  List<Efemeride> listado = new ArrayList<Efemeride>();
  try {
   Connection conexion = BaseDatos.getConnection();
   Statement recuperar = conexion.createStatement();

   ResultSet resultado;
   resultado = recuperar.executeQuery("select * from efemerides");

   while (resultado.next()) {
    Efemeride efemeride = new Efemeride();
    efemeride.setId(resultado.getInt("id"));
    efemeride.setTexto(resultado.getString("texto"));
    efemeride.setFecha(resultado.getDate("fecha"));
    listado.add(efemeride);
   }
  } catch (SQLException e) {
   e.printStackTrace();
   throw new EfemerideException("No se han podido recuperar las efemérides.");
  }
  return listado;
 }

 @Override
 public List<Efemeride> recuperarPorFecha(Date fecha)
   throws EfemerideException {
  List<Efemeride> listado = new ArrayList<Efemeride>();
  try {
   Connection conexion = BaseDatos.getConnection();
   String sql = "select * from efemerides where day(fecha)=? and month(fecha)=?";
   PreparedStatement recuperar = conexion.prepareStatement(sql);

   recuperar.setInt(1, DateUtils.dia(fecha));
   recuperar.setInt(2, DateUtils.mes(fecha));

   ResultSet resultado = recuperar.executeQuery();
   while (resultado.next()) {
    Efemeride efemeride = new Efemeride();
    efemeride.setId(resultado.getInt("id"));
    efemeride.setTexto(resultado.getString("texto"));
    efemeride.setFecha(resultado.getDate("fecha"));
    listado.add(efemeride);
   }
  } catch (SQLException e) {
   e.printStackTrace();
   throw new EfemerideException("No se han podido recuperar las efemérides.");
  }
  return listado;
 }

}

Finalmente, la aplicación con la que interactuará el usuario se creó utilizando varias clases proporcionadas por el profesor, que permitían crear menús y recoger los datos introducidos por el usuario.

CLASE MENUEFEMERIFE

public class MenuEfemeride {

 private static Efemerides efemerides = new Efemerides();
 
 public static void main(String[] args) {

  Menu principal = new Menu("Menú principal. Efemérides");
  Menu consultar = new Menu("Consultar");
  
  Accion insertarEfemeride = new Accion("Insertar efeméride") {
   public void accion() { onInsertarEfemeride(); }
  };
  Accion eliminarEfemeride = new Accion("Eliminar efeméride") {
   public void accion() { onEliminarEfemeride(); }
  };
  Accion actualizarEfemeride = new Accion("Actualizar efeméride") {
   public void accion() { onActualizarEfemeride(); }
  };
  Accion buscarPorId = new Accion("Buscar efeméride por identificador") {
   public void accion() { onBuscarPorId(); }
  };
  Accion buscarPorFecha = new Accion("Buscar efeméride por fecha") {
   public void accion() { onBuscarPorFecha(); }
  };
  Accion efemeridesHoy = new Accion("Efemérides de hoy") {
   public void accion() { onEfemeridesHoy(); }
  };
  Accion todas = new Accion("Todas las efemérides") {
   public void accion() { onTodas(); }
  };
  
  consultar.agregar(buscarPorId);
  consultar.agregar(buscarPorFecha);
  consultar.agregar(efemeridesHoy);
  consultar.agregar(todas);
  consultar.agregar(new OpcionSalir("Volver"));
  
  principal.agregar(insertarEfemeride);
  principal.agregar(eliminarEfemeride);
  principal.agregar(actualizarEfemeride);
  principal.agregar(consultar);
  principal.agregar(new OpcionSalir("Salir"));
  
  principal.ejecutar();
  
  BaseDatos.desconectar();

 }

 protected static void onInsertarEfemeride() {

  Efemeride efemeride = new Efemeride();
  
  String texto = Entrada.leerCadena("Introduzca el texto de la efeméride");
  String fechaTexto = Entrada.leerCadena("Introduzca la fecha (dd/mm/aaaa)");
  String[] partes = fechaTexto.split("/");
  
  GregorianCalendar fecha = new GregorianCalendar(Integer.parseInt(partes[2]), (Integer.parseInt(partes[1])-1), Integer.parseInt(partes[0]));

  efemeride.setTexto(texto);
  efemeride.setFecha(fecha.getTime());
  
  try {
   efemerides.insertar(efemeride);
   System.out.println("**EFEMÉRIDE INSERTADA CON ÉXITO**");
   Entrada.pausa();
  } catch (EfemerideException e) {
   System.err.println(e.getMessage());
   Entrada.pausa();
  }
  
 }
 
 protected static void onEliminarEfemeride() {

  Integer id = Entrada.leerEntero("Introduzca el identificador de la efeméride a eliminar");
  
  try {
   efemerides.eliminar(id);
   System.out.println("**EFEMÉRIDE ELIMINADA**");
   Entrada.pausa();
  } catch (EfemerideException e) {
   System.err.println(e.getMessage());
   Entrada.pausa();
  }
  
 }

 protected static void onActualizarEfemeride() {

  Efemeride efemeride = new Efemeride();
  Integer id = Entrada.leerEntero("Introduzca el identificador de la efeméride a actualizar");
  try {
   Efemeride recuperada = efemerides.recuperar(id);
   System.out.println("Datos de la efeméride a actualizar:");
   System.out.println("Id: "+id+" - Texto: "+recuperada.getTexto()+" - Fecha: "+DateUtils.formatear(recuperada.getFecha()));
   
   String texto = Entrada.leerCadena("Introduzca el nuevo texto de la efeméride");
   String fechaTexto = Entrada.leerCadena("Introduzca la nueva fecha (dd/mm/aaaa)");
   
   String[] partes = fechaTexto.split("/");
   GregorianCalendar fecha = new GregorianCalendar(Integer.parseInt(partes[2]), (Integer.parseInt(partes[1])-1), Integer.parseInt(partes[0]));

   efemeride.setId(id);
   efemeride.setTexto(texto);
   efemeride.setFecha(fecha.getTime());
   
   efemerides.actualizar(efemeride);
   System.out.println("**EFEMÉRIDE ACTUALIZADA CON ÉXITO**");
   Entrada.pausa();
  } catch (EfemerideException e) {
   System.err.println(e.getMessage());
   Entrada.pausa();
  }
 }
 
 protected static void onBuscarPorId() {

  Integer id = Entrada.leerEntero("Introduzca el identificador de la efeméride a buscar");
  try {
   Efemeride efemeride = efemerides.recuperar(id);
   System.out.println("DATOS DE LA EFEMÉRIDE:");
   System.out.println("Id: "+id+" - Texto: "+efemeride.getTexto()+" - Fecha: "+DateUtils.formatear(efemeride.getFecha()));
   Entrada.pausa();
  } catch (EfemerideException e) {
   System.err.println(e.getMessage());
   Entrada.pausa();
  }
 }
 
 protected static void onBuscarPorFecha() {

  String fechaTexto = Entrada.leerCadena("Introduzca la fecha (dd/mm) de las efemérides a buscar");
  String[] partes = fechaTexto.split("/");
  
  GregorianCalendar fecha = new GregorianCalendar(1999, (Integer.parseInt(partes[1])-1), Integer.parseInt(partes[0]));
  
  try {
   List <Efemeride> listado = efemerides.recuperarPorFecha(fecha.getTime());
   if (listado.isEmpty()) {
    System.out.println("**NO HAY EFEMÉRIDES PARA EL DÍA ESPECIFICADO**");
   }
   else {
    System.out.println("LISTADO DE LAS EFEMÉRIDES:");
    for (Efemeride efemeride : listado) {
     System.out.println("Id: "+efemeride.getId()+" - Texto: "+efemeride.getTexto()+" - Fecha: "+DateUtils.formatear(efemeride.getFecha()));
    }
   }
   Entrada.pausa();
   
  } catch (EfemerideException e) {
   System.err.println(e.getMessage());
   Entrada.pausa();
  }
  
 }
 
 protected static void onEfemeridesHoy() {
  
  try {
   List <Efemeride> listado = efemerides.hoy();
   if (listado.isEmpty()) {
    System.out.println("**NO HAY EFEMÉRIDES PARA EL DÍA DE HOY**");
   }
   else {
    System.out.println("LISTADO DE LAS EFEMÉRIDES DE HOY:");
    for (Efemeride efemeride : listado) {
     System.out.println("Id: "+efemeride.getId()+" - Texto: "+DateUtils.formatear(efemeride.getFecha()));
    }
   }
   Entrada.pausa();
  } catch (EfemerideException e) {
   System.err.println(e.getMessage());
   Entrada.pausa();
  }
 }
 
 protected static void onTodas() {
  try {
   List <Efemeride> listado = efemerides.recuperarTodas();
   if (listado.isEmpty()) {
    System.out.println("**NO HAY EFEMÉRIDES ALMACENADAS**");
   }
   else {
    System.out.println("LISTADO DE TODAS LAS EFEMÉRIDES:");
    for (Efemeride efemeride : listado) {
     System.out.println("Id: "+efemeride.getId()+" - Texto: "+efemeride.getTexto()+" - Fecha: "+DateUtils.formatear(efemeride.getFecha()));
    }
   }
   Entrada.pausa();
  } catch (EfemerideException e) {
   System.err.println(e.getMessage());
   Entrada.pausa();
  }  
 }

}

También se creó una clase con utilidades para trabajar con las fechas, debido a que nos veíamos en la obligación de utilizar por una parte la clase java.util.Date y por otra java.sql.Date. Esta clase, como la clase "BaseDatos", se puede reutilizar cada vez que tengamos que trabajar con fechas en el acceso a bases de datos.

CLASE DATEUTILS

public class DateUtils {
 
 public static java.util.Date crearFecha(int dia, int mes, int anyo) {
  GregorianCalendar calendario = new GregorianCalendar(anyo, mes-1, dia);
  return calendario.getTime();
 }
 
 public static java.sql.Date convertir(java.util.Date fecha) {
  return new java.sql.Date(fecha.getTime());
 }
 
 public static int dia(java.util.Date fecha) {
  GregorianCalendar calendario = new GregorianCalendar();
  calendario.setTime(fecha);
  
  return calendario.get(Calendar.DAY_OF_MONTH);
 }
 
 public static int mes(java.util.Date fecha) {
  GregorianCalendar calendario = new GregorianCalendar();
  calendario.setTime(fecha);
  
  return calendario.get(Calendar.MONTH)+1;
 }
 
 public static int anyo(java.util.Date fecha) {
  GregorianCalendar calendario = new GregorianCalendar();
  calendario.setTime(fecha);
  
  return calendario.get(Calendar.YEAR);
 }
 
 public static String formatear(java.util.Date fecha) {
  SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");
  return formatter.format(fecha);
 }

}