JSF Parametros por Get Request RESTFull

Olá tudo bem?

Como utilizar passagem de parâmetros via URL utilizando JSF? Creio que será o menor post aqui do blog, pois é muito simples.

Ao final do post você irá encontrar o link para download do código de hoje.

Os outros posts sobre JSF são: JSF Exemplos Simples com AjaxJSF Exibindo Objeto e Mensagens após Redirect, Validação de Login de Usuário com JSF e JAASJSF: Converter and Bean AutoCompleteJSF – Hello World, AutoComplete, Tratando Exceções em uma Aplicação Web, Autenticação de Usuários (Filter/Servlet), Criando um WebServer..

Veja como é simples o código para receber parâmetros via URL no JSF 2.0:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">
<h:head>
	<f:metadata>
		<f:viewParam name="email" value="#{user.email}" />
		<f:viewParam name="name" value="#{user.name}" />
	</f:metadata>
</h:head>
<h:body>
	<h:form>
		Your email is: #{user.email} <br/>
		Your name is: #{user.name} <br/>
		Your name and email from MB: #{user.nameAndEmail} <br/>
	</h:form>
</h:body>
</html>
package demo;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

/**
 * Created by JBoss Tools
 */
@ManagedBean(name = "user")
@RequestScoped
public class User {

	private String email;
	private String name;

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getNameAndEmail(){
		return email + " " + name;
	}
}

A URL utilizada foi: http://localhost:8080/RequestParameter/values.xhtml?email=Read+Romans&name=Chapter10+Ver+09.

Como você pode ver a página espera dois parâmetros e existe um método que utiliza os dois parâmetros; seria o método em que você iria disparar a consulta no banco de dados ou qualquer outra ação necessária.

E como criar um link para o usuário navegar? Fácil, olhe e página abaixo:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">
<h:head>

</h:head>
<h:body>
	<h:form>
		01) A pre-formed link: <br/>
		<h:link outcome="values?email=Read Romans&amp;name=Chapter10 Ver 09" value="Let us go!" />
		<br/>
		<br/>
		02)Link with the f:param component<br/>
		<h:link outcome="values" value="Write all params and let us go!" >
			<f:param name="email" value="Read Romans" />
			<f:param name="name" value="Chapter10 Ver 09" />
		</h:link>
		<br/>
		<br/>
		03) <h:commandLink value="Redirect Me!" action="values?faces-redirect=true&amp;email=Read Romans&amp;name=Chapter10 Ver 09" />
	</h:form>
</h:body>
</html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">
<h:head>
	<f:metadata>
		<f:viewParam name="email" value="#{user.email}" />
		<f:viewParam name="name" value="#{user.name}" />
	</f:metadata>
</h:head>
<h:body>
	<h:form>
		Your email is: #{user.email} <br/>
		Your name is: #{user.name} <br/>
		Your name and email from MB: #{user.nameAndEmail} <br/>

		04) <h:link outcome="values" value="Get everybody and leave this place" includeViewParams="true" />
	</h:form>
</h:body>
</html>

Vou comentar agora sobre cada abordagem utilizada na criação do link (de acordo com a numeração utilizada no código fonte “01,02…”):

  • 01)  Foi um link já formado onde todos os parâmetros estão escritos.
  • 02)  Utilizamos o componente “f:param” que acaba por deixar mais legível o código.
  • 03)  A navegação é feita através de um redirect e utilizando o commandLink. ATENÇÃO: até o dia de hoje, ainda não é possível utilizar o componente “f:param” junto com um redirect. Por isso que escrevi a URL toda de uma vez com o redirect no link. “<h:commandLink action=”values?faces-redirect=true.
  • 04) Com esse modo de link, com a opção (includeViewParams=”true”), você irá levar automaticamente todos os “f:viewParam” presentes na tela. Você nem irá precisar escrever os parâmetros progamaticamente. O JSF irá automaticamente pegar todos os parâmetros para você.

Você pode adicionar parâmetros de navegação após um redirect utilizando a faces-config.xml:

<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="2.0" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
	<navigation-rule>
		<navigation-case>
			<from-outcome>redirectMe</from-outcome>
			<to-view-id>/myPage.xhtml</to-view-id>
			<redirect include-view-params="true">
				<view-param>
					<name>calculatedValue</name>
					<value>#{myBean.value}</value>
				</view-param>
			</redirect>
		</navigation-case>
	</navigation-rule>
</faces-config>

O código de hoje funciona com os componentes “<h:link />” e “<h:commandLink />” ambos podendo ser utilizados com ou sem a opção redirect.

Lembro também que seu link pode ser escrito em seu ManagedBean e seu valor acessado por #{meuMB.createTheURL} nos componentes link/commandLink.

Click aqui para fazer o download do código do post de hoje.

Espero que esse post possa te ajudar.

Qualquer dúvida/pergunta/colocação basta postar.

Até a próxima pessoal.

JSF RESTFull Get Request Parameters

Hello, how are you?

How can we pass parameters through a URL using JSF 2.0? I believe this will be smallest post here.

At the end of this post you will find the link to download the created source code.

The other posts about JSF/Web Applications you can find in here: JSF Simple Ajax SamplesJSF Persisting Objects and Messages after a Redirect ,User Login Validation with JAAS and JSFJSF: Converter and Bean AutoCompleteJSF – Hello World, Auto CompleteHandling Exceptions on a WebAppUser Authentication (Filter/Servlet)Creating a WebServer.

Take a look bellow to see that is very easy to receive a parameter through a URL:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">
<h:head>
	<f:metadata>
		<f:viewParam name="email" value="#{user.email}" />
		<f:viewParam name="name" value="#{user.name}" />
	</f:metadata>
</h:head>
<h:body>
	<h:form>
		Your email is: #{user.email} <br/>
		Your name is: #{user.name} <br/>
		Your name and email from MB: #{user.nameAndEmail} <br/>
	</h:form>
</h:body>
</html>
package demo;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

/**
 * Created by JBoss Tools
 */
@ManagedBean(name = "user")
@RequestScoped
public class User {

	private String email;
	private String name;

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getNameAndEmail(){
		return email + " " + name;
	}
}

The used URL to access that page is: http://localhost:8080/RequestParameter/values.xhtml?email=Read+Romans&name=Chapter10+Ver+09.

As you can see the page expects two parameters and in our ManagedBean exists a method that uses both parameters; in this method you could search for your entities in the database or any other action that you need.

How can we create a link to be navigated by the user? Piece of cake, take a look at the page/code bellow:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">
<h:head>

</h:head>
<h:body>
	<h:form>
		01) A pre-formed link: <br/>
		<h:link outcome="values?email=Read Romans&amp;name=Chapter10 Ver 09" value="Let us go!" />
		<br/>
		<br/>
		02)Link with the f:param component<br/>
		<h:link outcome="values" value="Write all params and let us go!" >
			<f:param name="email" value="Read Romans" />
			<f:param name="name" value="Chapter10 Ver 09" />
		</h:link>
		<br/>
		<br/>
		03) <h:commandLink value="Redirect Me!" action="values?faces-redirect=true&amp;email=Read Romans&amp;name=Chapter10 Ver 09" />
	</h:form>
</h:body>
</html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">
<h:head>
	<f:metadata>
		<f:viewParam name="email" value="#{user.email}" />
		<f:viewParam name="name" value="#{user.name}" />
	</f:metadata>
</h:head>
<h:body>
	<h:form>
		Your email is: #{user.email} <br/>
		Your name is: #{user.name} <br/>
		Your name and email from MB: #{user.nameAndEmail} <br/>

		04) <h:link outcome="values" value="Get everybody and leave this place" includeViewParams="true" />
	</h:form>
</h:body>
</html>


I will talk about each approach used in the codes to generate the links (according to each number used like 01, 02…):

  • 01)  A pre-formed link that has all the parameters written in it.
  • 02)  We are using the “f:param” component that gives us a cleaner code.
  • 03)  A link that will use the redirect to navigate through the commandLink component. BE CAREFULL: until today, the JSF will not send paramters in a redirect if you use the “f:param” component. That is why I wrote all URL in the link with the redirect. “<h:commandLink action=”values?faces-redirect=true”.
  • 04)  Using this approach with the option (includeViewParams=”true”) you will automatically includ all the “f:viewParam” that are present in the view. You will not need to write them in the “h:link” component. JSF will gather all the parameters for you.

You can also add parameters when you use the navigation through the “faces-config.xml”:

<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="2.0" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
	<navigation-rule>
		<navigation-case>
			<from-outcome>redirectMe</from-outcome>
			<to-view-id>/myPage.xhtml</to-view-id>
			<redirect include-view-params="true">
				<view-param>
					<name>calculatedValue</name>
					<value>#{myBean.value}</value>
				</view-param>
			</redirect>
		</navigation-case>
	</navigation-rule>
</faces-config>

The code from this post will work with the “h:link” or the “h:commandLink” and both can be used with the redirect option.

I also want to remind you that your URL can be wrote in a ManagedBean and the value used like #{youMB.createTheURL} in the “link/commandLink” components.

Click here to download the source code.

I hope this post helps you.

If you have any question/doubt/suggestion just post it.

See you soon. o_

JSF Exemplos Simples com Ajax

Olá, tudo bem?

Hoje vamos exibir alguns simples modos de fazer chamadas Ajax utilizando JSF 2.0.

Para ver outros posts sobre Web Applications/JSF basta acessar os links a seguir: JSF Exibindo Objeto e Mensagens após Redirect, Validação de Login de Usuário com JSF e JAASJSF: Converter and Bean AutoCompleteJSF – Hello World, AutoComplete, Tratando Exceções em uma Aplicação Web, Autenticação de Usuários (Filter/Servlet), Criando um WebServer.

