Vamos a seguir desarrollando el proyecto Primefaces dentro de Learning Project que se explicó en el capítulo anterior, sino lo has visto todavía no te preocupes, está publicación empezamos de cero haciendo referencia a todo lo aprendido anteriormente solo que este caso vamos a hacer el desarrollo para todas las tablas.
En la primera publicación Tutorial Primefaces (1): Definición del proyecto y creación de Primefaces CRUD vimos con detalle y con imágenes la definición del proyecto, la creación de la base de datos MySQL (MariaDB), la preparación del entorno, la creación de entidades automática (Entity classes from database) y la creación de las páginas CRUD con el plugin para Primefaces.
¿Qué vamos a ver aquí?
Vamos a hacer un resumen general rápido para pasar a la acción, veremos como crear el proyecto basándonos en la publicación anterior, ahora lo haremos para implementar el proyecto Learning Project, y además, hacer nuestro proyecto multiidioma, actualizar la presentación y cambiar el tema usado por defecto, estos son los apartados:
- CREACIÓN DEL PROYECTO Y PUESTA EN MARCHA (2.1)
- CREAMOS LAS ENTIDADES PARA NUESTRO PROYECTO (2.1)
- ANALIZAMOS EL CÓDIGO CREADO (2.1)
- DEFINIENDO MANUALMENTE LA CONEXIÓN (GLASSFISH) (2.1)
- CREACIÓN DE LAS PÁGINAS CRUD CON PRIMEFACES (2.2) [Ya disponible]
- EL RESULTADO (2.2) [Ya disponible]
- AÑADIMOS SOPORTE MULTIIDIOMA (2.2) [Ya disponible]
- CAMBIAMOS EL TEMA (2.2) [Ya disponible]
- Actualizamos nuestra propia presentación (2.3) [Próximamente]
(Nota: los apartados indicados con (2.2) se explicarán en la siguiente publicación: Creación de las páginas CRUD Primefaces del proyecto – Primefaces (2.2))
Entity classes from database
Usamos la herramienta de Entity classes from database para generar las clases de persistencia JPA para nuestro proyecto JSF Primefaces CRUD.Xules
1. CREACIÓN DEL PROYECTO Y PUESTA EN MARCHA
La exposición aquí será sin detalles estos los puedes encontrar el la primera publicación Tutorial Primefaces (1) , según sea necesario iré haciendo referencia a los apartados para quesea fácil acceder a más aclaraciones si lo necesitas.
Lo que necesitas para empezar: instalación y creación de la base de datos
Información sobre como instalar MariaDB y como se crea la base de datos sobre MariaDB (MySQL), la puedes obtener aquí en el primer capítulo del tutorial de MariaDB.
En este proyecto usamos la base de datos MySQL usando la implementación de MariaDB, estos son los scripts que necesitarás para crear el esquema de customerdb definido en Learning Project y que desarrollaremos en esta publicación:
MariaDB SQL - Creación de las tablas
Y también para la población de datos de customerdb:
MariaDB SQL Script para la población de la base de datos Customerdb
Creamos el proyecto en Netbeans
Empezamos creando el proyecto (Creación del proyecto y configuración):
- Netbeans vamos a File > New Project (Archivo > Nuevo Proyecto)
- Creamos un nuevo proyecto Java Web > Web application
- Ubicamos nuestro proyecto y le damos nombre: CustomerdbPrimefacesProject
- Seleccionamos el servidor de aplicaciones GlassFish con Java EE 7 y mantenemos la ruta
- Seleccionaremos JSF y la librería Primefaces
- Listo ya tenemos el proyecto creado con Primefaces
2. CREAMOS LAS ENTIDADES PARA NUESTRO PROYECTO
Nuestro proyecto está compuesto por 8 tablas como irás viendo en la creación de las 8 entidades que vamos a hacer a continuación, este es el esquema que tienes implementada en la base de datos que te facilito para la descarga:
Los detalles de que consiste el proyecto se explicaron con detalle en la publicación anterior en Introducción al proyecto donde se especifica la funcionalidad de las tablas sus relaciones y su desarrollo dentro del proyecto Learning Project.
Ahora, nos colocamos sobre nuestro proyecto y abrimos el menú de opciones seleccionando New > Entity Classes from Database, en primer lugar te va a pedir que crees un nuevo Data Source, es decir, una fuente de datos, simplemente conecta la base de datos y establece un nombre JNDI, aquí lo puedes ver con más detalle, CustomerdbDataSource es el que yo le he asignado en esta ocasión, una vez aceptado se cargarán las tablas de la base de datos, selecciona todas como se muestra en la imagen:
A continuación, definimos el nombre del paquete donde queremos crear las entidades en mi caso selecciono org.xulescode.jpa, marcamos las opciones de la imagen:
Por último, seleccionamos las opciones de mapeo, una vez finalizado este proceso, se creará también la unidad de persistencia que utilizaremos en el proyecto:
El fichero de persistencia persistence.xml simplemente indica la fuente de datos que utilizamos:
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"> <persistence-unit name="CustomerdbPrimefacesProjectPU" transaction-type="JTA"> <jta-data-source>CustomerdbDataSource</jta-data-source> <exclude-unlisted-classes>false</exclude-unlisted-classes> <properties/> </persistence-unit> </persistence>
3. ANALIZAMOS EL CÓDIGO CREADO
Aunque este tipo de herramientas nos permiten agilizar la realización de nuestros proyectos es muy importante comprender lo que estamos haciendo, y por eso, vamos a echar un vistazo a las clases creadas en el paquete org.xulescode.jpa destacando los detalles más importantes.
Para empezar diré que estoy utilizando la implementación para JPA de eclipse EclipseLink , que se encuentra definida en el proyecto, es la que utiliza Netbeans por defecto, no es un detalle sin importancia, porque si te interesa utilizar otra implementación de JPA diferente como Hibernate deberás entender que tendrás que cambiar la librería que se está usando y revisar el código creado para comprobar la compatibilidad.
EclipseLink
Provides standards based Object-Relational persistence solution with additional support for many advanced features. EclipseLink JPA provides advanced support for leading relational databases and Java containers.EclipseLink
Entidad CbLanguage
Como puedes ver hacemos la definición básica de la entidad con la anotación @Entity indicando la tabla a la que hacemos referencia, en nuestro caso lo hemos realizado utilizando la ingeniería inversa que nos proporciona Netbeans con la generación de Entity classes from database, pero esta misma definición la podríamos hacer manualmente: @Table(name = «cb_language», catalog = «customerdb», schema = «»), como puedes ver se entiende fácilmente:
- name indicamos el nombre de la tabla/li>
- catalog este es el nombre de la base de datos
- schema definimos el esquema al que pertenece la tabla en este caso está vacío ya que se encuentra en el esquema public de la base de datos, es decir, sin esquema.
Para hacerlo fácil y comprobar que tenemos todo bien configurado esta es la clase CbLanguage para la tabla cb_language:
package org.xulescode.jpa; import java.io.Serializable; import java.util.Collection; import javax.persistence.Basic; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; /** * * @author xules */ @Entity @Table(name = "cb_language", catalog = "customerdb", schema = "") @XmlRootElement @NamedQueries({ @NamedQuery(name = "CbLanguage.findAll", query = "SELECT c FROM CbLanguage c"), @NamedQuery(name = "CbLanguage.findByIdlanguage", query = "SELECT c FROM CbLanguage c WHERE c.idlanguage = :idlanguage"), @NamedQuery(name = "CbLanguage.findByNamelanguage", query = "SELECT c FROM CbLanguage c WHERE c.namelanguage = :namelanguage"), @NamedQuery(name = "CbLanguage.findByIsactive", query = "SELECT c FROM CbLanguage c WHERE c.isactive = :isactive"), @NamedQuery(name = "CbLanguage.findByLanguageiso", query = "SELECT c FROM CbLanguage c WHERE c.languageiso = :languageiso"), @NamedQuery(name = "CbLanguage.findByCountrycode", query = "SELECT c FROM CbLanguage c WHERE c.countrycode = :countrycode"), @NamedQuery(name = "CbLanguage.findByIsbaselanguage", query = "SELECT c FROM CbLanguage c WHERE c.isbaselanguage = :isbaselanguage"), @NamedQuery(name = "CbLanguage.findByIssystemlanguage", query = "SELECT c FROM CbLanguage c WHERE c.issystemlanguage = :issystemlanguage")}) public class CbLanguage implements Serializable { private static final long serialVersionUID = 1L; @Id @Basic(optional = false) @NotNull @Size(min = 1, max = 6) @Column(name = "idlanguage") private String idlanguage; @Basic(optional = false) @NotNull @Size(min = 1, max = 60) @Column(name = "namelanguage") private String namelanguage; @Basic(optional = false) @NotNull @Size(min = 1, max = 1) @Column(name = "isactive") private String isactive; @Size(max = 2) @Column(name = "languageiso") private String languageiso; @Size(max = 2) @Column(name = "countrycode") private String countrycode; @Basic(optional = false) @NotNull @Size(min = 1, max = 1) @Column(name = "isbaselanguage") private String isbaselanguage; @Basic(optional = false) @NotNull @Size(min = 1, max = 1) @Column(name = "issystemlanguage") private String issystemlanguage; @OneToMany(mappedBy = "idlanguage") private Collection<CbEnterprise> cbEnterpriseCollection; @OneToMany(mappedBy = "idlanguage") private Collection<CbCountry> cbCountryCollection; @OneToMany(mappedBy = "idlanguage") private Collection<CbCustomer> cbCustomerCollection; public CbLanguage() { } public CbLanguage(String idlanguage) { this.idlanguage = idlanguage; } public CbLanguage(String idlanguage, String namelanguage, String isactive, String isbaselanguage, String issystemlanguage) { this.idlanguage = idlanguage; this.namelanguage = namelanguage; this.isactive = isactive; this.isbaselanguage = isbaselanguage; this.issystemlanguage = issystemlanguage; } // GETTER Y SETTERS @Override public int hashCode() { int hash = 0; hash += (idlanguage != null ? idlanguage.hashCode() : 0); return hash; } @Override public boolean equals(Object object) { // TODO: Warning - this method won't work in the case the id fields are not set if (!(object instanceof CbLanguage)) { return false; } CbLanguage other = (CbLanguage) object; if ((this.idlanguage == null && other.idlanguage != null) || (this.idlanguage != null && !this.idlanguage.equals(other.idlanguage))) { return false; } return true; } @Override public String toString() { return "org.xulescode.jpa.CbLanguage[ idlanguage=" + idlanguage + " ]"; } }
Se realizan las definiciones de las columnas de la base de datos usando JPA, las anotaciones básicas para la clave primaria (primary key) son:
- @Id indicamos que la columna es la clave primaria (primary key), observa que en este caso el campo id no se genera automáticamente sino que lo tendrá que introducir el usuario
- @Basic(optional = false)
- @NotNull se indica que no puede ser NULL
- @Size(min = 1, max = 6) indicamos el valor mínimo y máximo de la variable
- @Column(name = «idlanguage») el nombre de la columna de la base de datos, esto nos facilita que en la clase Java podamos utiliza otra representación para la columna.
En el resto de las columnas realizamos su definición indicando su nombre, tamaño y si pueden ser o no NULL, para todas las columnas se generan los getters y setters para todos los valores.
En esta creación automática podemos ver que se ha establecido una relación bidireccional de CbLanguage con las entidades: CbCustomer, CbCountry y CbEnterprise como se puede ver en el código:
@OneToMany(mappedBy = "idlanguage") private Collection<CbEnterprise> cbEnterpriseCollection; @OneToMany(mappedBy = "idlanguage") private Collection<CbCountry> cbCountryCollection; @OneToMany(mappedBy = "idlanguage") private Collection<CbCustomer> cbCustomerCollection;
Realmente esto no es necesario con el esquema que hemos planteado para este proyecto, lo realmente importante será la relación que establezcamos en las entidades anteriormente mencionadas con CbLanguage como veremos más adelante con CbCountry por ejemplo:
@JoinColumn(name = "idlanguage", referencedColumnName = "idlanguage") @ManyToOne private CbLanguage idlanguage;
También, en todas las entidades generadas se añaden los métodos base: public int hashCode() , public boolean equals(Object object) y public String toString().
Entidad CbCurrency
Está es la tabla de monedas del proyecto que utilizaremos en CbCountry, CbEnterprise y CbCustomer. Ahora nos centraremos en el código que define los campos de nuestra base de datos, ya que como hemos visto anteriormente para el resto de clases también se generarán las consultas básicas en @NamedQueries, los getters y setters correspondientes y los métodos base por defecto: public int hashCode() , public boolean equals(Object object) y public String toString().
Tal y como se explicó antes indicamos que es una entidad @Entity definimos la tabla @Table(name = «cb_currency», catalog = «customerdb», schema = «») e indicamos la clave primaria, en este caso la generación de la clave es automática y lo indicamos en la definición de la columna idcurrency con @GeneratedValue(strategy = GenerationType.IDENTITY), el resto de campos se definen según se puede ver en el código:
@Entity @Table(name = "cb_currency", catalog = "customerdb", schema = "") @XmlRootElement @NamedQueries({ ... }) public class CbCurrency implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Basic(optional = false) @Column(name = "idcurrency") private Integer idcurrency; @Basic(optional = false) @NotNull @Size(min = 1, max = 60) @Column(name = "currency") private String currency; @Basic(optional = false) @NotNull @Size(min = 1, max = 255) @Column(name = "description") private String description; @Basic(optional = false) @NotNull @Size(min = 1, max = 1) @Column(name = "isactive") private String isactive; @Basic(optional = false) @NotNull @Size(min = 1, max = 3) @Column(name = "isocode") private String isocode; @Size(max = 10) @Column(name = "cursymbol") private String cursymbol; @Basic(optional = false) @NotNull @Column(name = "precisionstd") private long precisionstd; @Basic(optional = false) @NotNull @Column(name = "precisioncost") private long precisioncost; @Basic(optional = false) @NotNull @Column(name = "precisionprize") private long precisionprize;
CbCountry
Como puedes ver en la clase o si analizaste la relación entre tablas verás que cb_country está relacionado con cb_currency y cb_language dentro de la definición de nuestra clase de países.
Al igual que en la base de datos en la clase también tenemos que definir las relaciones JPA que se utilizan, esto se tendrá en cuenta al generar la aplicación CRUD de presentación.
La relación de CbCountry con CbLanguage y CbCurrency es la misma: un país (CbCountry) puede tener un idioma (CbLanguage) seleccionano uno de todos los que existen, es decir, estamos hablando de una relación de varios a uno (ManyToOne), lo mismo sucede para CbCurrency. Veámoslo en el código:
@Entity @Table(name = "cb_country", catalog = "customerdb", schema = "") @XmlRootElement @NamedQueries({ ... }) public class CbCountry implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Basic(optional = false) @Column(name = "idcountry") private Integer idcountry; @Basic(optional = false) @NotNull @Size(min = 1, max = 100) @Column(name = "country") private String country; @Size(max = 255) @Column(name = "description") private String description; @Basic(optional = false) @NotNull @Size(min = 1, max = 2) @Column(name = "countrycode") private String countrycode; @Basic(optional = false) @NotNull @Size(min = 1, max = 1) @Column(name = "hasregion") private String hasregion; @Size(max = 60) @Column(name = "regionname") private String regionname; @Size(max = 20) @Column(name = "expressionphone") private String expressionphone; @Basic(optional = false) @NotNull @Size(min = 1, max = 20) @Column(name = "displaysequence") private String displaysequence; @Basic(optional = false) @NotNull @Size(min = 1, max = 1) @Column(name = "isdefault") private String isdefault; @Column(name = "ibannodigits") private Long ibannodigits; @Size(max = 2) @Column(name = "ibancountry") private String ibancountry; @Basic(optional = false) @NotNull @Column(name = "isactive") private boolean isactive; @JoinColumn(name = "idcurrency", referencedColumnName = "idcurrency") @ManyToOne private CbCurrency idcurrency; @JoinColumn(name = "idlanguage", referencedColumnName = "idlanguage") @ManyToOne private CbLanguage idlanguage;
Expliquémoslo:
- @ManyToOne
- Anotación JPA con la que definimos la relación de uno a muchos de CbCurrency.
- @JoinColumn(name = «idcurrency», referencedColumnName = «idcurrency»)
- Con @JoinColumn seguimos con JPA definiendo el nombre de la columna de la base de datos por la que relacionamos estas dos tablas en este caso idcurrency
- Si quisiéramos definir específico para la clave foránea lo tenemos que indicar: foreignKey = @ForeignKey(name = «fk_cb_country_idcurrency») donde es el nombre que le hemos dado en la base de datos, esto es importante si utilizamos posteriormente la creación de la base de datos a partir de la definición JPA, sino se generará un nombre automáticamente.
- private CbCurrency cbCurrency;
- En CbCountry creamos una clase CbCurrency que es la que hace referencia dentro de CbCountry a la relación y donde definimos los valores arriba descritos que indican la relación y la presentación.
Para CbLanguage la explicación es la misma como puedes ver en el código de la clase.
CbPaymentMethod
En la clase entidad creada CbPaymentMethod que será nuestra tabla de métodos de pago, indicando la clave primaria como clave primaria un Integer con el nombre idpaymentmethod de tipo serial como puedes comprobar en el código generado:
@Entity @Table(name = "cb_paymentmethod", catalog = "customerdb", schema = "") @XmlRootElement @NamedQueries({ .., }) public class CbPaymentmethod implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Basic(optional = false) @Column(name = "idpaymentmethod") private Integer idpaymentmethod; @Basic(optional = false) @NotNull @Size(min = 1, max = 100) @Column(name = "paymentmethod") private String paymentmethod; @Size(max = 150) @Column(name = "description") private String description; @Size(max = 250) @Column(name = "paymentterms") private String paymentterms; @Size(max = 50) @Column(name = "paymententity") private String paymententity;
Los métodos de pago CbPaymentMethod se asociarán con la entidad CbCustomer y en una extensión futura de la base de datos con otras entidades, como por ejemplo CbEnterprise.
CbEnterprise
La entidad CbEnterprise es la tabla de empresas de nuestro proyecto, lo que nos permitiría sentar la base para un sistema multiempresa, cada una de ellas tendrá sus propios clientes como se puede ver en el esquema SQL. Creamos la entidad CbEnterprise indicando la clave primaria, que en este caso es un Integer con el nombre identerprise:
@Entity @Table(name = "cb_enterprise", catalog = "customerdb", schema = "") @XmlRootElement @NamedQueries({ ... )}) public class CbEnterprise implements Serializable { private static final long serialVersionUID = 1L; @Id @Basic(optional = false) @NotNull @Column(name = "identerprise") private Integer identerprise; @Size(max = 150) @Column(name = "enterprise") private String enterprise; @Size(max = 250) @Column(name = "description") private String description; @Size(max = 100) @Column(name = "enterprisealias") private String enterprisealias; @Size(max = 250) @Column(name = "contact") private String contact; @Size(max = 30) @Column(name = "estate") private String estate; // @Max(value=?) @Min(value=?)//if you know range of your decimal fields consider using these annotations to enforce field validation @Column(name = "balance") private BigDecimal balance; @Size(max = 100) @Column(name = "ei") private String ei; @Size(max = 20) @Column(name = "enterprisepayer") private String enterprisepayer; @JoinColumn(name = "idlanguage", referencedColumnName = "idlanguage") @ManyToOne private CbLanguage idlanguage; @JoinColumn(name = "idcountry", referencedColumnName = "idcountry") @ManyToOne private CbCountry idcountry; @JoinColumn(name = "idcurrency", referencedColumnName = "idcurrency") @ManyToOne private CbCurrency idcurrency;
Definimos los campos generales detallados en el esquema para la tabla empresa como enterprise, description, …, y establecemos las relaciones con las tablas CbCountry, CbLanguage y CbCurrency para definir los valores relacionados para cada empresa. Este es el código en el que se definen estás relaciones:
@JoinColumn(name = "idlanguage", referencedColumnName = "idlanguage") @ManyToOne private CbLanguage idlanguage; @JoinColumn(name = "idcountry", referencedColumnName = "idcountry") @ManyToOne private CbCountry idcountry; @JoinColumn(name = "idcurrency", referencedColumnName = "idcurrency") @ManyToOne private CbCurrency idcurrency;
CbAddresses
La entidad CbAddresses es una tabla de registro de las direcciones de una entidad, en concreto en este esquema relacionaremos las direcciones que tiene un cliente. Así cada cliente tendrá un número ilimitado de direcciones con cb_address (CbAddress) relacionado mediante cb_addresses.
@Entity @Table(name = "cb_addresses", catalog = "customerdb", schema = "") @XmlRootElement @NamedQueries({ ... }) public class CbAddresses implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Basic(optional = false) @Column(name = "idaddresses") private Integer idaddresses; @Basic(optional = false) @NotNull @Size(min = 1, max = 100) @Column(name = "addressesentity") private String addressesentity; @OneToMany(mappedBy = "idaddresses") private Collection<CbAddress> cbAddressCollection; @OneToMany(mappedBy = "idaddresses") private Collection<CbCustomer> cbCustomerCollection;
Esta entidad autogenerada la cambiaremos más adelante en el desarrollo de este proyecto, para modificar la presentación y el funcionamiento, ya que lo que nos va a interesar modificar desde el cliente son las diferentes direcciones como veremos en el proyecto.
Aquí si tendremos que utilizar la relación cbAddressCollection:
@OneToMany(mappedBy = "idaddresses") private Collection<CbAddress> cbAddressCollection;
Esta va a ser la que nos permita incluir en la presentación de edición del cliente (CbCustomer) la múltiple edición de direcciones.
CbAddress
En esta entidad se almacena cada dirección que tendrá los campos generales de una dirección, así como números de teléfono, transportista y tipos de dirección, como nuestro proyecto busca que un cliente pueda tener múltiples direcciones no establecemos una relación directa con CbCustomer sino a través de CbAddresses
@Entity @Table(name = "cb_address", catalog = "customerdb", schema = "") @XmlRootElement @NamedQueries({ ... }) public class CbAddress implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Basic(optional = false) @Column(name = "idaddress") private Integer idaddress; @Size(max = 500) @Column(name = "address") private String address; @Size(max = 20) @Column(name = "postalnumber") private String postalnumber; @Size(max = 100) @Column(name = "mainphone") private String mainphone; @Size(max = 100) @Column(name = "movilephone") private String movilephone; @Size(max = 100) @Column(name = "phone2") private String phone2; @Size(max = 100) @Column(name = "phone3") private String phone3; @Size(max = 200) @Column(name = "carrier") private String carrier; @Size(max = 100) @Column(name = "addresstype") private String addresstype; @Size(max = 250) @Column(name = "locality") private String locality; @Size(max = 250) @Column(name = "estate") private String estate; @Size(max = 500) @Column(name = "notes1") private String notes1; @JoinColumn(name = "idaddresses", referencedColumnName = "idaddresses") @ManyToOne private CbAddresses idaddresses; @JoinColumn(name = "idcountry", referencedColumnName = "idcountry") @ManyToOne private CbCountry idcountry;
Es interesante que observes bien la relación que se establece con CbAddresses ya que en este caso si utilizaremos la relación bidireccional como explicaba en la entidad:
@JoinColumn(name = "idaddresses", referencedColumnName = "idaddresses") @ManyToOne private CbAddresses idaddresses;
Entidad CbCustomer
En la entidad CbCustomer se almacenarán los clientes de las diferentes empresas, para cumplir una de las características de nuestro proyecto en el que buscábamos que fuese multiempresa.
Se definen inicialmente: idioma (CbLanguage), moneda (CbCurrency), país (CbCountry), método de pago (CbPaymentMethod), direcciones (CbAddresses) y empresa (CbEnterprise), así como otros campos básicos propios.
Estas relaciones y campos se pueden entender bien con las explicaciones dadas con anterioridad, véase también que utilizamos una clave primaria Integer de tipo serial con idcustomer:
@Entity @Table(name = "cb_customer", catalog = "customerdb", schema = "") @XmlRootElement @NamedQueries({ ... }) public class CbCustomer implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Basic(optional = false) @Column(name = "idcustomer") private Integer idcustomer; @Basic(optional = false) @NotNull @Size(min = 1, max = 15) @Column(name = "customer") private String customer; @Size(max = 150) @Column(name = "customername") private String customername; @Size(max = 100) @Column(name = "customeralias") private String customeralias; @Size(max = 250) @Column(name = "contact") private String contact; @Size(max = 30) @Column(name = "customerstate") private String customerstate; // @Max(value=?) @Min(value=?)//if you know range of your decimal fields consider using these annotations to enforce field validation @Column(name = "sale") private BigDecimal sale; @Size(max = 100) @Column(name = "identitynumber") private String identitynumber; @Size(max = 20) @Column(name = "customerpayer") private String customerpayer; @JoinColumn(name = "idaddresses", referencedColumnName = "idaddresses") @ManyToOne private CbAddresses idaddresses; @JoinColumn(name = "idcountry", referencedColumnName = "idcountry") @ManyToOne private CbCountry idcountry; @JoinColumn(name = "idcurrency", referencedColumnName = "idcurrency") @ManyToOne private CbCurrency idcurrency; @JoinColumn(name = "identerprise", referencedColumnName = "identerprise") @ManyToOne private CbEnterprise identerprise; @JoinColumn(name = "idlanguage", referencedColumnName = "idlanguage") @ManyToOne private CbLanguage idlanguage; @JoinColumn(name = "idpaymentmethod", referencedColumnName = "idpaymentmethod") @ManyToOne private CbPaymentmethod idpaymentmethod;
4. DEFINIENDO MANUALMENTE LA CONEXIÓN (GLASSFISH)
Te preguntarás por qué explico esto, es sencillo será de los primeros problemas que te encuentres al desplegar tu proyecto ya que lo más probable es que tengas algún fallo al desplegar el proyecto en el servidor, y este es el por qué de esta explicación, ya que yo ya lo he sufrido.
The most common cause for the failure is a problem when generating the JDBC resources on the server. If this is the case, you will probably see a message similar to the following in the server log tab in the Output window.
Severe: Exception while preparing the app : Invalid resource : jdbc/consult__pm
com.sun.appserv.connectors.internal.api.ConnectorRuntimeException: Invalid resource : jdbc/consult__pmNetbeans JSF CRUD – Troubleshooting
En nuestro proyecto tenemos definido un fichero dentro de Server Resources que se llama glassfish-resources.xml y es donde definimos la conexión y el pool de conexiones que vamos a utilizar en nuestra aplicación. Cuando despleguemos nuestra aplicación esta información se va a transmitir al servidor para que cree el Datasource y el pool de conexiones.
Este es su contenido:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN" "http://glassfish.org/dtds/glassfish-resources_1_5.dtd"> <resources> <jdbc-connection-pool allow-non-component-callers="false" associate-with-thread="false" connection-creation-retry-attempts="0" connection-creation-retry-interval-in-seconds="10" connection-leak-reclaim="false" connection-leak-timeout-in-seconds="0" connection-validation-method="auto-commit" datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" fail-all-connections="false" idle-timeout-in-seconds="300" is-connection-validation-required="false" is-isolation-level-guaranteed="true" lazy-connection-association="false" lazy-connection-enlistment="false" match-connections="false" max-connection-usage-count="0" max-pool-size="32" max-wait-time-in-millis="60000" name="mariadb_customerdb" non-transactional-connections="false" pool-resize-quantity="2" res-type="javax.sql.DataSource" statement-timeout-in-seconds="-1" steady-pool-size="8" validate-atmost-once-period-in-seconds="0" wrap-jdbc-objects="false"> <property name="serverName" value="192.168.0.26"/> <property name="portNumber" value="3306"/> <property name="databaseName" value="customerdb"/> <property name="User" value="xulescode"/> <property name="Password" value="xulescode"/> <property name="URL" value="jdbc:mysql://192.168.0.26:3306/customerdb?zeroDateTimeBehavior=convertToNull"/> <property name="driverClass" value="com.mysql.jdbc.Driver"/> </jdbc-connection-pool> <jdbc-resource enabled="true" jndi-name="CustomerdbDataSource" object-type="user" pool-name="mariadb_customerdb"/> </resources>
Como puedes ver se define el nombre jndi jndi-name y el nombre de pool pool-name, estos valores los podremos cambiar si queremos crear un pool nuevo directamente en nuestro servidor Glassfish que es el que estamos usando para este proyecto, veamos como.
Si no has tocado la configuración por defecto la url para acceder a tu instalación del servidor Glassfish será: http://localhost:4848/common/index.jsf, dentro de la pestaña Resources encontrarás el apartado JDBC con los enlaces a JDBC Resources y JDBC Connection Pools como se muestra en la imagen:
En el apartado JDBC Resources aparecen los DataSources que tenemos definidos, en mi caso aparece CustomerdbDataSourceNew en vez de CustomerdbDataSource ya que lo he cambiado realizando pruebas para este ejemplo:
En el apartado JDBC Connection Pools podemos definir un nuevo pool de conexiones para después relacionarlo con un nuevo JDBC Resource (DataSource) por ejemplo, esta es la lista de mi configuración, dónde puede ver mariadb_customerdb_new que es la que modifique yo para este ejemplo:
Para crear el nuevo pool utiliza el botón New dentro de JDBC Connection Pools, donde te aparecerá la ventana de creación y podrás crearla con el nombre mariadb_customerdb_new o el que tu prefieras, en la imagen dejé marcadas las opciones que tienes que seleccionar para crear el pool (Pool Name: nombre-pool ; Resource Type: javax.sql.DataSource ; Database Driver Vendor: Mysql)
Rellenamos las opciones generales de base de datos, url de la conexión, usuario y clave:
Finalmente creamos el nuevo JDBC Resource CustomerdbDataSourceNew y lo relacionamos con el nuevo JDBC Connection Pool mariadb_customerdb_new :
Una vez creado ya solo te quedará actualizar el fichero glassfish-resources.xml con los nuevos datos de jndi-name y de pool-name, desplegar el proyecto y comprobar que todo funciona correctamente. Así es como queda ahora el mío:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN" "http://glassfish.org/dtds/glassfish-resources_1_5.dtd"> <resources> <jdbc-connection-pool allow-non-component-callers="false" associate-with-thread="false" connection-creation-retry-attempts="0" connection-creation-retry-interval-in-seconds="10" connection-leak-reclaim="false" connection-leak-timeout-in-seconds="0" connection-validation-method="auto-commit" datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" fail-all-connections="false" idle-timeout-in-seconds="300" is-connection-validation-required="false" is-isolation-level-guaranteed="true" lazy-connection-association="false" lazy-connection-enlistment="false" match-connections="false" max-connection-usage-count="0" max-pool-size="32" max-wait-time-in-millis="60000" name="mariadb_customerdb_new" non-transactional-connections="false" pool-resize-quantity="2" res-type="javax.sql.DataSource" statement-timeout-in-seconds="-1" steady-pool-size="8" validate-atmost-once-period-in-seconds="0" wrap-jdbc-objects="false"> <property name="serverName" value="192.168.0.26"/> <property name="portNumber" value="3306"/> <property name="databaseName" value="customerdb"/> <property name="User" value="xulescode"/> <property name="Password" value="xulescode"/> <property name="URL" value="jdbc:mysql://192.168.0.26:3306/customerdb?zeroDateTimeBehavior=convertToNull"/> <property name="driverClass" value="com.mysql.jdbc.Driver"/> </jdbc-connection-pool> <jdbc-resource enabled="true" jndi-name="CustomerdbDataSource_new" object-type="user" pool-name="mariadb_customerdb_new"/> </resources>
If the server log tab is not open you can open the tab by right-clicking the GlassFish Server node in the Services window and choosing View Domain Server Log.Netbeans
Espero que te haya sido útilXules
Primefaces (2.2): Creación de las páginas CRUD Primefaces del proyecto
En la siguiente publicación realizamos la parte CRUD web y exploramos las posibilidades que nos ofrece esta aplicación JSF – Primefaces, aquí un adelanto del resultado final:
¿Qué hacemos ahora?
También puedes comparar el desarrollo de Primefaces con otros frameworks que también usan metodologías CRUD para la creación rápida de proyectos como Spring Roo y OpenXava.
Aplicación Web CRUD, segura y multiidioma con Spring Roo con PostgreSQL
Aplicación Web CRUD, segura y multiidioma con Spring Roo con PostgreSQL – Guía Spring Roo 1. Con Spring Roo definimos nuestra entidades, generamos la aplicación Web CRUD, le añadimos Spring Security y diferentes idiomas para la aplicación desde nuestra consola Roo.Código Xules
OpenXava
Creamos el proyecto Learning Project con OpenXava donde prepararemos el entorno, empezaremos la programación, definiremos todas las clases del modelo para el proyecto, y por último, actualizaremos la presentación analizando las capacidades de OpenXava.Xules
Primefaces 2
Muy buena didactica
Hola Germán, muchas gracias por tu valoración.
Saludos.
He estado probando el CRUD generator con versiones diferentes tanto de netbeans como de glassfish y tomcat actualmente netbeans 8.1 .
Al parecer todo se crea bien pero a la hora de correr la aplicación en las páginas .xhtml CRÉATE, UPDATE de las entidades con dependencia el <p:commandButton para llamar el método de saveNew no funciona he estado viendo blogs y el creador del CRUD dice que es por la actualización de jsf de 2.1 a 2.2.
La pregunta esa versión de netbeans (8.0.2) con la que haces el ejemplo el mismo problema ocurre ?