viernes, 7 de septiembre de 2007

DBF 2 Java Library

He creado una biblioteca que maneja archivos DBF desde Java.
Las funciones básicas que se puede hacer son:
  • Abrir (claro está) archivos DBF, ya sea de Fox 2.x ó 3.
  • Agrega registro en blanco
  • Lee y escribe datos en los registros.
  • Obtiene un registro
  • Navega de registro en registro
  • Obtiene los tipos de cada campo
Se puede obtener la biblioteca aquí

http://dbf2java-library.googlecode.com/

También se dispone del código fuente para descargar usando subversion.

Ah! y está bajo licencia GNU General Public License v3 para quien quiera que desee colaborar, bienvenido sea

jueves, 6 de septiembre de 2007

El problema del AUTO_INCREMENT con el API de Persistencia

Al crear una clase entidad utilizando el API de persistencia (JPA) con Netbeans, el IDE creará por omisión los ID con las siguientes anotaciones
    @Id
@GeneratedValue(strategy = GenerationType.AUTO)


Con ello creará una tabla llamada SEQUENCE donde almacenará el valor del último ID utilizado. Es lo más estándar posible, ya que sabemos que existen RDBMS que no tienen la capacidad de generar un ID autoincrementado (como el Firebird, que necesita de un generator).

Pero ¿si uso MySQL, Apache Derby (o un RDBMS que pueda permitir valores de ID autoincrementales)? Pues, si revisamos la documentación de Java

http://java.sun.com/javaee/5/docs/api/javax/persistence/GenerationType.html#SEQUENCE

dice textualmente " Indicates that the persistence provider must assign primary keys for the entity using database sequence column."

entonces, la anotación que necesitamos es
    @GeneratedValue(strategy = GenerationType.SEQUENCE)


Para salir de las dudas, al ejecutar nuestra aplicación, veremos que en la carpeta del proyecto (Ctrl + 2 en Netbeans) veremos archivos de extensión .sql: createDDL.sql y dropDDL.sql. Abrimos el primero y veremos que hay un sql como este:

CREATE TABLE PERSONA (ID BIGINT AUTO_INCREMENT NOT NULL, NOMBRE VARCHAR(255), EDAD INTEGER, PRIMARY KEY (ID))

... si hemos usado MySQL, mientras que con Apache Derby, se mostraría así:
CREATE TABLE PERSONA (ID BIGINT GENERATED ALWAYS AS IDENTITY NOT NULL, NOMBRE VARCHAR(255), EDAD INTEGER, PRIMARY KEY (ID))

martes, 4 de septiembre de 2007

Persistencia de Java: Clave Primaria compleja con objeto

Como se vió, con el API de Persistencia de Java (Java Persistence API - JPA) se puede mapear todas las tablas de una base de datos como entidades para ser manipuladas desde objetos java.

Ahora, ¿qué pasa con entidades débiles?
Una entidad débil es aquella que depende su existencia de otra entidad. Por ejemplo, el detalle de una factura no puede existir sin una factura. Si se traslada este concepto a una base de datos, entonces, la clave primaria del detalle de factura será: la clave primaria de la factura a quien pertenece, y el número de orden que se muestra en la lista.

Mapear la entidad FACTURA es simple:
@Entity
public class Factura implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="ID_FACTURA")
private Long id;


La entidad DETALLE_FACTURA tiene la clave primaria compuesta por dos campos: ID_FACTURA y NRO_ORDEN. En java, toda la clave primara es un objeto con el mismo nombre de la clase primaria más el sufijo "PK". Pero además, esta clave primaria debe tener una referencia a la misma factura de quien pertenece. Entonces, debemos crear la entidad primaria así:
@Embeddable
public class FacturaDetallePK implements java.io.Serializable {
@Column(name="id_factura",nullable=false)
private int idFactura;
@Column(name="nro_orden",nullable=false)
private int nroOrden;


Como se ve, solo va el atributo que se enlazará con la tabla, que debe ser del mismo tipo (a menos que especifique lo contrario).

Ahora, al mapear la entidad FACTURA_DETALLE, esta debería tener la clave primaria (PK) además, del objeto que hará referencia con su 'padre', es decir, con FACTURA:
@Entity
@Table(name="FACTURA_DETALLE")
public class FacturaDetalle implements java.io.Serializable {
@EmbeddedId
private FacturaDetallePK facturaDetallePK;

@JoinColumn(name="ID_FACTURA",referencedColumnName="ID_FACTURA")
@ManyToOne
private Factura factura;


Se podrá pensar "pero, no se estará haciendo redundancia?, porque ya en el PK ya está el objeto Factura. tengo que volverlo a declarar como atributo de FacturaDetalle". Pues sí, de otra manera el API no podrá saber cómo hacer referencia desde la entidad FACTURA_DETALLE con FACTURA.

"¿No se haría más pesado tener que programar que cada vez que agrego una detalle a la factura colocar en ambos atributos el valor de su cabecera?"
Bueno, sí, pero utilizando una buena implementación del patrón DAO, este se puede hacer cargo de él. Además, debería de hacer persistente a cada línea de detalle. Aquí un ejemplo:
        emf = Persistence.createEntityManagerFactory("facturaPU");
Factura factura=new Factura();
FacturaDetalle det1=new FacturaDetalle();
det1.setDescripcion("PCs");
FacturaDetalle det2=new FacturaDetalle();
det2.setDescripcion("Monitores");
persist(factura);
factura.agregarDetalle(det1);
persist(det1);
factura.agregarDetalle(det2);
persist(det2);


Y el método agregarDetalle() sería así:
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "factura")
private Collection<FacturaDetalle> facturaDetalleCollection=new ArrayList<FacturaDetalle>();

void agregarDetalle(FacturaDetalle det) {

this.facturaDetalleCollection.add(det);
det.setFacturaDetallePK(new FacturaDetallePK(this.facturaDetalleCollection.size(),this.idFactura));
}