Ao final do post de hoje você verá o código para download. No post (Validação de Login de Usuário com JSF e JAAS) é exibido como instalar o JBoss 6 caso você queira executar o código do post de hoje. Você precisará instalar o JBoss Tools em seu Eclipse.

Veja a tela abaixo e seu respectivo código.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">
<h:head>
</h:head>
<h:body>
	<h:form>
		Your Name: <h:inputText id="inputname" label="${msgs.prompt}" value="#{user.name}"/>
		<br />
		<h:commandButton action="#{user.sayHello}" value="Display my name here, now!"/>
		<br />
	</h:form>
</h:body>
</html>

Como exibir o nome digitado na mesma tela utilizando Ajax? Fácil, basta adicionar o comando “f:ajax” na linha e pronto. Veja a alteração e o resultado na página:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">
<h:head>
</h:head>
<h:body>
	<h:form>
		Your Name: <h:inputText id="inputname" label="${msgs.prompt}" value="#{user.name}"/>
		<br />
		<h:commandButton action="#{user.sayHello}" value="Display my name here, now!">
			<f:ajax render="myName" execute="inputname" />
		</h:commandButton>
		<br />
		<br />
		<h:outputText id="myName" value="#{user.name}" />
	</h:form>
</h:body>
</html>

Viu como é simples? Passei apenas o valor que será levado,  para o ManagedBean, através do parâmetro “execute” e quem eu gostaria que o JSF atualizasse através do parâmetro “render”.

Repare que o nome digitado aparece no console também.

Esse exemplo serve para atualizar todos os tipos de componentes. Vamos para mais um?

Vamos fazer com que uma mensagem de erro seja exibida caso o nome preenchido tenha menos que 4 letras.

Veja o novo código da página e a mensagem exibida:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">
<h:head>
</h:head>
<h:body>
	<h:form>
		<h:messages id="myMessage" globalOnly="true" showDetail="true"/>
		Your Name: <h:inputText id="inputname" label="${msgs.prompt}" value="#{user.name}"/>
		<br />
		<h:commandButton action="#{user.sayHello}" value="Display my name here, now!">
			<f:ajax render="myName myMessage" execute="inputname" />
		</h:commandButton>
		<br />
		<br />
		<h:outputText id="myName" value="#{user.name}" />
	</h:form>
</h:body>
</html>
package demo;

import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.context.FacesContext;

/**
 * Created by JBoss Tools
 */
@ManagedBean(name = "user")
@RequestScoped
public class User {

	private String name;

	public String sayHello() {
		if (isNameIncorrect()) {
			FacesContext context = FacesContext.getCurrentInstance();
			context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Too small", "Can you write it a little bigger?"));
		}

		System.out.println(name);
		return null;
	}

