Swing Beans Binding JSR 295

Siempre me había llamado la atención poder enlazar componentes de swing con algún objeto o lista de objetos de manera similar a como se realiza con JSF. Este no es un tema nuevo ya que se puede realizar desde ya hace bastante pero la mayoria de personas (al menos las que programan aplicaciones de escritorio en java) lo desconocen.

En esta ocasión realizare un ejemplo basandome en la entrada anterior sobre Spring 3.0 e Hibernate, en el cual ya he generado el acceso a datos.

El ejemplo que desarrollare sera un pequeño CRUD e utilizare las herramientas Netbeans 7.0, Spring 3.0 e Hibernate 3.6.7

Mi clase de acceso a datos es la siguiente:

package org.dani.ejemplo.jpa.dao;
import org.springframework.orm.jpa.support.JpaDaoSupport;
import java.util.*;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
 *
 * @author dherrera
 */
public class DerbyDAOImpl extends JpaDaoSupport implements DerbyDAO{

    @Transactional(readOnly=true,propagation=Propagation.NOT_SUPPORTED)
    public List getAllProducts() {
        return getJpaTemplate().find("Select p From Product p order by p.productId desc");
    }

    @Transactional(readOnly=true,propagation=Propagation.NOT_SUPPORTED)
    public List getAllManufacturer() {
        return getJpaTemplate().find("Select m From Manufacturer m");
    }

    @Transactional(readOnly=true,propagation=Propagation.NOT_SUPPORTED)
    public List getAllProductCode() {
        return getJpaTemplate().find("Select p From ProductCode p");
    }

    @Transactional(propagation=Propagation.REQUIRES_NEW,readOnly=false)
    public Product updateProduct(Product p) {
        return getJpaTemplate().merge(p);
    }

    public Integer getNewProductId() {
        javax.persistence.EntityManager em=getJpaTemplate().getEntityManagerFactory().createEntityManager();
        javax.persistence.Query q=em.createQuery("select max(p.productId)+1 from Product p", Integer.class);
        return (Integer) q.getSingleResult();
    }

    @Transactional(propagation=Propagation.REQUIRES_NEW,readOnly=false)
    public Product addProduct(Product p) {
        return updateProduct(p);
    }

    @Transactional(propagation=Propagation.REQUIRES_NEW,readOnly=false)
    public void deleteProduct(final Product p) {
        getJpaTemplate().remove(getJpaTemplate().getReference(Product.class, p.getProductId()));
    }
}


Obtengo las listas de tipo observable de productos y listas simples para manufacteros y códigos de producto con sus respectivos getters y setters para que los componentes dentro del formulario principal FrameBinding.java puedan accesar a ellas en el contructor del formulario.

public FrameBinding() {
        ctx = new ClassPathXmlApplicationContext("/org/dani/ejemplo/jpa/applicationContext.xml");
        dao = (DerbyDAO) ctx.getBean("dao");
        fillModelTable();
        manufacturers=dao.getAllManufacturer();
        productCodes=dao.getAllProductCode();
        initComponents();
        actions();
    }

    private void fillModelTable() {
        if (products!=null) {
            products.clear();
            products.addAll(dao.getAllProducts());
        }
        else products=ObservableCollections.observableList(dao.getAllProducts());
    }

Antes que nada es necesario modificar el entity Product.java añadiendole lo siguiente:

@Transient
    private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        changeSupport.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        changeSupport.removePropertyChangeListener(listener);
    }


Y para cada setter de las propiedades agregar el FirePropertyChange, esto es para efectos de poder utilizar una lista observable para que la tabla siempre pueda saber cuando cambia una propiedad de la lista de productos que utilizamos.

public void setManufacturer(Manufacturer manufacturer) {
        Manufacturer old=this.manufacturer;
        this.manufacturer = manufacturer;
        changeSupport.firePropertyChange("manufacturer", old, manufacturer);
    }


Por ultimo tambien creo una propiedad transient para efectos de utilizarla en un JComboBox como se explicara mas adelante.

public boolean getBooleanAvailable() {
        return available!=null && !available.isEmpty() && available.equals("TRUE");
    }

    
    public void setBooleanAvailable(boolean res) {
        String old=this.available;
        available=res?"TRUE":"FALSE";
        changeSupport.firePropertyChange("booleanAvailable", old, available);
    }


Todo esto es necesario hacerlo ya que en una clase Entity no se puede utilizar la anotación @Bindable antes del nombre de la clase y esta realiza automaticamente lo anterior expuesto y así poder utilizar el pojo en una ObservableList.

El diseño del formulario seria el siguiente:



Luego en vista de diseño del formulario doy clic derecho a la tabla, selecciono propiedades y elijo la sección  "binding" edito la propiedad "elements" y lo lleno con la lista de productos.


Luego realizo de la misma manera el binding de la propiedad "selected" en propiedades>binding de la caja de texto, apunto hacia la propiedad virtual "selected" de la tabla que es de tipo Product y luego selecciono "description"



Luego hago lo mismo con el checkbox solamente que ahora en lugar de apuntar a la propiedad "description" del elemento seleccionado de la tabla lo haré a la propiedad "booleanAvailable".



Luego para los combos se hace similar que una tabla seleccionando la lista que le corresponde a cada jcombobox, por ejemplo para el combo de Manufacturer se selecciona la lista de manufacturers que se encuentra en el formulario para su propiedad elements.



Y para elegir el elemento seleccionado por defecto apuntamos la propiedad virtual "selectedItem" hacia "selected" de la tabla.



Se realiza el mismo procedimiento para el combo de códigos de producto solamente con la variante que se escoge la lista de product_code del formulario.

Se puede jugar con otras propiedades, así como por ejemplo para los botones hice el enlace para la propiedad enable, para que se valide que los botones de Actualizar y borrar solamente esten activos cuando se haya seleccionado un item de la tabla.



Por ultimo agrego el código de CRUD habitual en los demás botones.

private void btnUpdateActionPerformed(java.awt.event.ActionEvent evt) {                                          
        dao.updateProduct(selected);
        fillModelTable();
        javax.swing.JOptionPane.showMessageDialog(this, "Actualizado");
    }                                         

    private void btnNuevoActionPerformed(java.awt.event.ActionEvent evt) {                                         
        Integer id=dao.getNewProductId();
        Product nuevo=new Product();
        nuevo.setProductId(id);
        nuevo.setDescription(txtDescripcion.getText());
        nuevo.setManufacturer((Manufacturer) comboManufacturer.getSelectedItem());
        nuevo.setProductCode((ProductCode) comboProductCode.getSelectedItem());
        nuevo.setBooleanAvailable(checkAvailable.isSelected());
        selected=dao.addProduct(nuevo);
        fillModelTable();
    }                                        

    private void btnDeleteActionPerformed(java.awt.event.ActionEvent evt) {                                          
        try {
            dao.deleteProduct(selected);
            fillModelTable();
        }
        catch (DataIntegrityViolationException e) {
            javax.swing.JOptionPane.showMessageDialog(this, "El producto tiene orden de compra (tabla PURCHASE_ORDER)");
        }
    }

Aca el ejemplo en tiempo de ejecución.



Adjunto el ejemplo para efectos de estudio.

Comentarios (0)

0 Response to "Swing Beans Binding JSR 295"