	private boolean isNameIncorrect() {
		return "".equals(name.trim()) || name.length() < 3;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

Note no código que foi colocado um “h:messages” e ele tem seu ID utilizado pelo componente “f:ajax”.  Essa solução também funciona quando você tem um componente do tipo “h:message for=”YYY””.

Vamos trabalhar agora com comboboxes? Iremos exibir um combobox que contenha no máximo 4 itens quando temos um nome com menos de 6 caracteres ou uma lista com mais de 4 itens quando temos um nome com mais de 6 caracteres:

package demo;

import java.util.ArrayList;
import java.util.List;

import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.component.UISelectItems;
import javax.faces.component.html.HtmlSelectOneMenu;
import javax.faces.context.FacesContext;
import javax.faces.event.AjaxBehaviorEvent;

/**
 * Created by JBoss Tools
 */
@ManagedBean(name = "user")
@RequestScoped
public class User {

	private String name;

	private List<String> cars;

	private String selectedCar;
	private HtmlSelectOneMenu htmlSelectCars;

	private static final String SELECT_A_CAR = "Select One Car";

	public User() {
		cars = new ArrayList<String>();
	}

	public String sayHello() {
		if (isNameInCorrect()) {
			FacesContext context = FacesContext.getCurrentInstance();
			context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Too small", "Can you write it a little bigger?"));
		}

		System.out.println(name);
		return null;
	}

	private boolean isNameInCorrect() {
		return name == null || "".equals(name.trim()) || name.length() < 3;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void editMyCarsList(AjaxBehaviorEvent event) {
		if (htmlSelectCars == null) {
			htmlSelectCars = new HtmlSelectOneMenu();
		}

		htmlSelectCars.getChildren().clear();

		UISelectItems items = new UISelectItems();
		items.setValue(getCars());
		htmlSelectCars.getChildren().add(items);
	}

	public List<String> getCars() {
		cars.clear();

		cars.add(SELECT_A_CAR);

		if (!isNameInCorrect() && name.length() >= 6) {
			cars.add("Ferrari");
			cars.add("Porch");
			cars.add("Beetle");
			cars.add("Opala");
			cars.add("Passat");
			cars.add("Vectra");
			cars.add("Chevet");
			cars.add("Corvet");
		} else {
			cars.add("Ferrari");
			cars.add("Porch");
			cars.add("Beetle");
			cars.add("Opala");
		}

		return cars;
	}

	public void setCars(List<String> cars) {
		this.cars = cars;
	}

	public String getSelectedCar() {
		return selectedCar;
	}

	public void setSelectedCar(String selectedCar) {
		this.selectedCar = selectedCar;
	}

	public HtmlSelectOneMenu getHtmlSelectCars() {
		editMyCarsList(null);

		return htmlSelectCars;
	}

	public void setHtmlSelectCars(HtmlSelectOneMenu htmlSelectCars) {
		this.htmlSelectCars = htmlSelectCars;
	}
}

Repare agora nosso código na página:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">
<h:head>
</h:head>
<h:body>
	<h:form>
		<h:messages id="myMessage" globalOnly="true" showDetail="true" />
		Your Name: <h:inputText id="inputname" label="${msgs.prompt}" value="#{user.name}" />
		<br />
		<h:commandButton action="#{user.sayHello}" value="Display my name here, now!">
			<f:ajax render="myName myCars myMessage" execute="inputname" listener="#{user.editMyCarsList}" />
		</h:commandButton>
		<br />
		<br />
		<h:outputText id="myName" value="#{user.name}" />
		<br />
		<br />
		Choose your car: <h:selectOneMenu id="myCars" binding="#{user.htmlSelectCars}" value="#{user.selectedCar}" />
		<br />
		<br />
	</h:form>
</h:body>
</html>

Como você pode perceber no código acima, o combobox tem seu valor alterado de acordo com o nome digitado. Ao final do post, irei explicar por que utilizei o binding ao componente HtmlSelectOneMenu ao invés de passar uma lista (List<String>) diretamente.

Como último exemplo, vamos fazer com que tenha um combobox invisível que só apareça quando o usuário tiver selecionado algum carro.

Veja como ficar nosso ManagedBean:

package demo;

import java.util.ArrayList;
import java.util.List;

import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.component.UISelectItems;
import javax.faces.component.html.HtmlSelectOneMenu;
import javax.faces.context.FacesContext;
import javax.faces.event.AjaxBehaviorEvent;

/**
 * Created by JBoss Tools
 */
@ManagedBean(name = "user")
@RequestScoped
public class User {

	private String name;

	private List<String> cars;
	private List<String> colors;

	private String selectedCar;
	private String selectedColor;
	private HtmlSelectOneMenu htmlSelectCars;

	private static final String SELECT_A_CAR = "Select One Car";

	public User() {
		cars = new ArrayList<String>();
		colors = new ArrayList<String>();

		colors.add("Red");
		colors.add("Blue");
		colors.add("Orange");
		colors.add("Pink --> O.o");
	}

	public String sayHello() {
		if (isNameInCorrect()) {
			FacesContext context = FacesContext.getCurrentInstance();
			context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Too small", "Can you write it a little bigger?"));
		}

		System.out.println(name);
		return null;
	}

	private boolean isNameInCorrect() {
		return name == null || "".equals(name.trim()) || name.length() < 3;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void editMyCarsList(AjaxBehaviorEvent event) {
		if (htmlSelectCars == null) {
			htmlSelectCars = new HtmlSelectOneMenu();
		}

		htmlSelectCars.getChildren().clear();

		UISelectItems items = new UISelectItems();
		items.setValue(getCars());
		htmlSelectCars.getChildren().add(items);
	}

	public List<String> getCars() {
		cars.clear();

		cars.add(SELECT_A_CAR);

		if (!isNameInCorrect() && name.length() >= 6) {
			cars.add("Ferrari");
			cars.add("Porch");
			cars.add("Beetle");
			cars.add("Opala");
			cars.add("Passat");
			cars.add("Vectra");
			cars.add("Chevet");
			cars.add("Corvet");
		} else {
			cars.add("Ferrari");
			cars.add("Porch");
			cars.add("Beetle");
			cars.add("Opala");
		}

		return cars;
	}

	public void setCars(List<String> cars) {
		this.cars = cars;
	}

	public String getSelectedCar() {
		return selectedCar;
	}

	public void setSelectedCar(String selectedCar) {
		this.selectedCar = selectedCar;
	}

	public List<String> getColors() {
		return colors;
	}

	public void setColors(List<String> colors) {
		this.colors = colors;
	}

	public boolean isColorsAlloweToDisplay() {
		if (isNameInCorrect()) {
			return false;
		}

		if (selectedCar == null || selectedCar.trim().equals("") || selectedCar.equals(SELECT_A_CAR)) {
			return false;
		}

		return true;
	}

	public String getSelectedColor() {
		return selectedColor;
	}

	public void setSelectedColor(String selectedColor) {
		this.selectedColor = selectedColor;
	}

	public HtmlSelectOneMenu getHtmlSelectCars() {
		editMyCarsList(null);

		return htmlSelectCars;
	}

	public void setHtmlSelectCars(HtmlSelectOneMenu htmlSelectCars) {
		this.htmlSelectCars = htmlSelectCars;
	}
}

Repare que foram alterações bem simples, onde adicionamos um List um método que irá dizer quando a lista deve ser exibida ou não.

Veja como ficou nossa página:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">
<h:head>
</h:head>
<h:body>
	<h:form>
		<h:messages id="myMessage" globalOnly="true" showDetail="true" />
		Your Name: <h:inputText id="inputname" label="${msgs.prompt}" value="#{user.name}" />
		<br />
		<h:commandButton action="#{user.sayHello}" value="Display my name here, now!">
			<f:ajax render="myName myCars myMessage myColors" execute="inputname" listener="#{user.editMyCarsList}" />
		</h:commandButton>
		<br />
		<br />
		<h:outputText id="myName" value="#{user.name}" />
		<br />
		<br />
		Choose your car:
		<h:selectOneMenu id="myCars" binding="#{user.htmlSelectCars}" value="#{user.selectedCar}">
			<f:ajax render="myColors" execute="inputname myCars"/>
		</h:selectOneMenu>
		<br />
		<br />
		<h:panelGroup id="myColors">
			<h:selectOneMenu value="#{user.selectedColor}" rendered="#{user.colorsAlloweToDisplay}">
				<f:selectItems value="#{user.colors}" />
			</h:selectOneMenu>
		</h:panelGroup>
	</h:form>
	</h:body>
</html>

Agora farei diversas observações sobre o código e práticas adotadas:

  • HtmlSelectOneMenu – Eu utilizei o componente ao invés de uma simples lista por um pequeno detalhe. O JSF ainda não se dá bem na hora de construir os objetos na tela (DOM Tree). Com isso, se a primeira lista tinha 4 itens e depois, via ajax, você adiciona mais itens ele não irá reconhecer os itens mais novos. Com isso mesmo com uma lista contendo um novo valor o JSF não irá reconhecer, e você irá apenas poder utilizar os valores antigos. Você pode tentar com List<String> ao invés do component HtmlSelectOneMenu apenas e ver o que vai acontecer, fiquei umas 3~4 horas de pesquisa para achar uma solução.
  • HtmlSelectOneMenu dentro do “h:panelGroup” – Adicionei o selectOne dentro do panelGroup pois toda vez que um objeto não é exibido e você deseja exibi-lo é necessário atualizar todo seu container. Ou seja, se o segundo selectOne (cores) estivesse no mesmo “form”, você deveria utilizar seu “render” em todo o form.

Click Aqui para fazer o download do código do programa de hoje.

Espero que o post de hoje possa te ajuda.

Qualquer dúvida/pergunta/comentário poste abaixo.

Até a próxima

JSF Simple Ajax Samples

Hello, how are you?

Today we will see some Simple Samples of Ajax with JSF.

If you want to see other posts about JSF/Web Applications click on the next links: JSF Persisting Objects and Messages after a Redirect ,User Login Validation with JAAS and JSFJSF: Converter and Bean AutoComplete, JSF – Hello World, Auto Complete, Handling Exceptions on a WebApp, User Authentication (Filter/Servlet), Creating a WebServer.

In the end of this post you will find the link to download the source code of the samples. In this post (User Login Validation with JAAS and JSF) I show how to install the JBoss 6 in case you want to run the project from today. You will need to install the JBoss tools plugin in your Eclipse.

Take a look at the page bellow and its code:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">
<h:head>
</h:head>
<h:body>
	<h:form>
		Your Name: <h:inputText id="inputname" label="${msgs.prompt}" value="#{user.name}"/>
		<br />
		<h:commandButton action="#{user.sayHello}" value="Display my name here, now!"/>
		<br />
	</h:form>
</h:body>
</html>

How could we display the typed name in the same screen using Ajax? Piece of cake, just add the “f:ajax” component. Check the code update and the result:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">
<h:head>
</h:head>
<h:body>
	<h:form>
		Your Name: <h:inputText id="inputname" label="${msgs.prompt}" value="#{user.name}"/>
		<br />
		<h:commandButton action="#{user.sayHello}" value="Display my name here, now!">
			<f:ajax render="myName" execute="inputname" />
		</h:commandButton>
		<br />
		<br />
		<h:outputText id="myName" value="#{user.name}" />
	</h:form>
</h:body>
</html>

It is very easy right? We just need to pass the value that will be carried to the ManagedBean by the “execute” parameter; with the “render” parameter we will tell to the JSF which component will be “refreshed”.

Notice also that the typed name appears in the Console.

With this code we can “refresh” all kind of components. Let us see another sample?

Let us display an error message if the user types a name with less than 4 characters.

Check our new message and the new code:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">
<h:head>
</h:head>
<h:body>
	<h:form>
		<h:messages id="myMessage" globalOnly="true" showDetail="true"/>
		Your Name: <h:inputText id="inputname" label="${msgs.prompt}" value="#{user.name}"/>
		<br />
		<h:commandButton action="#{user.sayHello}" value="Display my name here, now!">
			<f:ajax render="myName myMessage" execute="inputname" />
		</h:commandButton>
		<br />
		<br />
		<h:outputText id="myName" value="#{user.name}" />
	</h:form>
</h:body>
</html>
package demo;

import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.context.FacesContext;

/**
 * Created by JBoss Tools
 */
@ManagedBean(name = "user")
@RequestScoped
public class User {

	private String name;

	public String sayHello() {
		if (isNameIncorrect()) {
			FacesContext context = FacesContext.getCurrentInstance();
			context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Too small", "Can you write it a little bigger?"));
		}

		System.out.println(name);
		return null;
	}

	private boolean isNameIncorrect() {
		return "".equals(name.trim()) || name.length() < 3;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

Notice that we have the component “h:messages” and its ID is used in the “f:ajax” component. This code also works when you use the “h:message for=“YYY”” component.

What if we work now with comboboxes? Let us display a combobox that will contain 4 items when we have a name with less than 6 characters, and a list with more than 4 items if the typed name has more than 6 characters.

package demo;

import java.util.ArrayList;
import java.util.List;

import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.component.UISelectItems;
import javax.faces.component.html.HtmlSelectOneMenu;
import javax.faces.context.FacesContext;
import javax.faces.event.AjaxBehaviorEvent;

/**
 * Created by JBoss Tools
 */
@ManagedBean(name = "user")
@RequestScoped
public class User {

	private String name;

	private List<String> cars;

	private String selectedCar;
	private HtmlSelectOneMenu htmlSelectCars;

	private static final String SELECT_A_CAR = "Select One Car";

	public User() {
		cars = new ArrayList<String>();
	}

	public String sayHello() {
		if (isNameInCorrect()) {
			FacesContext context = FacesContext.getCurrentInstance();
			context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Too small", "Can you write it a little bigger?"));
		}

		System.out.println(name);
		return null;
	}

	private boolean isNameInCorrect() {
		return name == null || "".equals(name.trim()) || name.length() < 3;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void editMyCarsList(AjaxBehaviorEvent event) {
		if (htmlSelectCars == null) {
			htmlSelectCars = new HtmlSelectOneMenu();
		}

		htmlSelectCars.getChildren().clear();

		UISelectItems items = new UISelectItems();
		items.setValue(getCars());
		htmlSelectCars.getChildren().add(items);
	}

	public List<String> getCars() {
		cars.clear();

		cars.add(SELECT_A_CAR);

		if (!isNameInCorrect() && name.length() >= 6) {
			cars.add("Ferrari");
			cars.add("Porch");
			cars.add("Beetle");
			cars.add("Opala");
			cars.add("Passat");
			cars.add("Vectra");
			cars.add("Chevet");
			cars.add("Corvet");
		} else {
			cars.add("Ferrari");
			cars.add("Porch");
			cars.add("Beetle");
			cars.add("Opala");
		}

		return cars;
	}

	public void setCars(List<String> cars) {
		this.cars = cars;
	}

	public String getSelectedCar() {
		return selectedCar;
	}

	public void setSelectedCar(String selectedCar) {
		this.selectedCar = selectedCar;
	}

	public HtmlSelectOneMenu getHtmlSelectCars() {
		editMyCarsList(null);

		return htmlSelectCars;
	}

	public void setHtmlSelectCars(HtmlSelectOneMenu htmlSelectCars) {
		this.htmlSelectCars = htmlSelectCars;
	}
}

Take a look now at our page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">
<h:head>
</h:head>
<h:body>
	<h:form>
		<h:messages id="myMessage" globalOnly="true" showDetail="true" />
		Your Name: <h:inputText id="inputname" label="${msgs.prompt}" value="#{user.name}" />
		<br />
		<h:commandButton action="#{user.sayHello}" value="Display my name here, now!">
			<f:ajax render="myName myCars myMessage" execute="inputname" listener="#{user.editMyCarsList}" />
		</h:commandButton>
		<br />
		<br />
		<h:outputText id="myName" value="#{user.name}" />
		<br />
		<br />
		Choose your car: <h:selectOneMenu id="myCars" binding="#{user.htmlSelectCars}" value="#{user.selectedCar}" />
		<br />
		<br />
	</h:form>
</h:body>
</html>

Notice that our combobox items size is updated according to the typed name. At the end of this post I will talk more about why I used the binding attribute to the HtmlSelectOneMenu instead returning the List<String>.

As our last sample, let us create a combobox that will appear and disappear according to the selected value in the Car combobox.

Take a look at our ManagedBean:

package demo;

import java.util.ArrayList;
import java.util.List;

import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.component.UISelectItems;
import javax.faces.component.html.HtmlSelectOneMenu;
import javax.faces.context.FacesContext;
import javax.faces.event.AjaxBehaviorEvent;

/**
 * Created by JBoss Tools
 */
@ManagedBean(name = "user")
@RequestScoped
public class User {

	private String name;

	private List<String> cars;
	private List<String> colors;

	private String selectedCar;
	private String selectedColor;
	private HtmlSelectOneMenu htmlSelectCars;

	private static final String SELECT_A_CAR = "Select One Car";

	public User() {
		cars = new ArrayList<String>();
		colors = new ArrayList<String>();

		colors.add("Red");
		colors.add("Blue");
		colors.add("Orange");
		colors.add("Pink --> O.o");
	}

	public String sayHello() {
		if (isNameInCorrect()) {
			FacesContext context = FacesContext.getCurrentInstance();
			context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Too small", "Can you write it a little bigger?"));
		}

		System.out.println(name);
		return null;
	}

	private boolean isNameInCorrect() {
		return name == null || "".equals(name.trim()) || name.length() < 3;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void editMyCarsList(AjaxBehaviorEvent event) {
		if (htmlSelectCars == null) {
			htmlSelectCars = new HtmlSelectOneMenu();
		}

		htmlSelectCars.getChildren().clear();

		UISelectItems items = new UISelectItems();
		items.setValue(getCars());
		htmlSelectCars.getChildren().add(items);
	}

	public List<String> getCars() {
		cars.clear();

		cars.add(SELECT_A_CAR);

		if (!isNameInCorrect() && name.length() >= 6) {
			cars.add("Ferrari");
			cars.add("Porch");
			cars.add("Beetle");
			cars.add("Opala");
			cars.add("Passat");
			cars.add("Vectra");
			cars.add("Chevet");
			cars.add("Corvet");
		} else {
			cars.add("Ferrari");
			cars.add("Porch");
			cars.add("Beetle");
			cars.add("Opala");
		}

		return cars;
	}

	public void setCars(List<String> cars) {
		this.cars = cars;
	}

	public String getSelectedCar() {
		return selectedCar;
	}

	public void setSelectedCar(String selectedCar) {
		this.selectedCar = selectedCar;
	}

	public List<String> getColors() {
		return colors;
	}

	public void setColors(List<String> colors) {
		this.colors = colors;
	}

	public boolean isColorsAlloweToDisplay() {
		if (isNameInCorrect()) {
			return false;
		}

		if (selectedCar == null || selectedCar.trim().equals("") || selectedCar.equals(SELECT_A_CAR)) {
			return false;
		}

		return true;
	}

	public String getSelectedColor() {
		return selectedColor;
	}

	public void setSelectedColor(String selectedColor) {
		this.selectedColor = selectedColor;
	}

	public HtmlSelectOneMenu getHtmlSelectCars() {
		editMyCarsList(null);

		return htmlSelectCars;
	}

	public void setHtmlSelectCars(HtmlSelectOneMenu htmlSelectCars) {
		this.htmlSelectCars = htmlSelectCars;
	}
}

Our ManagedBean were lightly updated, we just added a List with a method that return a list of colors that will populate our combobox; we added also a method that will return a Boolean – true if the combobox is allowed to be displayed.

Check our new page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">
<h:head>
</h:head>
<h:body>
	<h:form>
		<h:messages id="myMessage" globalOnly="true" showDetail="true" />
		Your Name: <h:inputText id="inputname" label="${msgs.prompt}" value="#{user.name}" />
		<br />
		<h:commandButton action="#{user.sayHello}" value="Display my name here, now!">
			<f:ajax render="myName myCars myMessage myColors" execute="inputname" listener="#{user.editMyCarsList}" />
		</h:commandButton>
		<br />
		<br />
		<h:outputText id="myName" value="#{user.name}" />
		<br />
		<br />
		Choose your car:
		<h:selectOneMenu id="myCars" binding="#{user.htmlSelectCars}" value="#{user.selectedCar}">
			<f:ajax render="myColors" execute="inputname myCars"/>
		</h:selectOneMenu>
		<br />
		<br />
		<h:panelGroup id="myColors">
			<h:selectOneMenu value="#{user.selectedColor}" rendered="#{user.colorsAlloweToDisplay}">
				<f:selectItems value="#{user.colors}" />
			</h:selectOneMenu>
		</h:panelGroup>
	</h:form>
	</h:body>
</html>

I will talk about the code used in the post:

  • HtmlSelectOneMenu – I used the component instead a List because the JSF is not so good in render the components in the user screen (DOM Tree). If your combobox has 4 lines and by using ajax you add more lines to the List, the JSF/Ajax will not recognize the new added lines; you will be able to use only the older values. You can try to use the code with a List<String> instead using the HtmlSelectOneMenu and see the result; I spend like 3~4 hours to understand this by a lot of searches on the internet.
  • HtmlSelectOneMenu inside the “h:panelGroup” component – I did this because every time you want to render a non rendered component you need to update its container. If our selectOne were inside the same form of the car selectOne you would need to render all form.

Click here to download the code from this post.

I hope this post might help you.

If you have any question/doubt/suggestion just post it.

See you later. o_

JPA Uma Tabela por Classe

EXISTE UMA NOVA VERSÃO DESSE POST.
CLICK AQUI: http://uaihebert.com/?p=1622&page=14

Olá pessoal, tudo bem?

Vamos falar hoje em como criar uma tabela por classe em uma herança onde cada tabela tem apenas as colunas da sua classe. No último post foi visto como persistir toda a herança em uma tabela apenas.

Irei utilizar o mesmo exemplo do ultimo post já colocando aqui como deve ser o código correto para utilizar essa abordagem.

Ao final do post você encontrará o link para download do projeto de hoje.

Click nos links a seguir para ver o último post sobre o assunto ou qualquer outro post sobre JPA: JPA Uma tabela por herança, Ordernando listas e utilizando Map como atributo mapeado, @ManyToMany Unidirecional e Bidirecional, JPA @OneToMany e @ManyToOne Unidirecional e Bidirecional, @OneToOne Unidirecional e Bidirecional, Mapeando Duas Tabelas em uma Classe, Mapeando Datas (Date) e Enum, Chave Primária Composta, SequenceGenerator, TableGenerator – Chave Primária Simples, Auto Create Schema Script com: Ant, Hibernate 3 e JPA 2, Tutorial Hibernate 3 com JPA 2.

Veja como ficará nosso código:

package com.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;

@Entity
@Table(name="CAR")
@Inheritance(strategy=InheritanceType.JOINED)
public abstract class Car {
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private int id;
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}
}
package com.model;

import javax.persistence.Entity;

@Entity
public class Beetle extends Car {
	private int gasCapacity;

	public void talk(){
		System.out.println("Herbie!");
	}

	public int getGasCapacity() {
		return gasCapacity;
	}

	public void setGasCapacity(int gasCapacity) {
		this.gasCapacity = gasCapacity;
	}
}
package com.model;

import javax.persistence.Entity;

@Entity
public class Ferrari extends Car {
	private int model;

	// The newest new models will fly... some day! =P
	public void fly() {

	}

	public int getModel() {
		return model;
	}

	public void setModel(int model) {
		this.model = model;
	}
}

Após executar a classe Main, veja como nossas tabelas ficaram:

Agora para cada tabela temos os dados pertinentes a sua respectiva classe.

Gostaria de deixar apenas uma alerta.

Repare que os IDs de todas as nossas classes têm o nome “id”. Caso sua classe tenha um id diferente de sua super classe, será necessário mapear de modo diferente.

Imagine que nossa classe Beetle teria seu id como beetleId, nosso mapeamento teria que apresentar a seguinte anotação a mais:

package com.model;

import javax.persistence.Entity;
import javax.persistence.PrimaryKeyJoinColumn;

@Entity
@PrimaryKeyJoinColumn(name="beetleId")
public class Beetle extends Car {

	private int gasCapacity;

	public void talk() {
		System.out.println("Herbie!");
	}

	public int getGasCapacity() {
		return gasCapacity;
	}

	public void setGasCapacity(int gasCapacity) {
		this.gasCapacity = gasCapacity;
	}
}


Repare que a chave primária agora tem o id com nome diferente da classe Car e Ferrari.

Lembre-se: se sua classe filha tem o nome do campo de id diferente da classe pai, você terá que usar a anotação @PrimaryKeyJoinColumn onde o campo “name” indica qual a coluna a ser mapeada. E caso você queira alterar qual a coluna de ligação na classe pai, basta você utilizar o parâmetro “referencedColumnName”.

Finalizando, vejamos vantagens e desvantagens:

  • Vantagem: Você poderá ter campos not null em sua classe ao contrário da estratégia de uma tabela para toda a herança. E tem vantagens em sobre o próximo tipo mapeamento que veremos: “Uma tabela para cada Sub-Classe com todos os campos da herança”. E segundo o livro “Enterprise JavaBens 3.0” essa estratégia será melhor do que a “Uma tabela para cada Sub-Classe com todos os campos da herança” caso SQL Union não seja suportado por seu banco de dados.
  • Desvantagem: Seu desempenho é pior do que a estratégia de Uma Tabela Para toda a Herança.

Clique aqui para fazer o download do post de hoje. Não esqueça de alterar a senha de conexão e o usuário.

Espero que esse post possa te ajudar.

Qualquer dúvida/colocação basta postar.

Até! o_

JPA One Table Per SubClass

THERE IS A NEW VERSION OF THIS POST.
CLICK HERE: http://uaihebert.com/?p=1674&page=14

Hello, how are you?

Let us talk today about how to create a database table per subclass of your hierarchy; each table will have only the data of its class. In the last post we had seen how to persist all hierarchy in one table.

I will use the same code from the last post but with the code already edited.

In the end of this post, you will find the source code of the application.

In the following links you can find the last post about this subject and the others post about JPA: JPA Single Table per Class Hierarchy, @ManyToMany Unidirectional and Bidirectional, @OneToMany and @ManyToOne Unidirectional and Bidirectional, OneToOne Unidirectional and Bidirectional, Mapping two Tables in one Class, Mapping Date and Enum, Composite Primary-Key, SequenceGenerator, TableGenerator – Simple Primay Key, Auto Create Schema Script with: Ant, Hibernate 3 and JPA 2, Tutorial Hibernate 3 with JPA 2.

Take a look and see how the code has to be:

package com.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;

@Entity
@Table(name="CAR")
@Inheritance(strategy=InheritanceType.JOINED)
public abstract class Car {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private int id;
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}
package com.model;

import javax.persistence.Entity;

@Entity
public class Beetle extends Car {
    private int gasCapacity;

    public void talk(){
        System.out.println("Herbie!");
    }

    public int getGasCapacity() {
        return gasCapacity;
    }

    public void setGasCapacity(int gasCapacity) {
        this.gasCapacity = gasCapacity;
    }
}
package com.model;

import javax.persistence.Entity;

@Entity
public class Ferrari extends Car {
    private int model;

    // The newest new models will fly... some day! =P
    public void fly() {

    }

    public int getModel() {
        return model;
    }

    public void setModel(int model) {
        this.model = model;
    }
}

After we run the Main class let us take a look and see how our tables will look like:

Now each table has the data of its respective class.

I would like to call your attention to a situation.

Notice that every class of our hierarchy has the id with the same name “id”. If your subclass has a different id attribute name, you will have to map this with a different annotation.

Imagine that the Beetle class has its table id named as “beetleId”; we need to add an annotation in the Beetle class:

package com.model;

import javax.persistence.Entity;
import javax.persistence.PrimaryKeyJoinColumn;

@Entity
@PrimaryKeyJoinColumn(name="beetleId")
public class Beetle extends Car {

    private int gasCapacity;

    public void talk() {
        System.out.println("Herbie!");
    }

    public int getGasCapacity() {
        return gasCapacity;
    }

    public void setGasCapacity(int gasCapacity) {
        this.gasCapacity = gasCapacity;
    }
}


Notice that the primary key now has a different name than Car and Ferrari.

Remember this: if your subclass id field has a name that is not the same of your super class, you will need to use the @PrimaryKeyJoinColumn annotation; the “name” parameter indicates which column will be mapped. If you need to set up the Super Class id column you can use the parameter: “referencedColumnName”.

Let us see now the advantages and the disadvantages of this approach:

  • Advantage: You will be able to have null attributes in your subclasses; you would not be able to do this if you use the one table for all hierarchy strategy. There is a good sql performance over the next pattern that we will see: “One table with all fields per Subclasses”. In the book “Enterprise JavaBens 3.0” the author says that this approach is better than “One table with all fields per Subclasses” only if the database SQL Union is not supported by your database.
  • Disadvantage: It has a lower performance than “One table to all hierarchy strategy”.

Click here to download the source code of this post. Just do not forget to change the user/password.

I hope this post may help you.

If you have any comments/questions just post it.

See you later! o_

JSF Exibindo Objeto e Mensagens após Redirect

Olá, tudo bem?

Vamos falar hoje sobre como enviar uma mensagem utilizando JSF após um redirect. O redirect acaba por eliminar os dados da requisição original impossibilitando a exibição de alguma informação.

Essa situação é muito comum quando se utiliza Ajax para navegar entre páginas, e uma alternativa para forçar a troca de páginas, é utilizar o redirect.

Outros posts de JSF/Web você poderá encontrar aqui: Validação de Login de Usuário com JSF e JAASJSF: Converter and Bean AutoCompleteJSF – Hello World, AutoCompleteTratando Exceções em uma Aplicação WebAutenticação de Usuários (Filter/Servlet)Criando um WebServer.

Ao final do post você poderá fazer o download do código do post de hoje.

Vou abordar dois modos para envio de mensagem e/ou objeto após um redirect. Utilizando o Flash (API do JSF 2.0) ou então criando uma classe para controlar o os ciclos (JSF 1.2 para cima).

Abaixo, as imagens mostrando a navegação e a classe do nosso projeto.

/*******************************************************************************
 * Copyright (c) 2010 Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is made available under the terms of the
 * Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Red Hat, Inc. - initial API and implementation
 ******************************************************************************/
package demo;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

/**
 * Created by JBoss Tools
 */
@ManagedBean(name="user")
@RequestScoped
public class User {
	private String name;

	public User() {
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String sayHello() {
		return "greeting";
	}
}
<?xml version="1.0" encoding="UTF-8"?>

<faces-config
    xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
	version="2.0">

	<navigation-rule>
		<from-view-id>/pages/inputname.xhtml</from-view-id>
		<navigation-case>
			<from-outcome>greeting</from-outcome>
			<to-view-id>/pages/greeting.xhtml</to-view-id>
		</navigation-case>
	</navigation-rule>

    <application>
        <resource-bundle>
            <base-name>resources</base-name>
            <var>msgs</var>
        </resource-bundle>
    </application>
</faces-config>

Agora, faremos uma pequena alteração para que a ação de ir para página final seja através de um redirect. O código abaixo demonstra como ficará nosso faces-config.xml e o resultado dessa alteração (note a linha 14).

<?xml version="1.0" encoding="UTF-8"?>

<faces-config
    xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
	version="2.0">

	<navigation-rule>
		<from-view-id>/pages/inputname.xhtml</from-view-id>
		<navigation-case>
			<from-outcome>greeting</from-outcome>
			<to-view-id>/pages/greeting.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
	</navigation-rule>

    <application>
        <resource-bundle>
            <base-name>resources</base-name>
            <var>msgs</var>
        </resource-bundle>
    </application>
</faces-config>


Onde o Peter foi parar? É isso que acontece quando utilizamos um redirect e o objeto não está na sessão. Para consertar esse “problema” vamos utilizar o não tão famoso Flash Scope.

Veja como o código da classe User ficará (repare nas linhas 38,39):

/*******************************************************************************
 * Copyright (c) 2010 Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is made available under the terms of the
 * Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Red Hat, Inc. - initial API and implementation
 ******************************************************************************/
package demo;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

import com.sun.faces.context.flash.ELFlash;

/**
 * Created by JBoss Tools
 */
@ManagedBean(name="user")
@RequestScoped
public class User {
	private String name;

	public User() {
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String sayHello() {
		// Put the object inside the flash scope
		ELFlash.getFlash().put("user", this);
		return "greeting";
	}
}

E para acessar o objeto usuário (user), basta alterar a página:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">

	<ui:composition template="/templates/common.xhtml">
			<ui:define name="pageTitle">Greeting to User</ui:define>
			<ui:define name="pageHeader">Greeting Page</ui:define>
			<ui:define name="body">
				#{msgs.greeting} #{flash.user.name}!
			</ui:define>
	</ui:composition>
</html>

E o Peter voltará a aparecer.

O escopo Flash nada mais faz do que guarda o objeto como se fosse sessão. Atenção para o fato de que após a sua utilização o objeto “user” será removido do escopo. Como manter o objeto no Flash Scope mesmo após um redirect? Fácil, basta você trocar o comando

      #{flash.user.name}

por

      #{flash.keep.user.name}

Desse modo o objeto usuário irá continuar dentro do Flash Scope.

Para JSF 1.2: Infelizmente para se manter um objeto em uma requisição após um redirect no JSF 1.2 (até o dia de hoje), será necessário fazer o controle do objeto na sessão. =/ MAS, caso você queira enviar apenas um alerta para a próxima tela existe outra solução.

Vou criar uma nova página para exibir uma mensagem em forma de alerta “<h:messages/>“. Veja como nosso código ficará:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:ez="http://java.sun.com/jsf/composite/demo">

	<ui:composition template="/templates/common.xhtml">

		<ui:define name="pageTitle">Input User Name</ui:define>

		<ui:define name="pageHeader">JSF 2 Hello Application</ui:define>

		<ui:define name="body">
			<h:message showSummary="true" showDetail="false" style="color: red; font-weight: bold;" for="inputname" />
			<ez:input id="inputname" label="${msgs.prompt}" value="#{user.name}" action="#{user.sayHello}" submitlabel="Say Hello"/>
			<ez:input id="inputname2" label="${msgs.prompt}" value="#{user.name}" action="#{user.sayHelloWithAlert}" submitlabel="Say Hello With Alert"/>
		</ui:define>
	</ui:composition>
</html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">

	<ui:composition template="/templates/common.xhtml">
			<ui:define name="pageTitle">Greeting to User With alert</ui:define>
			<ui:define name="pageHeader">Greeting Page With alert</ui:define>
			<ui:define name="body">
				<h:messages />
			</ui:define>
	</ui:composition>
</html>
/*******************************************************************************
 * Copyright (c) 2010 Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is made available under the terms of the
 * Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Red Hat, Inc. - initial API and implementation
 ******************************************************************************/
package demo;

import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.context.FacesContext;

import com.sun.faces.context.flash.ELFlash;

/**
 * Created by JBoss Tools
 */
@ManagedBean(name="user")
@RequestScoped
public class User {
	private String name;

	public User() {
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String sayHello() {
		// Put the object inside the flash scope
		ELFlash.getFlash().put("user", this);
		return "greeting";
	}

	public String sayHelloWithAlert() {
		String message = "Hello : " + name;
		FacesContext context = FacesContext.getCurrentInstance();

		context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, message, message));

		return "greetingWithAlert";
	}
}
<?xml version="1.0" encoding="UTF-8"?>

<faces-config
    xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
	version="2.0">

	<navigation-rule>
		<from-view-id>/pages/inputname.xhtml</from-view-id>
		<navigation-case>
			<from-outcome>greeting</from-outcome>
			<to-view-id>/pages/greeting.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
		<navigation-case>
			<from-outcome>greetingWithAlert</from-outcome>
			<to-view-id>/pages/greetingWithAlert.xhtml</to-view-id>
		</navigation-case>
	</navigation-rule>

    <application>
        <resource-bundle>
            <base-name>resources</base-name>
            <var>msgs</var>
        </resource-bundle>
    </application>
</faces-config>


E caso utilizemos o redirect a mensagem nossa mensagem não será exibida. Veja o que acontece:

<?xml version="1.0" encoding="UTF-8"?>

<faces-config
    xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
	version="2.0">

	<navigation-rule>
		<from-view-id>/pages/inputname.xhtml</from-view-id>
		<navigation-case>
			<from-outcome>greeting</from-outcome>
			<to-view-id>/pages/greeting.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
		<navigation-case>
			<from-outcome>greetingWithAlert</from-outcome>
			<to-view-id>/pages/greetingWithAlert.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
	</navigation-rule>

    <application>
        <resource-bundle>
            <base-name>resources</base-name>
            <var>msgs</var>
        </resource-bundle>
    </application>
</faces-config>


A solução abaixo eu encontrei no site: “ocpsoft.com/java/persist-and-pass-facesmessages-over-page-redirects/”.

O que a classe faz é adicionar sua mensagem ao contexto de sessão, exibe e depois remove a mensagem da sessão. Uma solução bem útil quando se quer prolongar as mensagens.

package demo;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

/**
 * Enables messages to be rendered on different pages from which they were set.
 *
 * After each phase where messages may be added, this moves the messages
 * from the page-scoped FacesContext to the session-scoped session map.
 *
 * Before messages are rendered, this moves the messages from the
 * session-scoped session map back to the page-scoped FacesContext.
 *
 * Only global messages, not associated with a particular component, are
 * moved. Component messages cannot be rendered on pages other than the one on
 * which they were added.
 *
 * To enable multi-page messages support, add a <code>lifecycle</code> block to your
 * faces-config.xml file. That block should contain a single
 * <code>phase-listener</code> block containing the fully-qualified classname
 * of this file.
 *
 * @author Jesse Wilson jesse[AT]odel.on.ca
 * @secondaryAuthor Lincoln Baxter III lincoln[AT]ocpsoft.com
 */
public class MultiPageMessagesSupport implements PhaseListener
{

    private static final long serialVersionUID = 1250469273857785274L;
    private static final String sessionToken = "MULTI_PAGE_MESSAGES_SUPPORT";

    public PhaseId getPhaseId()
    {
        return PhaseId.ANY_PHASE;
    }

    /*
     * Check to see if we are "naturally" in the RENDER_RESPONSE phase. If we
     * have arrived here and the response is already complete, then the page is
     * not going to show up: don't display messages yet.
     */
    // TODO: Blog this (MultiPageMessagesSupport)
    public void beforePhase(final PhaseEvent event)
    {
        FacesContext facesContext = event.getFacesContext();
        this.saveMessages(facesContext);

        if (PhaseId.RENDER_RESPONSE.equals(event.getPhaseId()))
        {
            if (!facesContext.getResponseComplete())
            {
                this.restoreMessages(facesContext);
            }
        }
    }

    /*
     * Save messages into the session after every phase.
     */
    public void afterPhase(final PhaseEvent event)
    {
        if (!PhaseId.RENDER_RESPONSE.equals(event.getPhaseId()))
        {
            FacesContext facesContext = event.getFacesContext();
            this.saveMessages(facesContext);
        }
    }

    @SuppressWarnings("unchecked")
    private int saveMessages(final FacesContext facesContext)
    {
        List<FacesMessage> messages = new ArrayList<FacesMessage>();
        for (Iterator<FacesMessage> iter = facesContext.getMessages(null); iter.hasNext();)
        {
            messages.add(iter.next());
            iter.remove();
        }

        if (messages.size() == 0)
        {
            return 0;
        }

        Map<String, Object> sessionMap = facesContext.getExternalContext().getSessionMap();
        List<FacesMessage> existingMessages = (List<FacesMessage>) sessionMap.get(sessionToken);
        if (existingMessages != null)
        {
            existingMessages.addAll(messages);
        }
        else
        {
            sessionMap.put(sessionToken, messages);
        }
        return messages.size();
    }

    @SuppressWarnings("unchecked")
    private int restoreMessages(final FacesContext facesContext)
    {
        Map<String, Object> sessionMap = facesContext.getExternalContext().getSessionMap();
        List<FacesMessage> messages = (List<FacesMessage>) sessionMap.remove(sessionToken);

        if (messages == null)
        {
            return 0;
        }

        int restoredCount = messages.size();
        for (Object element : messages)
        {
            facesContext.addMessage(null, (FacesMessage) element);
        }
        return restoredCount;
    }
}

E por último precisamos alterar o “faces-config.xml”:

<?xml version="1.0" encoding="UTF-8"?>

<faces-config
    xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
	version="2.0">

	<navigation-rule>
		<from-view-id>/pages/inputname.xhtml</from-view-id>
		<navigation-case>
			<from-outcome>greeting</from-outcome>
			<to-view-id>/pages/greeting.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
		<navigation-case>
			<from-outcome>greetingWithAlert</from-outcome>
			<to-view-id>/pages/greetingWithAlert.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
	</navigation-rule>
	<lifecycle>
		<phase-listener>demo.MultiPageMessagesSupport</phase-listener>
	</lifecycle>	

    <application>
        <resource-bundle>
            <base-name>resources</base-name>
            <var>msgs</var>
        </resource-bundle>
    </application>
</faces-config>

E ao executar novamente nosso sistema, veja quem voltou… Peter! o/

Para realizar o download do código fonte do post, clique aqui. Para executar o código você precisará ter o JBoss 6 configurado em seu Eclipse, para isso, terá que instalar o JBoss Tools. Nesse post, a instalação e configuração são abordadas: User Login Validation with JAAS and JSF.

Espero que o post de hoje possa te ajudar.

Qualquer dúvida ou colocação basta postar.

Esse post foi baseado:

  • Livro: “Core JavaServer Faces” Capítulo: “Redirection and Flash”
  • Código gerado pela ferramenta JBoss Tools
  • Código da classe de mensagem persistida “ocpsoft.com/”

Até a próxima! o_

JSF Persisting Objects and Messages after a Redirect

Hello, how are you?

Let us talk today about how to send messages with JSF after a redirect action. The redirect action will not carry any data from the first request; sometimes you will not have the value to be displayed.

We often see this happen in an application with a heavy use of Ajax; sometimes to navigate between the pages you will need to use the redirect command.

The other posts of JSF/Web you can find in here: User Login Validation with JAAS and JSFJSF: Converter and Bean AutoComplete, JSF – Hello World, Auto Complete, Handling Exceptions on a WebApp, User Authentication (Filter/Servlet), Creating a WebServer.

In the end of the post you will find the link to download the source code of today’s post.

Today we will see two ways to send an object and/or a message after a redirect. We will use the Flash Scope (JSF 2.0 API) or creating a class to control the JSF phases (works on JSF 1.2 or a higher version).

Bellow you will see some images of the project screen shots and a few codes:

/*******************************************************************************
 * Copyright (c) 2010 Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is made available under the terms of the
 * Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Red Hat, Inc. - initial API and implementation
 ******************************************************************************/
package demo;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

/**
 * Created by JBoss Tools
 */
@ManagedBean(name="user")
@RequestScoped
public class User {
	private String name;

	public User() {
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String sayHello() {
		return "greeting";
	}
}
<?xml version="1.0" encoding="UTF-8"?>

<faces-config
    xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
	version="2.0">

	<navigation-rule>
		<from-view-id>/pages/inputname.xhtml</from-view-id>
		<navigation-case>
			<from-outcome>greeting</from-outcome>
			<to-view-id>/pages/greeting.xhtml</to-view-id>
		</navigation-case>
	</navigation-rule>

    <application>
        <resource-bundle>
            <base-name>resources</base-name>
            <var>msgs</var>
        </resource-bundle>
    </application>
</faces-config>

I will edit now the “faces-config.xml”; we will use the redirect to navigate between the pages. The code bellow will show how our file will be (take a look at the line 14):

<?xml version="1.0" encoding="UTF-8"?>

<faces-config
    xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
	version="2.0">

	<navigation-rule>
		<from-view-id>/pages/inputname.xhtml</from-view-id>
		<navigation-case>
			<from-outcome>greeting</from-outcome>
			<to-view-id>/pages/greeting.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
	</navigation-rule>

    <application>
        <resource-bundle>
            <base-name>resources</base-name>
            <var>msgs</var>
        </resource-bundle>
    </application>
</faces-config>


Where is Peter? That is what happens when we use the redirect and the object is not in the session scope. To fix this “problem” we will use the not so popular Flash Scope.

Let us edit the User class (take a look at the lines 38, 39):

/*******************************************************************************
 * Copyright (c) 2010 Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is made available under the terms of the
 * Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Red Hat, Inc. - initial API and implementation
 ******************************************************************************/
package demo;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

import com.sun.faces.context.flash.ELFlash;

/**
 * Created by JBoss Tools
 */
@ManagedBean(name="user")
@RequestScoped
public class User {
	private String name;

	public User() {
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String sayHello() {
		// Put the object inside the flash scope
		ELFlash.getFlash().put("user", this);
		return "greeting";
	}
}

And to access the user object in the Flash Scope, you just have to edit your page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">

	<ui:composition template="/templates/common.xhtml">
			<ui:define name="pageTitle">Greeting to User</ui:define>
			<ui:define name="pageHeader">Greeting Page</ui:define>
			<ui:define name="body">
				#{msgs.greeting} #{flash.user.name}!
			</ui:define>
	</ui:composition>
</html>

Now we will be able to meet Peter again:

The Flash scope keeps the object just like if it was a session scope. Pay attention to the fact that after you use the object “user” that is stored inside the flash scope, the object will be removed. How can you keep an object inside the Flash Scope after we use it? Piece of cake, just edit the invocation:

      #{flash.user.name}

For

      #{flash.keep.user.name}

If you use the last line your object will remain inside the Flash Scope.

To JSF 1.2: Unfortunately the only way to keep an object / message after a redirect (until today) is controlling it inside the session. =/ BUT, if you want just to send an alert (message) to the next screen there is another solution.

I will create another page to display the welcome message as an alert “<h:messages/>”. Take a look at our new code:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:ez="http://java.sun.com/jsf/composite/demo">

	<ui:composition template="/templates/common.xhtml">

		<ui:define name="pageTitle">Input User Name</ui:define>

		<ui:define name="pageHeader">JSF 2 Hello Application</ui:define>

		<ui:define name="body">
			<h:message showSummary="true" showDetail="false" style="color: red; font-weight: bold;" for="inputname" />
			<ez:input id="inputname" label="${msgs.prompt}" value="#{user.name}" action="#{user.sayHello}" submitlabel="Say Hello"/>
			<ez:input id="inputname2" label="${msgs.prompt}" value="#{user.name}" action="#{user.sayHelloWithAlert}" submitlabel="Say Hello With Alert"/>
		</ui:define>
	</ui:composition>
</html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">

	<ui:composition template="/templates/common.xhtml">
			<ui:define name="pageTitle">Greeting to User With alert</ui:define>
			<ui:define name="pageHeader">Greeting Page With alert</ui:define>
			<ui:define name="body">
				<h:messages />
			</ui:define>
	</ui:composition>
</html>
/*******************************************************************************
 * Copyright (c) 2010 Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is made available under the terms of the
 * Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Red Hat, Inc. - initial API and implementation
 ******************************************************************************/
package demo;

import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.context.FacesContext;

import com.sun.faces.context.flash.ELFlash;

/**
 * Created by JBoss Tools
 */
@ManagedBean(name="user")
@RequestScoped
public class User {
	private String name;

	public User() {
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String sayHello() {
		// Put the object inside the flash scope
		ELFlash.getFlash().put("user", this);
		return "greeting";
	}

	public String sayHelloWithAlert() {
		String message = "Hello : " + name;
		FacesContext context = FacesContext.getCurrentInstance();

		context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, message, message));

		return "greetingWithAlert";
	}
}
<?xml version="1.0" encoding="UTF-8"?>

<faces-config
    xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
	version="2.0">

	<navigation-rule>
		<from-view-id>/pages/inputname.xhtml</from-view-id>
		<navigation-case>
			<from-outcome>greeting</from-outcome>
			<to-view-id>/pages/greeting.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
		<navigation-case>
			<from-outcome>greetingWithAlert</from-outcome>
			<to-view-id>/pages/greetingWithAlert.xhtml</to-view-id>
		</navigation-case>
	</navigation-rule>

    <application>
        <resource-bundle>
            <base-name>resources</base-name>
            <var>msgs</var>
        </resource-bundle>
    </application>
</faces-config>


But, if we use the redirect action our message will not be displayed:

<?xml version="1.0" encoding="UTF-8"?>

<faces-config
    xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
	version="2.0">

	<navigation-rule>
		<from-view-id>/pages/inputname.xhtml</from-view-id>
		<navigation-case>
			<from-outcome>greeting</from-outcome>
			<to-view-id>/pages/greeting.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
		<navigation-case>
			<from-outcome>greetingWithAlert</from-outcome>
			<to-view-id>/pages/greetingWithAlert.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
	</navigation-rule>

    <application>
        <resource-bundle>
            <base-name>resources</base-name>
            <var>msgs</var>
        </resource-bundle>
    </application>
</faces-config>


The solution bellow I found in the site: “ocpsoft.com/java/persist-and-pass-facesmessages-over-page-redirects/”.

The class bellow will get your message and store it in the session scope. Once the message was displayed it will be removed from the session scope. This is a very useful solution to prolong the scope of a message:

package demo;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

/**
 * Enables messages to be rendered on different pages from which they were set.
 *
 * After each phase where messages may be added, this moves the messages
 * from the page-scoped FacesContext to the session-scoped session map.
 *
 * Before messages are rendered, this moves the messages from the
 * session-scoped session map back to the page-scoped FacesContext.
 *
 * Only global messages, not associated with a particular component, are
 * moved. Component messages cannot be rendered on pages other than the one on
 * which they were added.
 *
 * To enable multi-page messages support, add a <code>lifecycle</code> block to your
 * faces-config.xml file. That block should contain a single
 * <code>phase-listener</code> block containing the fully-qualified classname
 * of this file.
 *
 * @author Jesse Wilson jesse[AT]odel.on.ca
 * @secondaryAuthor Lincoln Baxter III lincoln[AT]ocpsoft.com
 */
public class MultiPageMessagesSupport implements PhaseListener
{

    private static final long serialVersionUID = 1250469273857785274L;
    private static final String sessionToken = "MULTI_PAGE_MESSAGES_SUPPORT";

    public PhaseId getPhaseId()
    {
        return PhaseId.ANY_PHASE;
    }

    /*
     * Check to see if we are "naturally" in the RENDER_RESPONSE phase. If we
     * have arrived here and the response is already complete, then the page is
     * not going to show up: don't display messages yet.
     */
    // TODO: Blog this (MultiPageMessagesSupport)
    public void beforePhase(final PhaseEvent event)
    {
        FacesContext facesContext = event.getFacesContext();
        this.saveMessages(facesContext);

        if (PhaseId.RENDER_RESPONSE.equals(event.getPhaseId()))
        {
            if (!facesContext.getResponseComplete())
            {
                this.restoreMessages(facesContext);
            }
        }
    }

    /*
     * Save messages into the session after every phase.
     */
    public void afterPhase(final PhaseEvent event)
    {
        if (!PhaseId.RENDER_RESPONSE.equals(event.getPhaseId()))
        {
            FacesContext facesContext = event.getFacesContext();
            this.saveMessages(facesContext);
        }
    }

    @SuppressWarnings("unchecked")
    private int saveMessages(final FacesContext facesContext)
    {
        List<FacesMessage> messages = new ArrayList<FacesMessage>();
        for (Iterator<FacesMessage> iter = facesContext.getMessages(null); iter.hasNext();)
        {
            messages.add(iter.next());
            iter.remove();
        }

        if (messages.size() == 0)
        {
            return 0;
        }

        Map<String, Object> sessionMap = facesContext.getExternalContext().getSessionMap();
        List<FacesMessage> existingMessages = (List<FacesMessage>) sessionMap.get(sessionToken);
        if (existingMessages != null)
        {
            existingMessages.addAll(messages);
        }
        else
        {
            sessionMap.put(sessionToken, messages);
        }
        return messages.size();
    }

    @SuppressWarnings("unchecked")
    private int restoreMessages(final FacesContext facesContext)
    {
        Map<String, Object> sessionMap = facesContext.getExternalContext().getSessionMap();
        List<FacesMessage> messages = (List<FacesMessage>) sessionMap.remove(sessionToken);

        if (messages == null)
        {
            return 0;
        }

        int restoredCount = messages.size();
        for (Object element : messages)
        {
            facesContext.addMessage(null, (FacesMessage) element);
        }
        return restoredCount;
    }
}

And as our last action, let us edit the “faces-config.xml”:

<?xml version="1.0" encoding="UTF-8"?>

<faces-config
    xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
	version="2.0">

	<navigation-rule>
		<from-view-id>/pages/inputname.xhtml</from-view-id>
		<navigation-case>
			<from-outcome>greeting</from-outcome>
			<to-view-id>/pages/greeting.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
		<navigation-case>
			<from-outcome>greetingWithAlert</from-outcome>
			<to-view-id>/pages/greetingWithAlert.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
	</navigation-rule>
	<lifecycle>
		<phase-listener>demo.MultiPageMessagesSupport</phase-listener>
	</lifecycle>	

    <application>
        <resource-bundle>
            <base-name>resources</base-name>
            <var>msgs</var>
        </resource-bundle>
    </application>
</faces-config>

Deploy the application again and guess who is back? Peter! o/

If you want to download the source code of this post, click here. To run this code, you will need to have the JBoss 6 configured in your Eclipse. But to set up the JBoss 6 in you Eclipse you will need the JBoss tools. If you need some help to install the JBoss Tools check this post: User Login Validation with JAAS and JSF.

I hope this post might help you.

If you have any question or comment just post it.

This post was based on:

  • Book: “Core JavaServer Faces” Chapter: “Redirection and Flash”
  • JSF source code generated by the JBoss tools
  • The class to persist the message through the redirect: “ocpsoft.com/”

See you soon! o_

JPA Single Table per Class Hierarchy

THERE IS A NEW VERSION OF THIS POST.
CLICK HERE: http://uaihebert.com/?p=1674&page=13

Hello, how are you?

Let us talk today about how to persist in the database table classes with hierarchy. We will start with the “Single Table per Class Hierarchy” mode.

The others posts about JPA you can find in here: @ManyToMany Unidirectional and Bidirectional, @OneToMany and @ManyToOne Unidirectional and Bidirectional, OneToOne Unidirectional and Bidirectional, Mapping two Tables in one Class, Mapping Date and Enum, Composite Primary-Key, SequenceGenerator, TableGenerator – Simple Primay Key, Auto Create Schema Script with: Ant, Hibernate 3 and JPA 2, Tutorial Hibernate 3 with JPA 2.

In the end of this post you will find the source code of this post.

We will use a car project where we will have a Main class do compile and other two class extending a super class. Let us see the initial code:

package com.model;

public abstract class Car {
	private int id;
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}
}
package com.model;

public class Beetle extends Car {
	private int gasCapacity;

	public void talk(){
		System.out.println("Herbie!");
	}

	public int getGasCapacity() {
		return gasCapacity;
	}

	public void setGasCapacity(int gasCapacity) {
		this.gasCapacity = gasCapacity;
	}

}
package com.model;

public class Ferrari extends Car {
	private int model;

	// The newest new models will fly... some day! =P
	public void fly() {

	}

	public int getModel() {
		return model;
	}

	public void setModel(int model) {
		this.model = model;
	}
}

We have a simple model with three classes only; we will need to persist our class’s data. Today I will use the Postgres database as database, just to display how our data will be stored in the database.

Let us edit our model classes and add the JPA annotations:

package com.model;

import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;

@Entity
@Table(name="CAR")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="FROM_CLASS", discriminatorType=DiscriminatorType.STRING)
public abstract class Car {
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private int id;
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}
}
package com.model;

import javax.persistence.Entity;

@Entity
public class Beetle extends Car {
	private int gasCapacity;

	public void talk(){
		System.out.println("Herbie!");
	}

	public int getGasCapacity() {
		return gasCapacity;
	}

	public void setGasCapacity(int gasCapacity) {
		this.gasCapacity = gasCapacity;
	}
}
package com.model;

import javax.persistence.Entity;

@Entity
public class Ferrari extends Car {
	private int model;

	// The newest new models will fly... some day! =P
	public void fly() {

	}

	public int getModel() {
		return model;
	}

	public void setModel(int model) {
		this.model = model;
	}
}

I will talk a little bit about the code above:

  • The “@Inheritance” indicates to the JPA which model of hierarchy persistence we will use.
  • The “@DiscriminatorColumn” it is used to store a column name that will describe to which column that data line belongs to.

With the class bellow we will insert the data in the database. Run it and let us take a look in the result:

package com;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import com.model.Beetle;
import com.model.Ferrari;

public class Main {

	public static void main(String[] args) {
		EntityManagerFactory emf = Persistence.createEntityManagerFactory("Database");
		EntityManager em = emf.createEntityManager();

		em.getTransaction().begin();

		try {
			Ferrari ferrari = new Ferrari();
			ferrari.setModel(455);
			ferrari.setName("WWJD");

			Beetle beetle = new Beetle();
			beetle.setGasCapacity(46);
			beetle.setName("Heeerbie");

			em.persist(ferrari);
			em.persist(beetle);
		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			em.getTransaction().commit();
			emf.close();
		}

		System.out.println("It is over");
	}

}


Notice that in our table appeared a column named “from_clas” that indicates to which class that line belongs to.

To finish our post, what could we do to change that column value? What if we want to change how the name of our class will be persisted? What if we want CAR_FERRARI instead of Ferrari?

Let us edit our class and leave it like the code below:

package com.model;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue(value="CAR_BEETLE")
public class Beetle extends Car {
	private int gasCapacity;

	public void talk(){
		System.out.println("Herbie!");
	}

	public int getGasCapacity() {
		return gasCapacity;
	}

	public void setGasCapacity(int gasCapacity) {
		this.gasCapacity = gasCapacity;
	}
}
package com.model;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue(value="CAR_FERRARI")
public class Ferrari extends Car {
	private int model;

	// The newest new models will fly... some day! =P
	public void fly() {

	}

	public int getModel() {
		return model;
	}

	public void setModel(int model) {
		this.model = model;
	}
}

Run the Main class again and check the result:

Take a closer look and notice that the line 1 and 2 hold the older values; lines 3 and 4 already has the new values that we defined in the annotation “@DiscriminatorValue”.

Let us talk now about the Advantages and Disadvantages:

  • Advantages: The system will have a better performance; the JPA will not need to do join in all classes tables to gather the information. To those developers that are not familiar with this kind of persistence it will be easy to check the data in the tables.
  • Disadvantages: Your subclasses can not have any “not null” attributes. If the Beetle class has a not null attribute and you call the persist method on the Ferrari object, the database would raise an Exception.

Click here to download the code from this post. To run the code you will need the Postgres database running with a schema named SingleTable. Do not forget to change the user/password. ^o^

I hope this post can be useful to you.

If you have any question/comment just post it.

See you soon. o_

JPA Uma tabela por herança

EXISTE UMA NOVA VERSÃO DESSE POST.
CLICK AQUI: http://uaihebert.com/?p=1622&page=13

Olá, tudo bem?

Vamos falar hoje sobre como salvar nas tabelas que envolvem herança. Vamos começar pelo modo “Single Table per Class Hierarchy” (Uma tabela para toda herança).

Os outros assuntos já tratados sobre JPA: Ordernando listas e utilizando Map como atributo mapeado, @ManyToMany Unidirecional e Bidirecional, JPA @OneToMany e @ManyToOne Unidirecional e Bidirecional, @OneToOne Unidirecional e Bidirecional, Mapeando Duas Tabelas em uma Classe, Mapeando Datas (Date) e Enum, Chave Primária Composta, SequenceGenerator, TableGenerator – Chave Primária Simples, Auto Create Schema Script com: Ant, Hibernate 3 e JPA 2, Tutorial Hibernate 3 com JPA 2.

Ao final do post de hoje você irá encontrar o código fonte para download.

Vamos utilizar um sistema de carros onde teremos a classe principal e outras duas classes herdando da classe pai. Vejamos nosso código inicial:

package com.model;

public abstract class Car {
	private int id;
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}
}
package com.model;

public class Beetle extends Car {
	private int gasCapacity;

	public void talk(){
		System.out.println("Herbie!");
	}

	public int getGasCapacity() {
		return gasCapacity;
	}

	public void setGasCapacity(int gasCapacity) {
		this.gasCapacity = gasCapacity;
	}

}
package com.model;

public class Ferrari extends Car {
	private int model;

	// The newest new models will fly... some day! =P
	public void fly() {

	}

	public int getModel() {
		return model;
	}

	public void setModel(int model) {
		this.model = model;
	}
}

Temos um modelo bem simples onde queremos persistir todas as informações em uma tabela. No exemplo de hoje irei utilizar o Postgres como banco de dados para exibir no final como os dados ficarão gravados no banco de dados.

Coloquemos as anotações JPA para que a herança seja persistida:

package com.model;

import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;

@Entity
@Table(name="CAR")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="FROM_CLASS", discriminatorType=DiscriminatorType.STRING)
public abstract class Car {
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private int id;
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}
}
package com.model;

import javax.persistence.Entity;

@Entity
public class Beetle extends Car {
	private int gasCapacity;

	public void talk(){
		System.out.println("Herbie!");
	}

	public int getGasCapacity() {
		return gasCapacity;
	}

	public void setGasCapacity(int gasCapacity) {
		this.gasCapacity = gasCapacity;
	}
}
package com.model;

import javax.persistence.Entity;

@Entity
public class Ferrari extends Car {
	private int model;

	// The newest new models will fly... some day! =P
	public void fly() {

	}

	public int getModel() {
		return model;
	}

	public void setModel(int model) {
		this.model = model;
	}
}

Algumas explicações sobre o código acima:

  • A anotação “@Inheritance” indica ao JPA qual o tipo de herança iremos utilizar ao persistir as informações.
  • A anotação “@DiscriminatorColumn” é uma coluna onde ficará armazenado um valor descrevendo a qual classe pertence aquela linha.

Com a classe abaixo, vamos inserir dados no banco de dados e ver como ficou.

package com;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import com.model.Beetle;
import com.model.Ferrari;

public class Main {

	public static void main(String[] args) {
		EntityManagerFactory emf = Persistence.createEntityManagerFactory("Database");
		EntityManager em = emf.createEntityManager();

		em.getTransaction().begin();

		try {
			Ferrari ferrari = new Ferrari();
			ferrari.setModel(455);
			ferrari.setName("WWJD");

			Beetle beetle = new Beetle();
			beetle.setGasCapacity(46);
			beetle.setName("Heeerbie");

			em.persist(ferrari);
			em.persist(beetle);
		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			em.getTransaction().commit();
			emf.close();
		}

		System.out.println("It is over");
	}

}


Repare que na tabela apareceu uma coluna chamada “from_class” que indica à qual classe pertence à respectiva linha.

E para finalizar, como faríamos para alterar o valor salvo na no coluna? E se quisemos mudar o nome de como o valor da classe será salvo? Ao invés de Ferrari for CAR_FERRARI, como faríamos?

Basta editar a classe e deixar conforme o código abaixo:

package com.model;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue(value="CAR_BEETLE")
public class Beetle extends Car {
	private int gasCapacity;

	public void talk(){
		System.out.println("Herbie!");
	}

	public int getGasCapacity() {
		return gasCapacity;
	}

	public void setGasCapacity(int gasCapacity) {
		this.gasCapacity = gasCapacity;
	}
}
package com.model;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue(value="CAR_FERRARI")
public class Ferrari extends Car {
	private int model;

	// The newest new models will fly... some day! =P
	public void fly() {

	}

	public int getModel() {
		return model;
	}

	public void setModel(int model) {
		this.model = model;
	}
}

Execute a classe Main novamente e veja o resultado:


Note que as linhas 1 e 2 estão com os valores antigos. As linhas 3 e 4 já constam os valores da nova anotação inserida “@DiscriminatorValue”.

Vamos falar agora dos contras e prós dessa abordagem:

  • Prós: O desempenho é maior e melhor, pois o JPA não irá necessitar fazer diversos Joins para ter as informações de todas as classes. Para quem está começando a trabalhar com esse tipo de persistência é mais fácil de adaptar e coletar informações no banco de dados.
  • Contras: As subclasses não poderão conter campos “não nullos”. Se a classe Beetle tivesse um atributo não nulo ao se salvar um objeto Ferrari o banco de dados levantaria uma Exception.

Clique aqui para fazer o download do código de hoje. Esteja atento ao fato que para o código rodar você precisará ter o Postgres instalado e com um “database” chamado “SingleTable”. E não esqueça também de configurar o usuário e senha. ^o^

Espero que o post de hoje possa te ajudar.

Qualquer dúvida/colocação basta postar.

Até mais! o_